osb/source/core/StarNetElementDynamicGroup.hpp
2024-02-25 15:46:47 +01:00

315 lines
9.3 KiB
C++

#pragma once
#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(std::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, std::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(std::move(get<1>(*addition)));
element->netLoad(storeBuffer);
readyElement(element);
m_idMap.add(get<0>(*addition), std::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, std::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();
}
}