318 lines
9.3 KiB
C++
318 lines
9.3 KiB
C++
|
#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
|