diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarNetElementDynamicGroup.hpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/core/StarNetElementDynamicGroup.hpp')
-rw-r--r-- | source/core/StarNetElementDynamicGroup.hpp | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/source/core/StarNetElementDynamicGroup.hpp b/source/core/StarNetElementDynamicGroup.hpp new file mode 100644 index 0000000..5d34c8d --- /dev/null +++ b/source/core/StarNetElementDynamicGroup.hpp @@ -0,0 +1,317 @@ +#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 <typename Element> +class NetElementDynamicGroup : public NetElement { +public: + typedef shared_ptr<Element> 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<ElementId> netElementIds() const; + ElementPtr getNetElement(ElementId id) const; + + List<ElementPtr> 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<ElementId, ByteArray> ElementAdditionType; + + strong_typedef(Empty, ElementReset); + strong_typedef_builtin(ElementRemovalType, ElementRemoval); + strong_typedef(ElementAdditionType, ElementAddition); + + typedef Variant<ElementReset, ElementRemoval, ElementAddition> ElementChange; + + typedef IdMap<ElementId, ElementPtr> 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<ElementId>()); + + Deque<pair<uint64_t, ElementChange>> m_changeData; + uint64_t m_changeDataLastVersion = 0; + + mutable DataStreamBuffer m_buffer; + mutable HashSet<ElementId> m_receivedDeltaIds; +}; + +template <typename Element> +auto NetElementDynamicGroup<Element>::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 <typename Element> +void NetElementDynamicGroup<Element>::removeNetElement(ElementId id) { + m_idMap.remove(id); + addChangeData(ElementRemoval{id}); +} + +template <typename Element> +void NetElementDynamicGroup<Element>::clearNetElements() { + for (auto const& id : netElementIds()) + removeNetElement(id); +} + +template <typename Element> +auto NetElementDynamicGroup<Element>::netElementIds() const -> List<ElementId> { + return m_idMap.keys(); +} + +template <typename Element> +auto NetElementDynamicGroup<Element>::getNetElement(ElementId id) const -> ElementPtr { + return m_idMap.get(id); +} + +template <typename Element> +auto NetElementDynamicGroup<Element>::netElements() const -> List<ElementPtr> { + return m_idMap.values(); +} + +template <typename Element> +void NetElementDynamicGroup<Element>::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 <typename Element> +void NetElementDynamicGroup<Element>::enableNetInterpolation(float extrapolationHint) { + m_interpolationEnabled = true; + m_extrapolationHint = extrapolationHint; + for (auto& p : m_idMap) + p.second->enableNetInterpolation(extrapolationHint); +} + +template <typename Element> +void NetElementDynamicGroup<Element>::disableNetInterpolation() { + m_interpolationEnabled = false; + m_extrapolationHint = 0.0f; + for (auto& p : m_idMap) + p.second->disableNetInterpolation(); +} + +template <typename Element> +void NetElementDynamicGroup<Element>::tickNetInterpolation(float dt) { + for (auto& p : m_idMap) + p.second->tickNetInterpolation(dt); +} + +template <typename Element> +void NetElementDynamicGroup<Element>::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 <typename Element> +void NetElementDynamicGroup<Element>::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<ByteArray>()); + + ElementPtr element = make_shared<Element>(); + element->netLoad(storeBuffer); + readyElement(element); + + m_idMap.add(id, move(element)); + addChangeData(ElementAddition(id, storeBuffer.takeData())); + } +} + +template <typename Element> +bool NetElementDynamicGroup<Element>::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { + if (fromVersion < m_changeDataLastVersion) { + ds.write<bool>(true); + netStore(ds); + return true; + + } else { + bool deltaWritten = false; + auto willWrite = [&]() { + if (!deltaWritten) { + deltaWritten = true; + ds.write<bool>(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 <typename Element> +void NetElementDynamicGroup<Element>::readNetDelta(DataStream& ds, float interpolationTime) { + bool isFull = ds.read<bool>(); + if (isFull) { + netLoad(ds); + } else { + while (true) { + uint64_t code = ds.readVlqU(); + if (code == 0) { + break; + } + if (code == 1) { + auto changeUpdate = ds.read<ElementChange>(); + addChangeData(changeUpdate); + + if (changeUpdate.template is<ElementReset>()) { + m_idMap.clear(); + } else if (auto addition = changeUpdate.template ptr<ElementAddition>()) { + ElementPtr element = make_shared<Element>(); + 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<ElementRemoval>()) { + 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 <typename Element> +void NetElementDynamicGroup<Element>::blankNetDelta(float interpolationTime) { + if (m_interpolationEnabled) { + for (auto& p : m_idMap) + p.second->blankNetDelta(interpolationTime); + } +} + +template <typename Element> +void NetElementDynamicGroup<Element>::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>((int64_t)currentVersion - MaxChangeDataVersions, 0); + while (!m_changeData.empty() && m_changeData.first().first < m_changeDataLastVersion) + m_changeData.removeFirst(); +} + +template <typename Element> +void NetElementDynamicGroup<Element>::readyElement(ElementPtr const& element) { + element->initNetVersion(m_netVersion); + if (m_interpolationEnabled) + element->enableNetInterpolation(m_extrapolationHint); + else + element->disableNetInterpolation(); +} + +} + +#endif |