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

summaryrefslogtreecommitdiff
path: root/source/core/StarNetElementBasicFields.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/core/StarNetElementBasicFields.hpp')
-rw-r--r--source/core/StarNetElementBasicFields.hpp331
1 files changed, 331 insertions, 0 deletions
diff --git a/source/core/StarNetElementBasicFields.hpp b/source/core/StarNetElementBasicFields.hpp
new file mode 100644
index 0000000..f0939a7
--- /dev/null
+++ b/source/core/StarNetElementBasicFields.hpp
@@ -0,0 +1,331 @@
+#ifndef STAR_NET_STEP_STATES_HPP
+#define STAR_NET_STEP_STATES_HPP
+
+#include <type_traits>
+
+#include "StarNetElement.hpp"
+#include "StarString.hpp"
+#include "StarByteArray.hpp"
+
+namespace Star {
+
+template <typename T>
+class NetElementBasicField : public NetElement {
+public:
+ virtual ~NetElementBasicField() = default;
+
+ T const& get() const;
+
+ // Updates the value if the value is different than the existing value,
+ // requires T have operator==
+ void set(T const& value);
+
+ // Always updates the value and marks it as updated.
+ void push(T value);
+
+ // Has this field been updated since the last call to pullUpdated?
+ bool pullUpdated();
+
+ // Update the value in place. The mutator will be called as bool
+ // mutator(T&), return true to signal that the value was updated.
+ template <typename Mutator>
+ void update(Mutator&& mutator);
+
+ 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;
+
+protected:
+ virtual void readData(DataStream& ds, T& t) const = 0;
+ virtual void writeData(DataStream& ds, T const& t) const = 0;
+
+ virtual void updated();
+
+private:
+ NetElementVersion const* m_netVersion = nullptr;
+ uint64_t m_latestUpdateVersion = 0;
+ T m_value = T();
+ bool m_updated = false;
+ Maybe<Deque<pair<float, T>>> m_pendingInterpolatedValues;
+};
+
+template <typename T>
+class NetElementIntegral : public NetElementBasicField<T> {
+protected:
+ void readData(DataStream& ds, T& v) const override;
+ void writeData(DataStream& ds, T const& v) const override;
+};
+
+typedef NetElementIntegral<int64_t> NetElementInt;
+typedef NetElementIntegral<uint64_t> NetElementUInt;
+
+// Properly encodes NPos no matter the platform width of size_t NetElement
+// size_t values are NOT clamped when setting.
+class NetElementSize : public NetElementBasicField<size_t> {
+protected:
+ void readData(DataStream& ds, size_t& v) const override;
+ void writeData(DataStream& ds, size_t const& v) const override;
+};
+
+class NetElementBool : public NetElementBasicField<bool> {
+protected:
+ void readData(DataStream& ds, bool& v) const override;
+ void writeData(DataStream& ds, bool const& v) const override;
+};
+
+template <typename Enum>
+class NetElementEnum : public NetElementBasicField<Enum> {
+protected:
+ void readData(DataStream& ds, Enum& v) const override;
+ void writeData(DataStream& ds, Enum const& v) const override;
+};
+
+// Wraps a uint64_t to give a simple event stream. Every trigger is an
+// increment to a held uint64_t value, and slaves can see how many triggers
+// have occurred since the last check.
+class NetElementEvent : public NetElementUInt {
+public:
+ void trigger();
+
+ // Returns the number of times this event has been triggered since the last
+ // pullOccurrences call.
+ uint64_t pullOccurrences();
+
+ // Pulls whether this event occurred at all, ignoring the number
+ bool pullOccurred();
+
+ // Ignore all the existing ocurrences
+ void ignoreOccurrences();
+ void setIgnoreOccurrencesOnNetLoad(bool ignoreOccurrencesOnNetLoad);
+
+ void netLoad(DataStream& ds) override;
+
+protected:
+ void updated() override;
+
+private:
+ using NetElementUInt::get;
+ using NetElementUInt::set;
+ using NetElementUInt::push;
+ using NetElementUInt::update;
+
+ uint64_t m_pulledOccurrences = 0;
+ bool m_ignoreOccurrencesOnNetLoad = false;
+};
+
+// Holds an arbitrary serializable value
+template <typename T>
+class NetElementData : public NetElementBasicField<T> {
+public:
+ NetElementData();
+ NetElementData(function<void(DataStream&, T&)> reader, function<void(DataStream&, T const&)> writer);
+
+protected:
+ void readData(DataStream& ds, T& v) const override;
+ void writeData(DataStream& ds, T const& v) const override;
+
+private:
+ function<void(DataStream&, T&)> m_reader;
+ function<void(DataStream&, T const&)> m_writer;
+};
+
+typedef NetElementData<String> NetElementString;
+typedef NetElementData<ByteArray> NetElementBytes;
+
+template <typename T>
+T const& NetElementBasicField<T>::get() const {
+ return m_value;
+}
+
+template <typename T>
+void NetElementBasicField<T>::set(T const& value) {
+ if (!(m_value == value))
+ push(value);
+}
+
+template <typename T>
+void NetElementBasicField<T>::push(T value) {
+ m_value = move(value);
+ updated();
+ m_latestUpdateVersion = m_netVersion ? m_netVersion->current() : 0;
+ if (m_pendingInterpolatedValues)
+ m_pendingInterpolatedValues->clear();
+}
+
+template <typename T>
+bool NetElementBasicField<T>::pullUpdated() {
+ return take(m_updated);
+}
+
+template <typename T>
+template <typename Mutator>
+void NetElementBasicField<T>::update(Mutator&& mutator) {
+ if (mutator(m_value)) {
+ updated();
+ m_latestUpdateVersion = m_netVersion ? m_netVersion->current() : 0;
+ if (m_pendingInterpolatedValues)
+ m_pendingInterpolatedValues->clear();
+ }
+}
+
+template <typename T>
+void NetElementBasicField<T>::initNetVersion(NetElementVersion const* version) {
+ m_netVersion = version;
+ m_latestUpdateVersion = 0;
+}
+
+template <typename T>
+void NetElementBasicField<T>::enableNetInterpolation(float) {
+ if (!m_pendingInterpolatedValues)
+ m_pendingInterpolatedValues.emplace();
+}
+
+template <typename T>
+void NetElementBasicField<T>::disableNetInterpolation() {
+ if (m_pendingInterpolatedValues) {
+ if (!m_pendingInterpolatedValues->empty())
+ m_value = m_pendingInterpolatedValues->takeLast().second;
+ m_pendingInterpolatedValues.reset();
+ }
+}
+
+template <typename T>
+void NetElementBasicField<T>::tickNetInterpolation(float dt) {
+ if (m_pendingInterpolatedValues) {
+ for (auto& p : *m_pendingInterpolatedValues)
+ p.first -= dt;
+ while (!m_pendingInterpolatedValues->empty() && m_pendingInterpolatedValues->first().first <= 0.0f) {
+ m_value = m_pendingInterpolatedValues->takeFirst().second;
+ updated();
+ }
+ }
+}
+
+template <typename T>
+void NetElementBasicField<T>::netStore(DataStream& ds) const {
+ if (m_pendingInterpolatedValues && !m_pendingInterpolatedValues->empty())
+ writeData(ds, m_pendingInterpolatedValues->last().second);
+ else
+ writeData(ds, m_value);
+}
+
+template <typename T>
+void NetElementBasicField<T>::netLoad(DataStream& ds) {
+ readData(ds, m_value);
+ m_latestUpdateVersion = m_netVersion ? m_netVersion->current() : 0;
+ updated();
+ if (m_pendingInterpolatedValues)
+ m_pendingInterpolatedValues->clear();
+}
+
+template <typename T>
+bool NetElementBasicField<T>::writeNetDelta(DataStream& ds, uint64_t fromVersion) const {
+ if (m_latestUpdateVersion < fromVersion)
+ return false;
+
+ if (m_pendingInterpolatedValues && !m_pendingInterpolatedValues->empty())
+ writeData(ds, m_pendingInterpolatedValues->last().second);
+ else
+ writeData(ds, m_value);
+ return true;
+}
+
+template <typename T>
+void NetElementBasicField<T>::readNetDelta(DataStream& ds, float interpolationTime) {
+ T t;
+ readData(ds, t);
+ m_latestUpdateVersion = m_netVersion ? m_netVersion->current() : 0;
+ if (m_pendingInterpolatedValues) {
+ // Only append an incoming delta to our pending value list if the incoming
+ // step is forward in time of every other pending value. In any other
+ // case, this is an error or the step tracking is wildly off, so just clear
+ // any other incoming values.
+ if (interpolationTime > 0.0f && (m_pendingInterpolatedValues->empty() || interpolationTime >= m_pendingInterpolatedValues->last().first)) {
+ m_pendingInterpolatedValues->append({interpolationTime, move(t)});
+ } else {
+ m_value = move(t);
+ m_pendingInterpolatedValues->clear();
+ updated();
+ }
+ } else {
+ m_value = move(t);
+ updated();
+ }
+}
+
+template <typename T>
+void NetElementBasicField<T>::updated() {
+ m_updated = true;
+}
+
+template <typename T>
+void NetElementIntegral<T>::readData(DataStream& ds, T& v) const {
+ if (sizeof(T) == 1) {
+ ds.read(v);
+ } else {
+ if (std::is_unsigned<T>::value)
+ v = ds.readVlqU();
+ else
+ v = ds.readVlqI();
+ }
+}
+
+template <typename T>
+void NetElementIntegral<T>::writeData(DataStream& ds, T const& v) const {
+ if (sizeof(T) == 1) {
+ ds.write(v);
+ } else {
+ if (std::is_unsigned<T>::value)
+ ds.writeVlqU(v);
+ else
+ ds.writeVlqI(v);
+ }
+}
+
+template <typename Enum>
+void NetElementEnum<Enum>::readData(DataStream& ds, Enum& v) const {
+ if (sizeof(Enum) == 1)
+ ds.read(v);
+ else
+ v = (Enum)ds.readVlqI();
+}
+
+template <typename Enum>
+void NetElementEnum<Enum>::writeData(DataStream& ds, Enum const& v) const {
+ if (sizeof(Enum) == 1)
+ ds.write(v);
+ else
+ ds.writeVlqI((int64_t)v);
+}
+
+template <typename T>
+NetElementData<T>::NetElementData()
+ : NetElementData([](DataStream& ds, T & t) { ds >> t; }, [](DataStream& ds, T const& t) { ds << t; }) {}
+
+template <typename T>
+NetElementData<T>::NetElementData(function<void(DataStream&, T&)> reader, function<void(DataStream&, T const&)> writer)
+ : m_reader(move(reader)), m_writer(move(writer)) {}
+
+template <typename T>
+void NetElementData<T>::readData(DataStream& ds, T& v) const {
+ m_reader(ds, v);
+}
+
+template <typename T>
+void NetElementData<T>::writeData(DataStream& ds, T const& v) const {
+ m_writer(ds, v);
+}
+
+}
+
+#endif