#ifndef STAR_NET_ELEMENT_SIGNAL_HPP #define STAR_NET_ELEMENT_SIGNAL_HPP #include "StarNetElement.hpp" namespace Star { // NetElement that sends signals during delta writes that can be received by // slaves. It has no 'state', and nothing is sent during a store / load, and // it only keeps past signals for a maximum number of versions. Thus, it is // not appropriate to use to send updates to long term states, only for event // like things that are not harmful if missed. template class NetElementSignal : public NetElement { public: NetElementSignal(size_t maxSignalQueue = 32); void initNetVersion(NetElementVersion const* version = nullptr) override; void netStore(DataStream& ds) const override; void netLoad(DataStream& ds) override; void enableNetInterpolation(float extrapolationHint = 0.0f) override; void disableNetInterpolation() override; void tickNetInterpolation(float dt) override; bool writeNetDelta(DataStream& ds, uint64_t fromVersion) const override; void readNetDelta(DataStream& ds, float interpolationTime = 0.0) override; void send(Signal signal); List receive(); private: struct SignalEntry { uint64_t version; Signal signal; bool received; }; size_t m_maxSignalQueue; NetElementVersion const* m_netVersion = nullptr; bool m_netInterpolationEnabled = false; Deque m_signals; Deque> m_pendingSignals; }; template NetElementSignal::NetElementSignal(size_t maxSignalQueue) { m_maxSignalQueue = maxSignalQueue; } template void NetElementSignal::initNetVersion(NetElementVersion const* version) { m_netVersion = version; m_signals.clear(); } template void NetElementSignal::netStore(DataStream&) const {} template void NetElementSignal::netLoad(DataStream&) { } template void NetElementSignal::enableNetInterpolation(float) { m_netInterpolationEnabled = true; } template void NetElementSignal::disableNetInterpolation() { m_netInterpolationEnabled = false; for (auto& p : take(m_pendingSignals)) send(std::move(p.second)); } template void NetElementSignal::tickNetInterpolation(float dt) { for (auto& p : m_pendingSignals) p.first -= dt; while (!m_pendingSignals.empty() && m_pendingSignals.first().first <= 0.0f) send(m_pendingSignals.takeFirst().second); } template bool NetElementSignal::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { size_t numToWrite = 0; for (auto const& p : m_signals) { if (p.version >= fromVersion) ++numToWrite; } if (numToWrite == 0) return false; ds.writeVlqU(numToWrite); for (auto const& p : m_signals) { if (p.version >= fromVersion) ds.write(p.signal); } return true; } template void NetElementSignal::readNetDelta(DataStream& ds, float interpolationTime) { size_t numToRead = ds.readVlqU(); for (size_t i = 0; i < numToRead; ++i) { Signal s; ds.read(s); if (m_netInterpolationEnabled && interpolationTime > 0.0f) { if (!m_pendingSignals.empty() && m_pendingSignals.last().first > interpolationTime) { for (auto& p : take(m_pendingSignals)) send(std::move(p.second)); } m_pendingSignals.append({interpolationTime, std::move(s)}); } else { send(std::move(s)); } } } template void NetElementSignal::send(Signal signal) { m_signals.append({m_netVersion ? m_netVersion->current() : 0, signal, false}); while (m_signals.size() > m_maxSignalQueue) m_signals.removeFirst(); } template List NetElementSignal::receive() { List received; for (auto& p : m_signals) { if (!p.received) { received.append(p.signal); p.received = true; } } return received; } } #endif