#ifndef STAR_NET_ELEMENT_DYNAMIC_GROUP_HPP #define STAR_NET_ELEMENT_DYNAMIC_GROUP_HPP #include "StarNetElement.hpp" #include "StarIdMap.hpp" #include "StarStrongTypedef.hpp" #include "StarDataStreamExtra.hpp" namespace Star { // A dynamic group of NetElements that manages creation and destruction of // individual elements, that is itself a NetElement. Element changes are not // delayed by the interpolation delay, they will always happen immediately, but // this does not inhibit the Elements themselves from handling their own delta // update delays normally. template class NetElementDynamicGroup : public NetElement { public: typedef shared_ptr ElementPtr; typedef uint32_t ElementId; static ElementId const NullElementId = 0; NetElementDynamicGroup() = default; NetElementDynamicGroup(NetElementDynamicGroup const&) = delete; NetElementDynamicGroup& operator=(NetElementDynamicGroup const&) = delete; // Must not call addNetElement / removeNetElement when being used as a slave, // id errors will result. ElementId addNetElement(ElementPtr element); void removeNetElement(ElementId id); // Remove all elements void clearNetElements(); List netElementIds() const; ElementPtr getNetElement(ElementId id) const; List netElements() const; void initNetVersion(NetElementVersion const* version = nullptr) override; // Values are never interpolated, but they will be delayed for the given // interpolationTime. void enableNetInterpolation(float extrapolationHint = 0.0f) override; void disableNetInterpolation() override; void tickNetInterpolation(float dt) override; void netStore(DataStream& ds) const override; void netLoad(DataStream& ds) override; bool writeNetDelta(DataStream& ds, uint64_t fromVersion) const override; void readNetDelta(DataStream& ds, float interpolationTime = 0.0f) override; void blankNetDelta(float interpolationTime = 0.0f) override; private: // If a delta is written from further back than this many versions, the delta // will fall back to a full serialization of the entire state. static int64_t const MaxChangeDataVersions = 100; typedef ElementId ElementRemovalType; typedef pair ElementAdditionType; strong_typedef(Empty, ElementReset); strong_typedef_builtin(ElementRemovalType, ElementRemoval); strong_typedef(ElementAdditionType, ElementAddition); typedef Variant ElementChange; typedef IdMap ElementMap; void addChangeData(ElementChange change); void readyElement(ElementPtr const& element); NetElementVersion const* m_netVersion = nullptr; bool m_interpolationEnabled = false; float m_extrapolationHint = 0.0f; ElementMap m_idMap = ElementMap(1, highest()); Deque> m_changeData; uint64_t m_changeDataLastVersion = 0; mutable DataStreamBuffer m_buffer; mutable HashSet m_receivedDeltaIds; }; template auto NetElementDynamicGroup::addNetElement(ElementPtr element) -> ElementId { readyElement(element); DataStreamBuffer storeBuffer; element->netStore(storeBuffer); auto id = m_idMap.add(move(element)); addChangeData(ElementAddition(id, storeBuffer.takeData())); return id; } template void NetElementDynamicGroup::removeNetElement(ElementId id) { m_idMap.remove(id); addChangeData(ElementRemoval{id}); } template void NetElementDynamicGroup::clearNetElements() { for (auto const& id : netElementIds()) removeNetElement(id); } template auto NetElementDynamicGroup::netElementIds() const -> List { return m_idMap.keys(); } template auto NetElementDynamicGroup::getNetElement(ElementId id) const -> ElementPtr { return m_idMap.get(id); } template auto NetElementDynamicGroup::netElements() const -> List { return m_idMap.values(); } template void NetElementDynamicGroup::initNetVersion(NetElementVersion const* version) { m_netVersion = version; m_changeData.clear(); m_changeDataLastVersion = 0; addChangeData(ElementReset()); for (auto& pair : m_idMap) { pair.second->initNetVersion(m_netVersion); DataStreamBuffer storeBuffer; pair.second->netStore(storeBuffer); addChangeData(ElementAddition(pair.first, storeBuffer.takeData())); } } template void NetElementDynamicGroup::enableNetInterpolation(float extrapolationHint) { m_interpolationEnabled = true; m_extrapolationHint = extrapolationHint; for (auto& p : m_idMap) p.second->enableNetInterpolation(extrapolationHint); } template void NetElementDynamicGroup::disableNetInterpolation() { m_interpolationEnabled = false; m_extrapolationHint = 0.0f; for (auto& p : m_idMap) p.second->disableNetInterpolation(); } template void NetElementDynamicGroup::tickNetInterpolation(float dt) { for (auto& p : m_idMap) p.second->tickNetInterpolation(dt); } template void NetElementDynamicGroup::netStore(DataStream& ds) const { ds.writeVlqU(m_idMap.size()); for (auto& pair : m_idMap) { ds.writeVlqU(pair.first); pair.second->netStore(m_buffer); ds.write(m_buffer.data()); m_buffer.clear(); } } template void NetElementDynamicGroup::netLoad(DataStream& ds) { m_changeData.clear(); m_changeDataLastVersion = m_netVersion ? m_netVersion->current() : 0; m_idMap.clear(); addChangeData(ElementReset()); uint64_t count = ds.readVlqU(); for (uint64_t i = 0; i < count; ++i) { ElementId id = ds.readVlqU(); DataStreamBuffer storeBuffer(ds.read()); ElementPtr element = make_shared(); element->netLoad(storeBuffer); readyElement(element); m_idMap.add(id, move(element)); addChangeData(ElementAddition(id, storeBuffer.takeData())); } } template bool NetElementDynamicGroup::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { if (fromVersion < m_changeDataLastVersion) { ds.write(true); netStore(ds); return true; } else { bool deltaWritten = false; auto willWrite = [&]() { if (!deltaWritten) { deltaWritten = true; ds.write(false); } }; for (auto const& p : m_changeData) { if (p.first >= fromVersion) { willWrite(); ds.writeVlqU(1); ds.write(p.second); } } for (auto& p : m_idMap) { if (p.second->writeNetDelta(m_buffer, fromVersion)) { willWrite(); ds.writeVlqU(p.first + 1); ds.writeBytes(m_buffer.data()); m_buffer.clear(); } } if (deltaWritten) ds.writeVlqU(0); return deltaWritten; } } template void NetElementDynamicGroup::readNetDelta(DataStream& ds, float interpolationTime) { bool isFull = ds.read(); if (isFull) { netLoad(ds); } else { while (true) { uint64_t code = ds.readVlqU(); if (code == 0) { break; } if (code == 1) { auto changeUpdate = ds.read(); addChangeData(changeUpdate); if (changeUpdate.template is()) { m_idMap.clear(); } else if (auto addition = changeUpdate.template ptr()) { ElementPtr element = make_shared(); DataStreamBuffer storeBuffer(move(get<1>(*addition))); element->netLoad(storeBuffer); readyElement(element); m_idMap.add(get<0>(*addition), move(element)); } else if (auto removal = changeUpdate.template ptr()) { m_idMap.remove(*removal); } } else { ElementId elementId = code - 1; auto const& element = m_idMap.get(elementId); element->readNetDelta(ds, interpolationTime); if (m_interpolationEnabled) m_receivedDeltaIds.add(elementId); } } if (m_interpolationEnabled) { for (auto& p : m_idMap) { if (!m_receivedDeltaIds.contains(p.first)) p.second->blankNetDelta(interpolationTime); } m_receivedDeltaIds.clear(); } } } template void NetElementDynamicGroup::blankNetDelta(float interpolationTime) { if (m_interpolationEnabled) { for (auto& p : m_idMap) p.second->blankNetDelta(interpolationTime); } } template void NetElementDynamicGroup::addChangeData(ElementChange change) { uint64_t currentVersion = m_netVersion ? m_netVersion->current() : 0; starAssert(m_changeData.empty() || m_changeData.last().first <= currentVersion); m_changeData.append({currentVersion, move(change)}); m_changeDataLastVersion = max((int64_t)currentVersion - MaxChangeDataVersions, 0); while (!m_changeData.empty() && m_changeData.first().first < m_changeDataLastVersion) m_changeData.removeFirst(); } template void NetElementDynamicGroup::readyElement(ElementPtr const& element) { element->initNetVersion(m_netVersion); if (m_interpolationEnabled) element->enableNetInterpolation(m_extrapolationHint); else element->disableNetInterpolation(); } } #endif