431a9c00a5
On Linux and macOS, using Clang to compile OpenStarbound produces about 400 MB worth of warnings during the build, making the compiler output unreadable and slowing the build down considerably. 99% of the warnings were unqualified uses of std::move and std::forward, which are now all properly qualified. Fixed a few other minor warnings about non-virtual destructors and some uses of std::move preventing copy elision on temporary objects. Most remaining warnings are now unused parameters.
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(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();
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|