osb/source/core/StarNetElementContainers.hpp

453 lines
13 KiB
C++
Raw Normal View History

2023-06-20 04:33:09 +00:00
#ifndef STAR_NET_ELEMENT_CONTAINERS_HPP
#define STAR_NET_ELEMENT_CONTAINERS_HPP
#include "StarMap.hpp"
#include "StarDataStreamExtra.hpp"
#include "StarNetElement.hpp"
#include "StarStrongTypedef.hpp"
namespace Star {
// NetElement map container that is more efficient than the naive serialization
// of an entire Map, because it delta encodes changes to save networking
// traffic.
template <typename BaseMap>
class NetElementMapWrapper : public NetElement, private BaseMap {
public:
typedef typename BaseMap::iterator iterator;
typedef typename BaseMap::const_iterator const_iterator;
typedef typename BaseMap::key_type key_type;
typedef typename BaseMap::mapped_type mapped_type;
typedef typename BaseMap::value_type value_type;
void initNetVersion(NetElementVersion const* version = nullptr) override;
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;
mapped_type const& get(key_type const& key) const;
mapped_type const* ptr(key_type const& key) const;
const_iterator begin() const;
const_iterator end() const;
using BaseMap::keys;
using BaseMap::values;
using BaseMap::pairs;
using BaseMap::contains;
using BaseMap::size;
using BaseMap::empty;
using BaseMap::maybe;
using BaseMap::value;
pair<const_iterator, bool> insert(value_type v);
pair<const_iterator, bool> insert(key_type k, mapped_type v);
void add(key_type k, mapped_type v);
// Calling set with a matching key and value does not cause a delta to be
// produced
void set(key_type k, mapped_type v);
// set requires that mapped_type implement operator==, push always generates
// a delta and does not require mapped_type operator==
void push(key_type k, mapped_type v);
bool remove(key_type const& k);
const_iterator erase(const_iterator i);
mapped_type take(key_type const& k);
Maybe<mapped_type> maybeTake(key_type const& k);
void clear();
BaseMap const& baseMap() const;
void reset(BaseMap values);
bool pullUpdated();
// Sets this map to contain the same keys / values as the given map. All
// values in this map not found in the given map are removed. (Same as
// reset, but with arbitrary map type).
template <typename MapType>
void setContents(MapType const& values);
private:
// If a delta is written from further back than this many steps, the delta
// will fall back to a full serialization of the entire state.
static int64_t const MaxChangeDataVersions = 100;
struct SetChange {
key_type key;
mapped_type value;
};
struct RemoveChange {
key_type key;
};
struct ClearChange {};
typedef Variant<SetChange, RemoveChange, ClearChange> ElementChange;
static void writeChange(DataStream& ds, ElementChange const& change);
static ElementChange readChange(DataStream& ds);
void addChangeData(ElementChange change);
void addPendingChangeData(ElementChange change, float interpolationTime);
void applyChange(ElementChange change);
Deque<pair<uint64_t, ElementChange>> m_changeData;
Deque<pair<float, ElementChange>> m_pendingChangeData;
NetElementVersion const* m_netVersion = nullptr;
uint64_t m_changeDataLastVersion = 0;
bool m_updated = false;
bool m_interpolationEnabled = false;
};
template <typename Key, typename Value>
using NetElementMap = NetElementMapWrapper<Map<Key, Value>>;
template <typename Key, typename Value>
using NetElementHashMap = NetElementMapWrapper<HashMap<Key, Value>>;
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::initNetVersion(NetElementVersion const* version) {
m_netVersion = version;
m_changeData.clear();
m_changeDataLastVersion = 0;
for (auto& change : Star::take(m_pendingChangeData))
applyChange(std::move(change.second));
2023-06-20 04:33:09 +00:00
addChangeData(ClearChange());
for (auto const& p : *this)
addChangeData(SetChange{p.first, p.second});
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::enableNetInterpolation(float) {
m_interpolationEnabled = true;
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::disableNetInterpolation() {
m_interpolationEnabled = false;
for (auto& change : Star::take(m_pendingChangeData))
applyChange(std::move(change.second));
2023-06-20 04:33:09 +00:00
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::tickNetInterpolation(float dt) {
for (auto& p : m_pendingChangeData)
p.first -= dt;
while (!m_pendingChangeData.empty() && m_pendingChangeData.first().first <= 0.0f)
applyChange(m_pendingChangeData.takeFirst().second);
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::netStore(DataStream& ds) const {
ds.writeVlqU(BaseMap::size() + m_pendingChangeData.size());
for (auto const& pair : *this)
writeChange(ds, SetChange{pair.first, pair.second});
for (auto const& p : m_pendingChangeData)
writeChange(ds, p.second);
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::netLoad(DataStream& ds) {
m_changeData.clear();
m_changeDataLastVersion = m_netVersion ? m_netVersion->current() : 0;
m_pendingChangeData.clear();
BaseMap::clear();
addChangeData(ClearChange());
uint64_t count = ds.readVlqU();
for (uint64_t i = 0; i < count; ++i) {
auto change = readChange(ds);
addChangeData(change);
applyChange(std::move(change));
2023-06-20 04:33:09 +00:00
}
m_updated = true;
}
template <typename BaseMap>
bool NetElementMapWrapper<BaseMap>::writeNetDelta(DataStream& ds, uint64_t fromVersion) const {
bool deltaWritten = false;
if (fromVersion < m_changeDataLastVersion) {
deltaWritten = true;
ds.writeVlqU(1);
netStore(ds);
} else {
for (auto const& p : m_changeData) {
if (p.first >= fromVersion) {
deltaWritten = true;
ds.writeVlqU(2);
writeChange(ds, p.second);
}
}
}
if (deltaWritten)
ds.writeVlqU(0);
return deltaWritten;
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::readNetDelta(DataStream& ds, float interpolationTime) {
while (true) {
uint64_t code = ds.readVlqU();
if (code == 0) {
break;
} else if (code == 1) {
netLoad(ds);
} else if (code == 2) {
auto change = readChange(ds);
addChangeData(change);
if (m_interpolationEnabled && interpolationTime > 0.0f)
addPendingChangeData(std::move(change), interpolationTime);
2023-06-20 04:33:09 +00:00
else
applyChange(std::move(change));
2023-06-20 04:33:09 +00:00
} else {
throw IOException("Improper delta code received in NetElementMapWrapper::readNetDelta");
}
}
}
template <typename BaseMap>
auto NetElementMapWrapper<BaseMap>::get(key_type const& key) const -> mapped_type const & {
return BaseMap::get(key);
}
template <typename BaseMap>
auto NetElementMapWrapper<BaseMap>::ptr(key_type const& key) const -> mapped_type const * {
return BaseMap::ptr(key);
}
template <typename BaseMap>
auto NetElementMapWrapper<BaseMap>::begin() const -> const_iterator {
return BaseMap::begin();
}
template <typename BaseMap>
auto NetElementMapWrapper<BaseMap>::end() const -> const_iterator {
return BaseMap::end();
}
template <typename BaseMap>
auto NetElementMapWrapper<BaseMap>::insert(value_type v) -> pair<const_iterator, bool> {
auto res = BaseMap::insert(v);
if (res.second) {
addChangeData(SetChange{std::move(v.first), std::move(v.second)});
2023-06-20 04:33:09 +00:00
m_updated = true;
}
return res;
}
template <typename BaseMap>
auto NetElementMapWrapper<BaseMap>::insert(key_type k, mapped_type v) -> pair<const_iterator, bool> {
return insert(value_type(std::move(k), std::move(v)));
2023-06-20 04:33:09 +00:00
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::add(key_type k, mapped_type v) {
if (!insert(value_type(std::move(k), std::move(v))).second)
2023-06-27 10:23:44 +00:00
throw MapException::format("Entry with key '{}' already present.", outputAny(k));
2023-06-20 04:33:09 +00:00
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::set(key_type k, mapped_type v) {
auto i = BaseMap::find(k);
if (i != BaseMap::end()) {
if (!(i->second == v)) {
addChangeData(SetChange{std::move(k), v});
i->second = std::move(v);
2023-06-20 04:33:09 +00:00
m_updated = true;
}
} else {
addChangeData(SetChange{k, v});
BaseMap::insert(value_type(std::move(k), std::move(v)));
2023-06-20 04:33:09 +00:00
m_updated = true;
}
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::push(key_type k, mapped_type v) {
auto i = BaseMap::find(k);
if (i != BaseMap::end()) {
addChangeData(SetChange(std::move(k), v));
i->second = std::move(v);
2023-06-20 04:33:09 +00:00
} else {
addChangeData(SetChange(k, v));
BaseMap::insert(value_type(std::move(k), std::move(v)));
2023-06-20 04:33:09 +00:00
}
m_updated = true;
}
template <typename BaseMap>
bool NetElementMapWrapper<BaseMap>::remove(key_type const& k) {
auto i = BaseMap::find(k);
if (i != BaseMap::end()) {
BaseMap::erase(i);
addChangeData(RemoveChange{k});
m_updated = true;
return true;
}
return false;
}
template <typename BaseMap>
auto NetElementMapWrapper<BaseMap>::erase(const_iterator i) -> const_iterator {
addChangeData(RemoveChange(i->first));
m_updated = true;
return BaseMap::erase(i);
}
template <typename BaseMap>
auto NetElementMapWrapper<BaseMap>::take(key_type const& k) -> mapped_type {
auto i = BaseMap::find(k);
if (i == BaseMap::end())
2023-06-27 10:23:44 +00:00
throw MapException::format("Key '{}' not found in Map::take()", outputAny(k));
auto m = std::move(i->second);
2023-06-20 04:33:09 +00:00
erase(i);
return m;
}
template <typename BaseMap>
auto NetElementMapWrapper<BaseMap>::maybeTake(key_type const& k) -> Maybe<mapped_type> {
auto i = BaseMap::find(k);
if (i == BaseMap::end())
return {};
auto m = std::move(i->second);
2023-06-20 04:33:09 +00:00
erase(i);
return Maybe<mapped_type>(std::move(m));
2023-06-20 04:33:09 +00:00
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::clear() {
if (!empty()) {
addChangeData(ClearChange());
m_updated = true;
BaseMap::clear();
}
}
template <typename BaseMap>
BaseMap const& NetElementMapWrapper<BaseMap>::baseMap() const {
return *this;
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::reset(BaseMap values) {
for (auto const& p : *this) {
if (!values.contains(p.first)) {
addChangeData(RemoveChange{p.first});
m_updated = true;
}
}
for (auto const& p : values) {
auto v = ptr(p.first);
if (!v || !(*v == p.second)) {
addChangeData(SetChange{p.first, p.second});
m_updated = true;
}
}
BaseMap::operator=(std::move(values));
2023-06-20 04:33:09 +00:00
}
template <typename BaseMap>
bool NetElementMapWrapper<BaseMap>::pullUpdated() {
return Star::take(m_updated);
}
template <typename BaseMap>
template <typename MapType>
void NetElementMapWrapper<BaseMap>::setContents(MapType const& values) {
reset(BaseMap::from(values));
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::writeChange(DataStream& ds, ElementChange const& change) {
if (auto sc = change.template ptr<SetChange>()) {
ds.write<uint8_t>(0);
ds.write(sc->key);
ds.write(sc->value);
} else if (auto rc = change.template ptr<RemoveChange>()) {
ds.write<uint8_t>(1);
ds.write(rc->key);
} else {
ds.write<uint8_t>(2);
}
}
template <typename BaseMap>
auto NetElementMapWrapper<BaseMap>::readChange(DataStream& ds) -> ElementChange {
uint8_t t = ds.read<uint8_t>();
if (t == 0) {
SetChange sc;
ds.read(sc.key);
ds.read(sc.value);
return sc;
} else if (t == 1) {
RemoveChange rc;
ds.read(rc.key);
return rc;
} else if (t == 2) {
return ClearChange();
} else {
throw IOException("Improper type code received in NetElementMapWrapper::readChange");
}
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::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)});
2023-06-20 04:33:09 +00:00
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 BaseMap>
void NetElementMapWrapper<BaseMap>::addPendingChangeData(ElementChange change, float interpolationTime) {
if (!m_pendingChangeData.empty() && interpolationTime < m_pendingChangeData.last().first) {
for (auto& change : Star::take(m_pendingChangeData))
applyChange(std::move(change.second));
2023-06-20 04:33:09 +00:00
}
m_pendingChangeData.append({interpolationTime, std::move(change)});
2023-06-20 04:33:09 +00:00
}
template <typename BaseMap>
void NetElementMapWrapper<BaseMap>::applyChange(ElementChange change) {
if (auto set = change.template ptr<SetChange>())
BaseMap::set(std::move(set->key), std::move(set->value));
2023-06-20 04:33:09 +00:00
else if (auto remove = change.template ptr<RemoveChange>())
BaseMap::remove(std::move(remove->key));
2023-06-20 04:33:09 +00:00
else
BaseMap::clear();
m_updated = true;
}
}
#endif