#include "StarNetTransmitters.hpp" #include "StarIterator.hpp" namespace Star { NetVersionSender::NetVersionSender() { m_currentVersion = 0; } NetVersionSender::NetVersionSender(NetSchema const& schema) : NetVersionSender() { initialize(schema); } void NetVersionSender::initialize(NetSchema const& schema) { if (!schema.isFinalized()) throw NetException("schema unfinalized in NetVersionSender::initialize"); m_currentVersion = 0; m_valuesByName.clear(); m_values.clear(); m_schema = schema; for (auto id : m_schema.ids()) { ValuePtr value = std::make_shared(); value->lastUpdated = m_currentVersion; // Initialize with default values for each NetType, so we can error if a // mismatched set was called. auto const& fieldInfo = m_schema.fieldInfo(id); if (fieldInfo.fixedPointBase != 0.0) { value->data.set(0.0); } else if (fieldInfo.type == NetType::Event) { value->data.set(0); } else if (netTypeIsSignedInt(fieldInfo.type)) { value->data.set(0); } else if (netTypeIsUnsignedInt(fieldInfo.type)) { value->data.set(0); } else if (fieldInfo.type == NetType::Size) { value->data.set(NPos); } else if (netTypeIsFloat(fieldInfo.type)) { value->data.set(0.0); } else if (fieldInfo.type == NetType::Bool) { value->data.set(false); } else if (fieldInfo.type == NetType::String) { value->data.set(String()); } else if (fieldInfo.type == NetType::Data) { value->data.set(ByteArray()); } else if (fieldInfo.type == NetType::Variant) { value->data.set(Variant()); } m_values.insert(id, value); m_valuesByName.insert(m_schema.name(id), value); } } NetSchema const& NetVersionSender::schema() const { return m_schema; } void NetVersionSender::resetVersion() { m_currentVersion = 0; for (auto& pair : m_values) pair.second->lastUpdated = m_currentVersion; } void NetVersionSender::incrementVersion(uint64_t version) { if (version < m_currentVersion) throw NetException("Version reversed in NetVersionSender::incrementVersion"); m_currentVersion = version; } uint64_t NetVersionSender::currentVersion() const { return m_currentVersion; } void NetVersionSender::triggerEvent(StringRef name) { auto const& value = getValue(name); if (!value->data.is()) throw NetException("NetSender type mismatch in triggerEvent"); value->data.set(value->data.get() + 1); value->lastUpdated = m_currentVersion; } void NetVersionSender::setInt(StringRef name, int64_t v) { auto const& value = getValue(name); if (!value->data.is()) throw NetException("NetSender type mismatch in setInt"); if (value->data.cget() != v) { value->lastUpdated = m_currentVersion; value->data.set(v); } } void NetVersionSender::setUInt(StringRef name, uint64_t v) { auto const& value = getValue(name); if (!value->data.is()) throw NetException("NetSender type mismatch in setUInt"); if (value->data.cget() != v) { value->lastUpdated = m_currentVersion; value->data.set(v); } } void NetVersionSender::setSize(StringRef name, size_t v) { auto const& value = getValue(name); if (!value->data.is()) throw NetException("NetSender type mismatch in setSize"); if (value->data.cget() != v) { value->lastUpdated = m_currentVersion; value->data.set(v); } } void NetVersionSender::setFloat(StringRef name, double v) { auto const& value = getValue(name); if (!value->data.is()) throw NetException("NetSender type mismatch in setFloat"); // TODO: For right now does a straight != comparison, should we include a // tolerance setting? if (value->data.cget() != v) { value->lastUpdated = m_currentVersion; value->data.set(v); } } void NetVersionSender::setBool(StringRef name, bool v) { auto const& value = getValue(name); if (!value->data.is()) throw NetException("NetSender type mismatch in setBool"); if (value->data.cget() != v) { value->lastUpdated = m_currentVersion; value->data.set(v); } } void NetVersionSender::setString(StringRef name, String const& v) { auto const& value = getValue(name); if (!value->data.is()) throw NetException("NetSender type mismatch in setString"); if (value->data.cget() != v) { value->lastUpdated = m_currentVersion; value->data.set(v); } } void NetVersionSender::setData(StringRef name, ByteArray const& v) { auto const& value = getValue(name); if (!value->data.is()) throw NetException("NetSender type mismatch in setData"); if (value->data.cget() != v) { value->lastUpdated = m_currentVersion; value->data.set(v); } } void NetVersionSender::setVariant(StringRef name, Variant const& v) { auto const& value = getValue(name); if (!value->data.is()) throw NetException("NetSender type mismatch in setVariant"); if (value->data.cget() != v) { value->lastUpdated = m_currentVersion; value->data.set(v); } } size_t NetVersionSender::writeFull(DataStream& ds) const { size_t bytesWritten = 0; for (auto id : m_schema.ids()) { auto const& value = m_values.value(id); auto const& field = m_schema.fieldInfo(id); bytesWritten += writeValue(ds, field, value); } return bytesWritten; } ByteArray NetVersionSender::writeFull() const { DataStreamBuffer buffer; writeFull(buffer); return buffer.data(); } size_t NetVersionSender::writeDelta(DataStream& ds, uint64_t sinceVersion) const { size_t bytesWritten = 0; for (auto const& pair : m_values) { auto const& value = pair.second; if (value->lastUpdated < sinceVersion) continue; auto const& field = m_schema.fieldInfo(pair.first); bytesWritten += ds.writeVlqU(pair.first); bytesWritten += writeValue(ds, field, value); } return bytesWritten; } ByteArray NetVersionSender::writeDelta(uint64_t sinceVersion) const { DataStreamBuffer buffer; writeDelta(buffer, sinceVersion); return buffer.data(); } NetVersionSender::ValuePtr const& NetVersionSender::getValue(StringRef name) const { auto i = m_valuesByName.find(name); if (i == m_valuesByName.end()) throw NetException(strf("No such NetSender state named '%s'", name)); return i->second; } size_t NetVersionSender::writeValue(DataStream& ds, NetFieldInfo const& fieldInfo, ValuePtr const& value) const { StreamOffset pos = ds.pos(); DataPoint data; if (fieldInfo.fixedPointBase != 0.0) { if (netTypeIsSignedInt(fieldInfo.type)) data.set(value->data.get() / fieldInfo.fixedPointBase); else data.set(value->data.get() / fieldInfo.fixedPointBase); } else { data = value->data; } if (fieldInfo.type == NetType::Event) { ds.writeVlqU(data.get()); } else if (fieldInfo.type == NetType::Int8) { ds.write(data.get()); } else if (fieldInfo.type == NetType::UInt8) { ds.write(data.get()); } else if (fieldInfo.type == NetType::Int16) { ds.write(data.get()); } else if (fieldInfo.type == NetType::UInt16) { ds.write(data.get()); } else if (fieldInfo.type == NetType::VarInt) { ds.writeVlqI(data.get()); } else if (fieldInfo.type == NetType::VarUInt) { ds.writeVlqU(data.get()); } else if (fieldInfo.type == NetType::Size) { size_t v = data.get(); ds.writeVlqU(v == NPos ? 0 : v + 1); } else if (fieldInfo.type == NetType::Float) { ds.write(data.get()); } else if (fieldInfo.type == NetType::Double) { ds.write(data.get()); } else if (fieldInfo.type == NetType::NFloat8) { ds.write(std::round(data.get() * 255.0f)); } else if (fieldInfo.type == NetType::NFloat16) { ds.write(std::round(data.get() * 65535.0f)); } else if (fieldInfo.type == NetType::Bool) { ds.write(data.get()); } else if (fieldInfo.type == NetType::String) { ds.write(data.get()); } else if (fieldInfo.type == NetType::Data) { ds.write(data.get()); } else if (fieldInfo.type == NetType::Variant) { ds.write(data.get()); } return ds.pos() - pos; } NetSyncSender::NetSyncSender() {} NetSyncSender::NetSyncSender(NetSchema const& schema) { initialize(schema); } size_t NetSyncSender::writeDelta(DataStream& ds) { size_t size = NetVersionSender::writeDelta(ds, currentVersion()); // Increment the version on write so we only write newly written states. incrementVersion(currentVersion() + 1); return size; } size_t NetSyncSender::writeFull(DataStream& ds) { size_t size = NetVersionSender::writeFull(ds); // Increment the version on write so we only write newly written states. incrementVersion(currentVersion() + 1); return size; } ByteArray NetSyncSender::writeFull() { DataStreamBuffer buffer; writeFull(buffer); return buffer.data(); } ByteArray NetSyncSender::writeDelta() { DataStreamBuffer buffer; writeDelta(buffer); return buffer.data(); } NetReceiver::NetReceiver() : m_interpolationEnabled(false), m_interpolationStep(0.0) {} NetReceiver::NetReceiver(NetSchema const& schema) : NetReceiver() { initialize(schema); } void NetReceiver::initialize(NetSchema const& schema) { if (!schema.isFinalized()) throw NetException("schema unfinalized in NetReceiver::initialize"); m_valuesByName.clear(); m_values.clear(); m_schema = schema; for (auto id : m_schema.ids()) { ValuePtr value = std::make_shared(); value->needsForwarding = false; value->exactUpdated = false; // Initialize with default values because NetSender assumes these as the // starting values without sending any data auto const& fieldInfo = m_schema.fieldInfo(id); if (fieldInfo.fixedPointBase != 0.0) { value->latestData = 0.0; } else if (fieldInfo.type == NetType::Event) { value->latestData = (uint64_t)0; } else if (netTypeIsSignedInt(fieldInfo.type)) { value->latestData = (int64_t)0; } else if (netTypeIsUnsignedInt(fieldInfo.type)) { value->latestData = (uint64_t)0; } else if (fieldInfo.type == NetType::Size) { value->latestData = (size_t)NPos; } else if (netTypeIsFloat(fieldInfo.type)) { value->latestData = 0.0; } else if (fieldInfo.type == NetType::Bool) { value->latestData = false; } else if (fieldInfo.type == NetType::String) { value->latestData = String(); } else if (fieldInfo.type == NetType::Data) { value->latestData = ByteArray(); } else if (fieldInfo.type == NetType::Variant) { value->latestData = Variant(); } value->exactData = value->latestData; value->exactUpdated = false; if (m_interpolationEnabled) { value->stream.reset(); value->stream.appendDataPoint(m_interpolationStep, value->latestData); } m_values.insert(id, value); m_valuesByName.insert(m_schema.name(id), value); } } NetSchema const& NetReceiver::schema() const { return m_schema; } void NetReceiver::enableInterpolation(double startStep, double extrapolation) { m_interpolationEnabled = true; m_interpolationStep = startStep; for (auto const& pair : m_values) { pair.second->stream.reset(); pair.second->stream.setExtrapolation(extrapolation); pair.second->stream.appendDataPoint(startStep, pair.second->latestData); pair.second->stream.setStep(startStep); if (pair.second->exactData != pair.second->latestData) { pair.second->exactData = pair.second->latestData; pair.second->exactUpdated = true; } } } void NetReceiver::disableInterpolation() { m_interpolationEnabled = false; m_interpolationStep = 0.0; for (auto const& pair : m_values) { pair.second->stream.reset(); if (pair.second->exactData != pair.second->latestData) { pair.second->exactData = pair.second->latestData; pair.second->exactUpdated = true; } } } void NetReceiver::setInterpolationDiffFunction(StringRef name, DifferenceFunction diffFunction) { getValue(name)->diffFunction = diffFunction; } void NetReceiver::setInterpolationStep(double step) { if (!m_interpolationEnabled) return; m_interpolationStep = step; for (auto const& pair : m_values) { pair.second->stream.setStep(step); auto data = pair.second->stream.exact(); if (data != pair.second->exactData) { pair.second->exactData = data; pair.second->exactUpdated = true; } } } void NetReceiver::readFull(DataStream& ds, size_t dataSize) { int bytesLeft = dataSize; for (unsigned id : m_schema.ids()) { auto const& field = m_schema.fieldInfo(id); bytesLeft -= readValue(ds, field, m_values[id], m_interpolationStep); if (bytesLeft < 0) throw NetException("Read past end of dataSize in NetReceiver::readFull!"); } if (bytesLeft != 0) throw NetException("Extra data at end of NetReceiver::readFull!"); } void NetReceiver::readFull(ByteArray const& fullState) { DataStreamBuffer buffer(fullState); readFull(buffer, buffer.size()); } void NetReceiver::readDelta(DataStream& ds, size_t dataSize, double interpolationStep) { int bytesLeft = dataSize; while (bytesLeft > 0) { uint64_t id; bytesLeft -= ds.readVlqU(id); bytesLeft -= readValue(ds, m_schema.fieldInfo(id), m_values[id], interpolationStep); // TODO: This is pretty terrible, we shouldn't read extra *then* fail if (bytesLeft < 0) throw NetException("Read past end of dataSize in NetReceiver::readDelta!"); } } void NetReceiver::readDelta(ByteArray const& deltaState, double interpolationStep) { DataStreamBuffer buffer(deltaState); readDelta(buffer, buffer.size(), interpolationStep); } void NetReceiver::interpolationHeartbeat(double interpolationStep) { if (m_interpolationEnabled) { for (auto const& pair : m_values) pair.second->stream.heartbeat(interpolationStep); } } bool NetReceiver::updated(StringRef name) const { return getValue(name)->exactUpdated; } bool NetReceiver::eventOccurred(StringRef eventName) { auto const& value = getValue(eventName); if (value->exactUpdated) { value->exactUpdated = false; return true; } else { return false; } } int64_t NetReceiver::getInt(StringRef name) { auto const& value = getValue(name); value->exactUpdated = false; return value->exactData.cget(); } uint64_t NetReceiver::getUInt(StringRef name) { auto const& value = getValue(name); value->exactUpdated = false; return value->exactData.cget(); } size_t NetReceiver::getSize(StringRef name) { auto const& value = getValue(name); value->exactUpdated = false; return value->exactData.cget(); } double NetReceiver::getFloat(StringRef name, bool interpolateIfAvailable) { auto const& value = getValue(name); value->exactUpdated = false; if (m_interpolationEnabled && interpolateIfAvailable) { auto weighting = value->stream.weighting(); auto pointMin = weighting.pointMin.cget(); auto pointMax = weighting.pointMax.cget(); if (value->diffFunction) return pointMin + value->diffFunction(pointMax, pointMin) * weighting.offset; else return pointMin + (pointMax - pointMin) * weighting.offset; } else { return value->exactData.cget(); } } bool NetReceiver::getBool(StringRef name) { auto const& value = getValue(name); value->exactUpdated = false; return value->exactData.cget(); } String NetReceiver::getString(StringRef name) { auto const& value = getValue(name); value->exactUpdated = false; return value->exactData.cget(); } ByteArray NetReceiver::getData(StringRef name) { auto const& value = getValue(name); value->exactUpdated = false; return value->exactData.cget(); } Variant NetReceiver::getVariant(StringRef name) { auto const& value = getValue(name); value->exactUpdated = false; return value->exactData.cget(); } void NetReceiver::forward(NetVersionSender& sender, bool forceAll) { for (auto id : m_schema.ids()) { auto const& value = m_values.get(id); auto const& fieldInfo = m_schema.fieldInfo(id); if (!forceAll && !value->needsForwarding) continue; if (fieldInfo.fixedPointBase != 0.0) { sender.setFloat(fieldInfo.name, value->latestData.cget()); } else if (fieldInfo.type == NetType::Event) { if (value->needsForwarding) sender.triggerEvent(fieldInfo.name); } else if (netTypeIsSignedInt(fieldInfo.type)) { sender.setInt(fieldInfo.name, value->latestData.cget()); } else if (netTypeIsUnsignedInt(fieldInfo.type)) { sender.setUInt(fieldInfo.name, value->latestData.cget()); } else if (fieldInfo.type == NetType::Size) { sender.setSize(fieldInfo.name, value->latestData.cget()); } else if (netTypeIsFloat(fieldInfo.type)) { sender.setFloat(fieldInfo.name, value->latestData.cget()); } else if (fieldInfo.type == NetType::Bool) { sender.setBool(fieldInfo.name, value->latestData.cget()); } else if (fieldInfo.type == NetType::String) { sender.setString(fieldInfo.name, value->latestData.cget()); } else if (fieldInfo.type == NetType::Data) { sender.setData(fieldInfo.name, value->latestData.cget()); } else if (fieldInfo.type == NetType::Variant) { sender.setVariant(fieldInfo.name, value->latestData.cget()); } value->needsForwarding = false; } } NetReceiver::ValuePtr const& NetReceiver::getValue(StringRef name) const { auto i = m_valuesByName.find(name); if (i == m_valuesByName.end()) throw NetException(strf("No such NetReceiver state named '%s'", name)); return i->second; } size_t NetReceiver::readValue(DataStream& ds, NetFieldInfo const& fieldInfo, ValuePtr const& value, double step) { StreamOffset pos = ds.pos(); DataPoint dataPoint; if (fieldInfo.type == NetType::Event) { dataPoint.set(ds.readVlqU()); } else if (fieldInfo.type == NetType::Int8) { dataPoint.set(ds.read()); } else if (fieldInfo.type == NetType::UInt8) { dataPoint.set(ds.read()); } else if (fieldInfo.type == NetType::Int16) { dataPoint.set(ds.read()); } else if (fieldInfo.type == NetType::UInt16) { dataPoint.set(ds.read()); } else if (fieldInfo.type == NetType::VarInt) { dataPoint.set(ds.readVlqI()); } else if (fieldInfo.type == NetType::VarUInt) { dataPoint.set(ds.readVlqU()); } else if (fieldInfo.type == NetType::Size) { auto size = ds.readVlqU(); dataPoint.set(size == 0 ? NPos : size - 1); } else if (fieldInfo.type == NetType::Float) { dataPoint.set(ds.read()); } else if (fieldInfo.type == NetType::NFloat8) { dataPoint.set(ds.read() / 255.0f); } else if (fieldInfo.type == NetType::NFloat16) { dataPoint.set(ds.read() / 65535.0f); } else if (fieldInfo.type == NetType::Double) { dataPoint.set(ds.read()); } else if (fieldInfo.type == NetType::Bool) { dataPoint.set(ds.read()); } else if (fieldInfo.type == NetType::String) { dataPoint.set(ds.read()); } else if (fieldInfo.type == NetType::Data) { dataPoint.set(ds.read()); } else if (fieldInfo.type == NetType::Variant) { dataPoint.set(ds.read()); } if (fieldInfo.fixedPointBase != 0.0) { if (netTypeIsSignedInt(fieldInfo.type)) dataPoint.set(dataPoint.cget() * fieldInfo.fixedPointBase); else dataPoint.set(dataPoint.cget() * fieldInfo.fixedPointBase); } value->latestData = dataPoint; value->needsForwarding = true; if (m_interpolationEnabled) { value->stream.appendDataPoint(step, dataPoint); auto data = value->stream.exact(); if (value->exactData != data) { value->exactData = data; value->exactUpdated = true; } } else { if (value->exactData != value->latestData) { value->exactData = value->latestData; value->exactUpdated = true; } } return ds.pos() - pos; } }