Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarNetElementDynamicGroup.hpp
diff options
context:
space:
mode:
authorKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
committerKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
commit6352e8e3196f78388b6c771073f9e03eaa612673 (patch)
treee23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarNetElementDynamicGroup.hpp
parent6741a057e5639280d85d0f88ba26f000baa58f61 (diff)
everything everywhere
all at once
Diffstat (limited to 'source/core/StarNetElementDynamicGroup.hpp')
-rw-r--r--source/core/StarNetElementDynamicGroup.hpp317
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