diff options
Diffstat (limited to 'source/core')
26 files changed, 560 insertions, 193 deletions
diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt index fb31fca..2f0c858 100644 --- a/source/core/CMakeLists.txt +++ b/source/core/CMakeLists.txt @@ -70,10 +70,12 @@ SET (star_core_HEADERS StarMultiArray.hpp StarMultiArrayInterpolator.hpp StarMultiTable.hpp + StarNetCompatibility.hpp StarNetElement.hpp StarNetElementBasicFields.hpp StarNetElementContainers.hpp StarNetElementDynamicGroup.hpp + StarNetElementExt.hpp StarNetElementFloatFields.hpp StarNetElementGroup.hpp StarNetElementSignal.hpp @@ -122,6 +124,7 @@ SET (star_core_HEADERS StarUnicode.hpp StarUuid.hpp StarVector.hpp + StarVersion.hpp StarVlqEncoding.hpp StarWeightedPool.hpp StarWorkerPool.hpp @@ -162,6 +165,7 @@ SET (star_core_SOURCES StarLua.cpp StarLuaConverters.cpp StarMemory.cpp + StarNetCompatibility.cpp StarNetElement.cpp StarNetElementBasicFields.cpp StarNetElementGroup.cpp @@ -217,7 +221,8 @@ ELSEIF (STAR_SYSTEM_FAMILY_WINDOWS) ENDIF () -ADD_LIBRARY (star_core OBJECT ${star_core_SOURCES} ${star_core_HEADERS}) +CONFIGURE_FILE (StarVersion.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/StarVersion.cpp) +ADD_LIBRARY (star_core OBJECT ${star_core_SOURCES} ${star_core_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/StarVersion.cpp) IF(STAR_PRECOMPILED_HEADERS) TARGET_PRECOMPILE_HEADERS (star_core PUBLIC StarPch.hpp) diff --git a/source/core/StarBTreeDatabase.cpp b/source/core/StarBTreeDatabase.cpp index 19b0bd2..f66bea1 100644 --- a/source/core/StarBTreeDatabase.cpp +++ b/source/core/StarBTreeDatabase.cpp @@ -1,6 +1,7 @@ #include "StarBTreeDatabase.hpp" #include "StarSha256.hpp" #include "StarVlqEncoding.hpp" +#include "StarLogging.hpp" namespace Star { @@ -243,7 +244,7 @@ uint32_t BTreeDatabase::freeBlockCount() { indexBlockIndex = indexBlock.nextFreeBlock; } - count += m_availableBlocks.size() + m_pendingFree.size(); + count += m_availableBlocks.size(); // Include untracked blocks at the end of the file in the free count. count += (m_device->size() - m_deviceSize) / m_blockSize; @@ -272,7 +273,7 @@ uint32_t BTreeDatabase::leafBlockCount() { return true; } - BTreeDatabase* parent; + BTreeDatabase* parent = nullptr; BlockIndex leafBlockCount = 0; }; @@ -293,8 +294,8 @@ void BTreeDatabase::rollback() { m_availableBlocks.clear(); m_indexCache.clear(); + m_uncommittedWrites.clear(); m_uncommitted.clear(); - m_pendingFree.clear(); readRoot(); @@ -305,7 +306,8 @@ void BTreeDatabase::rollback() { void BTreeDatabase::close(bool closeDevice) { WriteLocker writeLocker(m_lock); if (m_open) { - doCommit(); + if (!tryFlatten()) + doCommit(); m_indexCache.clear(); @@ -536,7 +538,7 @@ auto BTreeDatabase::BTreeImpl::loadIndex(Pointer pointer) -> Index { index->pointers.resize(s); for (uint32_t i = 0; i < s; ++i) { auto& e = index->pointers[i]; - e.key =buffer.readBytes(parent->m_keySize); + e.key = buffer.readBytes(parent->m_keySize); e.pointer = buffer.read<BlockIndex>(); } @@ -896,17 +898,25 @@ void BTreeDatabase::rawReadBlock(BlockIndex blockIndex, size_t blockOffset, char if (size <= 0) return; - m_device->readFullAbsolute(HeaderSize + blockIndex * (StreamOffset)m_blockSize + blockOffset, block, size); + if (auto buffer = m_uncommittedWrites.ptr(blockIndex)) + buffer->copyTo(block, blockOffset, size); + else + m_device->readFullAbsolute(HeaderSize + blockIndex * (StreamOffset)m_blockSize + blockOffset, block, size); } -void BTreeDatabase::rawWriteBlock(BlockIndex blockIndex, size_t blockOffset, char const* block, size_t size) const { +void BTreeDatabase::rawWriteBlock(BlockIndex blockIndex, size_t blockOffset, char const* block, size_t size) { if (blockOffset > m_blockSize || size > m_blockSize - blockOffset) throw DBException::format("Write past end of block, offset: {} size {}", blockOffset, size); if (size <= 0) return; - m_device->writeFullAbsolute(HeaderSize + blockIndex * (StreamOffset)m_blockSize + blockOffset, block, size); + StreamOffset blockStart = HeaderSize + blockIndex * (StreamOffset)m_blockSize; + auto buffer = m_uncommittedWrites.find(blockIndex); + if (buffer == m_uncommittedWrites.end()) + buffer = m_uncommittedWrites.emplace(blockIndex, m_device->readBytesAbsolute(blockStart, m_blockSize)).first; + + buffer->second.writeFrom(block, blockOffset, size); } auto BTreeDatabase::readFreeIndexBlock(BlockIndex blockIndex) -> FreeIndexBlock { @@ -991,12 +1001,12 @@ auto BTreeDatabase::leafTailBlocks(BlockIndex leafPointer) -> List<BlockIndex> { } void BTreeDatabase::freeBlock(BlockIndex b) { - if (m_uncommitted.contains(b)) { + if (m_uncommitted.contains(b)) m_uncommitted.remove(b); - m_availableBlocks.add(b); - } else { - m_pendingFree.append(b); - } + if (m_uncommittedWrites.contains(b)) + m_uncommittedWrites.remove(b); + + m_availableBlocks.add(b); } auto BTreeDatabase::reserveBlock() -> BlockIndex { @@ -1007,10 +1017,7 @@ auto BTreeDatabase::reserveBlock() -> BlockIndex { FreeIndexBlock indexBlock = readFreeIndexBlock(m_headFreeIndexBlock); for (auto const& b : indexBlock.freeBlocks) m_availableBlocks.add(b); - // We cannot make available the block itself, because we must maintain - // atomic consistency. We will need to free this block later and commit - // the new free index block chain. - m_pendingFree.append(m_headFreeIndexBlock); + m_availableBlocks.add(m_headFreeIndexBlock); m_headFreeIndexBlock = indexBlock.nextFreeBlock; } @@ -1068,63 +1075,166 @@ void BTreeDatabase::readRoot() { } void BTreeDatabase::doCommit() { - if (m_availableBlocks.empty() && m_pendingFree.empty() && m_uncommitted.empty()) + if (m_availableBlocks.empty() && m_uncommitted.empty()) return; - if (!m_availableBlocks.empty() || !m_pendingFree.empty()) { + if (!m_availableBlocks.empty()) { // First, read the existing head FreeIndexBlock, if it exists FreeIndexBlock indexBlock = FreeIndexBlock{InvalidBlockIndex, {}}; - if (m_headFreeIndexBlock != InvalidBlockIndex) { + + auto newBlock = [&]() -> BlockIndex { + if (!m_availableBlocks.empty()) + return m_availableBlocks.takeFirst(); + else + return makeEndBlock(); + }; + + if (m_headFreeIndexBlock != InvalidBlockIndex) indexBlock = readFreeIndexBlock(m_headFreeIndexBlock); - if (indexBlock.freeBlocks.size() >= maxFreeIndexLength()) { - // If the existing head free index block is full, then we should start a - // new one and leave it alone - indexBlock.nextFreeBlock = m_headFreeIndexBlock; - indexBlock.freeBlocks.clear(); - } else { - // If we are copying an existing free index block, the old free index - // block will be a newly freed block - indexBlock.freeBlocks.append(m_headFreeIndexBlock); - } - } + else + m_headFreeIndexBlock = newBlock(); - // Then, we need to write all the available blocks, which are safe to write - // to, and the pending free blocks, which are NOT safe to write to, to the - // FreeIndexBlock chain. + // Then, we need to write all the available blocks to the FreeIndexBlock chain. while (true) { - if (indexBlock.freeBlocks.size() < maxFreeIndexLength() && (!m_availableBlocks.empty() || !m_pendingFree.empty())) { - // If we have room on our current FreeIndexblock, just add a block to - // it. Prioritize the pending free blocks, because we cannot use those - // to write to. - BlockIndex toAdd; - if (m_pendingFree.empty()) - toAdd = m_availableBlocks.takeFirst(); - else - toAdd = m_pendingFree.takeFirst(); - + // If we have room on our current FreeIndexBlock, just add a block to it. + if (!m_availableBlocks.empty() && indexBlock.freeBlocks.size() < maxFreeIndexLength()) { + BlockIndex toAdd = m_availableBlocks.takeFirst(); indexBlock.freeBlocks.append(toAdd); } else { - // If our index block is full OR we are out of blocks to free, then - // need to write a new head free index block. - if (m_availableBlocks.empty()) - m_headFreeIndexBlock = makeEndBlock(); - else - m_headFreeIndexBlock = m_availableBlocks.takeFirst(); + // Update the current head free index block. writeFreeIndexBlock(m_headFreeIndexBlock, indexBlock); // If we're out of blocks to free, then we're done - if (m_availableBlocks.empty() && m_pendingFree.empty()) + if (m_availableBlocks.empty()) break; - indexBlock.nextFreeBlock = m_headFreeIndexBlock; - indexBlock.freeBlocks.clear(); + // If our head free index block is full, then + // need to write a new head free index block. + if (indexBlock.freeBlocks.size() >= maxFreeIndexLength()) { + indexBlock.nextFreeBlock = m_headFreeIndexBlock; + indexBlock.freeBlocks.clear(); + + m_headFreeIndexBlock = newBlock(); + writeFreeIndexBlock(m_headFreeIndexBlock, indexBlock); + } } } } + + commitWrites(); writeRoot(); + m_uncommitted.clear(); +} +void BTreeDatabase::commitWrites() { + for (auto& write : m_uncommittedWrites) + m_device->writeFullAbsolute(HeaderSize + write.first * (StreamOffset)m_blockSize, write.second.ptr(), m_blockSize); + + m_device->sync(); + m_uncommittedWrites.clear(); +} + +bool BTreeDatabase::tryFlatten() { + if (m_headFreeIndexBlock == InvalidBlockIndex || m_rootIsLeaf || !m_device->isWritable()) + return false; + + BlockIndex freeBlockCount = 0; + BlockIndex indexBlockIndex = m_headFreeIndexBlock; + while (indexBlockIndex != InvalidBlockIndex) { + FreeIndexBlock indexBlock = readFreeIndexBlock(indexBlockIndex); + freeBlockCount += 1 + indexBlock.freeBlocks.size(); + indexBlockIndex = indexBlock.nextFreeBlock; + } + + BlockIndex expectedBlockCount = (m_deviceSize - HeaderSize) / m_blockSize; + float free = float(freeBlockCount) / float(expectedBlockCount); + if (free < 0.05f) + return false; + + Logger::info("[BTreeDatabase] File '{}' is {:.2f}% free space, flattening", m_device->deviceName(), free * 100.f); + + indexBlockIndex = m_headFreeIndexBlock; + { + List<BlockIndex> availableBlocksList; + do { + FreeIndexBlock indexBlock = readFreeIndexBlock(indexBlockIndex); + availableBlocksList.appendAll(indexBlock.freeBlocks); + availableBlocksList.append(indexBlockIndex); + indexBlockIndex = indexBlock.nextFreeBlock; + } while (indexBlockIndex != InvalidBlockIndex); + m_headFreeIndexBlock = InvalidBlockIndex; + + sort(availableBlocksList); + for (auto& availableBlock : availableBlocksList) + m_availableBlocks.insert(m_availableBlocks.end(), availableBlock); + } + + BlockIndex count = 1; // 1 to include root index + + double start = Time::monotonicTime(); + auto index = m_impl.loadIndex(m_impl.rootPointer()); + if (flattenVisitor(index, count)) { + m_impl.deleteIndex(index); + index->self = InvalidBlockIndex; + m_root = m_impl.storeIndex(index); + } + + m_availableBlocks.clear(); + m_device->resize(m_deviceSize = HeaderSize + (StreamOffset)m_blockSize * count); + + m_indexCache.clear(); + commitWrites(); + writeRoot(); m_uncommitted.clear(); + + Logger::info("[BTreeDatabase] Finished flattening '{}' in {:.2f} milliseconds", m_device->deviceName(), (Time::monotonicTime() - start) * 1000.f); + return true; +} + +bool BTreeDatabase::flattenVisitor(BTreeImpl::Index& index, BlockIndex& count) { + auto pointerCount = index->pointerCount(); + count += pointerCount; + bool canStore = !m_availableBlocks.empty(); + + bool needsStore = false; + if (m_impl.indexLevel(index) == 0) { + for (size_t i = 0; i != pointerCount; ++i) { + auto indexPointer = index->pointer(i); + auto tailBlocks = leafTailBlocks(indexPointer); + if (canStore) { + bool leafNeedsStore = m_availableBlocks.first() < indexPointer; + + if (!leafNeedsStore) + for (size_t i = 0; !leafNeedsStore && i != tailBlocks.size(); ++i) + if (m_availableBlocks.first() < tailBlocks[i]) + leafNeedsStore = true; + + if (leafNeedsStore) { + auto leaf = m_impl.loadLeaf(indexPointer); + m_impl.deleteLeaf(leaf); + leaf->self = InvalidBlockIndex; + index->updatePointer(i, m_impl.storeLeaf(leaf)); + tailBlocks = leafTailBlocks(leaf->self); + needsStore = true; + } + canStore = !m_availableBlocks.empty(); + } + count += tailBlocks.size(); + } + } else { + for (size_t i = 0; i != pointerCount; ++i) { + auto childIndex = m_impl.loadIndex(index->pointer(i)); + if (canStore && flattenVisitor(childIndex, count)) { + m_impl.deleteIndex(childIndex); + childIndex->self = InvalidBlockIndex; + index->updatePointer(i, m_impl.storeIndex(childIndex)); + canStore = !m_availableBlocks.empty(); + needsStore = true; + } + } + } + return needsStore || (canStore && m_availableBlocks.first() < index->self); } void BTreeDatabase::checkIfOpen(char const* methodName, bool shouldBeOpen) const { @@ -1146,7 +1256,7 @@ void BTreeDatabase::checkKeySize(ByteArray const& k) const { } uint32_t BTreeDatabase::maxFreeIndexLength() const { - return (m_blockSize - 2 - sizeof(BlockIndex) - 4) / sizeof(BlockIndex); + return (m_blockSize / sizeof(BlockIndex)) - 2 - sizeof(BlockIndex) - 4; } BTreeSha256Database::BTreeSha256Database() { diff --git a/source/core/StarBTreeDatabase.hpp b/source/core/StarBTreeDatabase.hpp index f1b88a1..b3a530f 100644 --- a/source/core/StarBTreeDatabase.hpp +++ b/source/core/StarBTreeDatabase.hpp @@ -230,7 +230,7 @@ private: void updateBlock(BlockIndex blockIndex, ByteArray const& block); void rawReadBlock(BlockIndex blockIndex, size_t blockOffset, char* block, size_t size) const; - void rawWriteBlock(BlockIndex blockIndex, size_t blockOffset, char const* block, size_t size) const; + void rawWriteBlock(BlockIndex blockIndex, size_t blockOffset, char const* block, size_t size); void updateHeadFreeIndexBlock(BlockIndex newHead); @@ -251,6 +251,9 @@ private: void writeRoot(); void readRoot(); void doCommit(); + void commitWrites(); + bool tryFlatten(); + bool flattenVisitor(BTreeImpl::Index& index, BlockIndex& count); void checkIfOpen(char const* methodName, bool shouldBeOpen) const; void checkBlockIndex(size_t blockIndex) const; @@ -285,14 +288,14 @@ private: bool m_dirty; // Blocks that can be freely allocated and written to without violating - // atomic consistency + // atomic consistency. Set<BlockIndex> m_availableBlocks; - // Blocks to be freed on next commit. - Deque<BlockIndex> m_pendingFree; - // Blocks that have been written in uncommitted portions of the tree. Set<BlockIndex> m_uncommitted; + + // Temporarily holds written data so that it can be rolled back. + mutable Map<BlockIndex, ByteArray> m_uncommittedWrites; }; // Version of BTreeDatabase that hashes keys with SHA-256 to produce a unique diff --git a/source/core/StarDataStream.cpp b/source/core/StarDataStream.cpp index 18456b6..3a4ed0c 100644 --- a/source/core/StarDataStream.cpp +++ b/source/core/StarDataStream.cpp @@ -35,6 +35,10 @@ void DataStream::setStreamCompatibilityVersion(unsigned streamCompatibilityVersi m_streamCompatibilityVersion = streamCompatibilityVersion; } +void DataStream::setStreamCompatibilityVersion(NetCompatibilityRules const& rules) { + m_streamCompatibilityVersion = rules.version(); +} + ByteArray DataStream::readBytes(size_t len) { ByteArray ba; ba.resize(len); diff --git a/source/core/StarDataStream.hpp b/source/core/StarDataStream.hpp index 02cb922..abcbff4 100644 --- a/source/core/StarDataStream.hpp +++ b/source/core/StarDataStream.hpp @@ -1,6 +1,7 @@ #pragma once #include "StarString.hpp" +#include "StarNetCompatibility.hpp" namespace Star { @@ -12,7 +13,7 @@ public: DataStream(); virtual ~DataStream() = default; - static unsigned const CurrentStreamVersion = 1; + static unsigned const CurrentStreamVersion = 2; // DataStream defaults to big-endian order for all primitive types ByteOrder byteOrder() const; @@ -27,7 +28,7 @@ public: // changed for compatibility with older versions of DataStream serialization. unsigned streamCompatibilityVersion() const; void setStreamCompatibilityVersion(unsigned streamCompatibilityVersion); - + void setStreamCompatibilityVersion(NetCompatibilityRules const& rules); // Do direct reads and writes virtual void readData(char* data, size_t len) = 0; virtual void writeData(char const* data, size_t len) = 0; diff --git a/source/core/StarDataStreamDevices.hpp b/source/core/StarDataStreamDevices.hpp index 3f34a72..4452592 100644 --- a/source/core/StarDataStreamDevices.hpp +++ b/source/core/StarDataStreamDevices.hpp @@ -126,8 +126,8 @@ private: class DataStreamExternalBuffer : public DataStream { public: DataStreamExternalBuffer(); - explicit DataStreamExternalBuffer(ByteArray const& byteArray); - explicit DataStreamExternalBuffer(DataStreamBuffer const& buffer); + DataStreamExternalBuffer(ByteArray const& byteArray); + DataStreamExternalBuffer(DataStreamBuffer const& buffer); DataStreamExternalBuffer(DataStreamExternalBuffer const& buffer) = default; DataStreamExternalBuffer(char const* externalData, size_t len); diff --git a/source/core/StarFont.cpp b/source/core/StarFont.cpp index 47569f2..b0d20fc 100644 --- a/source/core/StarFont.cpp +++ b/source/core/StarFont.cpp @@ -30,8 +30,8 @@ FTContext ftContext; struct FontImpl { FT_Face face; - unsigned loadedPixelSize = 0; String::Char loadedChar = 0; + unsigned renderedPixelSize = 0; }; FontPtr Font::loadFont(String const& fileName, unsigned pixelSize) { @@ -84,7 +84,8 @@ unsigned Font::width(String::Char c) { if (auto width = m_widthCache.maybe({c, m_pixelSize})) { return *width; } else { - FT_Load_Char(m_fontImpl->face, c, FontLoadFlags); + if (m_fontImpl->loadedChar != c) + FT_Load_Char(m_fontImpl->face, m_fontImpl->loadedChar = c, FontLoadFlags); unsigned newWidth = (m_fontImpl->face->glyph->linearHoriAdvance + 32768) / 65536; m_widthCache.insert({c, m_pixelSize}, newWidth); return newWidth; @@ -98,19 +99,19 @@ tuple<Image, Vec2I, bool> Font::render(String::Char c) { FT_Face face = m_fontImpl->face; - if (m_fontImpl->loadedPixelSize != m_pixelSize || m_fontImpl->loadedChar != c) { - FT_UInt glyph_index = FT_Get_Char_Index(face, c); - if (FT_Load_Glyph(face, glyph_index, FontLoadFlags) != 0) + if (m_fontImpl->loadedChar != c) { + if (FT_Load_Glyph(face, FT_Get_Char_Index(face, m_fontImpl->loadedChar = c), FontLoadFlags) != 0) return {}; - // convert to an anti-aliased bitmap + m_fontImpl->renderedPixelSize = 0; + } + + if (m_fontImpl->renderedPixelSize != m_pixelSize) { + m_fontImpl->renderedPixelSize = m_pixelSize; if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL) != 0) return {}; } - m_fontImpl->loadedPixelSize = m_pixelSize; - m_fontImpl->loadedChar = c; - FT_GlyphSlot slot = face->glyph; if (!slot->bitmap.buffer) return {}; @@ -134,7 +135,7 @@ tuple<Image, Vec2I, bool> Font::render(String::Char c) { } } } - } else if (colored = slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) { + } else if (colored = (slot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA)) { unsigned bpp = image.bytesPerPixel(); uint8_t* data = image.data() + bpp + ((image.width() * (image.height() - 2)) * bpp); // offset by 1 pixel as it's padded for (size_t y = 0; y != height; ++y) { diff --git a/source/core/StarImageProcessing.cpp b/source/core/StarImageProcessing.cpp index 6d2c51d..390f600 100644 --- a/source/core/StarImageProcessing.cpp +++ b/source/core/StarImageProcessing.cpp @@ -214,7 +214,7 @@ ImageOperation imageOperationFromString(StringView string) { else // we're in A of A=B. In vanilla only A=B pairs are evaluated, so only throw an error if B is also there. return operation; - if (which = !which) + if ((which = !which)) operation.colorReplaceMap[*(Vec4B*)&a] = *(Vec4B*)&b; hexLen = 0; diff --git a/source/core/StarNetCompatibility.cpp b/source/core/StarNetCompatibility.cpp new file mode 100644 index 0000000..fdd53fb --- /dev/null +++ b/source/core/StarNetCompatibility.cpp @@ -0,0 +1,7 @@ +#include "StarNetCompatibility.hpp" + +namespace Star { + +VersionNumber const OpenProtocolVersion = 2; + +}
\ No newline at end of file diff --git a/source/core/StarNetCompatibility.hpp b/source/core/StarNetCompatibility.hpp new file mode 100644 index 0000000..4b950ab --- /dev/null +++ b/source/core/StarNetCompatibility.hpp @@ -0,0 +1,55 @@ +#pragma once +#include "StarVersion.hpp" +#include "StarHash.hpp" + +namespace Star { + +extern VersionNumber const OpenProtocolVersion; + +constexpr VersionNumber AnyVersion = 0xFFFFFFFF; +constexpr VersionNumber LegacyVersion = 0; + +class NetCompatibilityRules { +public: + NetCompatibilityRules(); + NetCompatibilityRules(uint64_t) = delete; + NetCompatibilityRules(VersionNumber version); + + VersionNumber version() const; + void setVersion(VersionNumber version); + bool isLegacy() const; + + bool operator==(NetCompatibilityRules const& a) const; + +private: + VersionNumber m_version = OpenProtocolVersion; +}; + +inline NetCompatibilityRules::NetCompatibilityRules() : m_version(OpenProtocolVersion) {} + +inline NetCompatibilityRules::NetCompatibilityRules(VersionNumber v) : m_version(v) {} + +inline VersionNumber NetCompatibilityRules::version() const { + return m_version; +} + +inline void NetCompatibilityRules::setVersion(VersionNumber version) { + m_version = version; +} + +inline bool NetCompatibilityRules::isLegacy() const { + return m_version == LegacyVersion; +} + +inline bool NetCompatibilityRules::operator==(NetCompatibilityRules const& a) const { + return m_version == a.m_version; +} + +template <> +struct hash<NetCompatibilityRules> { + size_t operator()(NetCompatibilityRules const& s) const { + return s.version(); + } +}; + +}
\ No newline at end of file diff --git a/source/core/StarNetElement.cpp b/source/core/StarNetElement.cpp index 914badd..343d45a 100644 --- a/source/core/StarNetElement.cpp +++ b/source/core/StarNetElement.cpp @@ -7,8 +7,8 @@ uint64_t NetElementVersion::current() const { return m_version; } -void NetElementVersion::increment() { - ++m_version; +uint64_t NetElementVersion::increment() { + return ++m_version; } void NetElement::enableNetInterpolation(float) {} diff --git a/source/core/StarNetElement.hpp b/source/core/StarNetElement.hpp index dd909b8..7d73b85 100644 --- a/source/core/StarNetElement.hpp +++ b/source/core/StarNetElement.hpp @@ -9,7 +9,7 @@ namespace Star { class NetElementVersion { public: uint64_t current() const; - void increment(); + uint64_t increment(); private: uint64_t m_version = 0; @@ -20,15 +20,15 @@ class NetElement { public: virtual ~NetElement() = default; - // A network of NetElements will have a shared monotinically increasing + // A network of NetElements will have a shared monotonically increasing // NetElementVersion. When elements are updated, they will mark the version // number at the time they are updated so that a delta can be constructed // that contains only changes since any past version. virtual void initNetVersion(NetElementVersion const* version = nullptr) = 0; // Full store / load of the entire element. - virtual void netStore(DataStream& ds) const = 0; - virtual void netLoad(DataStream& ds) = 0; + virtual void netStore(DataStream& ds, NetCompatibilityRules rules) const = 0; + virtual void netLoad(DataStream& ds, NetCompatibilityRules rules) = 0; // Enables interpolation mode. If interpolation mode is enabled, then // NetElements will delay presenting incoming delta data for the @@ -46,14 +46,34 @@ public: // the version at the time of the *last* call to writeDelta, + 1. If // fromVersion is 0, this will always write the full state. Should return // true if a delta was needed and was written to DataStream, false otherwise. - virtual bool writeNetDelta(DataStream& ds, uint64_t fromVersion) const = 0; + virtual bool writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const = 0; // Read a delta written by writeNetDelta. 'interpolationTime' is the time in // the future that data from this delta should be delayed and smoothed into, // if interpolation is enabled. - virtual void readNetDelta(DataStream& ds, float interpolationTime = 0.0) = 0; + virtual void readNetDelta(DataStream& ds, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) = 0; // When extrapolating, it is important to notify when a delta WOULD have been // received even if no deltas are produced, so no extrapolation takes place. virtual void blankNetDelta(float interpolationTime); + + VersionNumber compatibilityVersion() const; + void setCompatibilityVersion(VersionNumber version); + bool checkWithRules(NetCompatibilityRules const& rules) const; +private: + VersionNumber m_netCompatibilityVersion = AnyVersion; }; +inline VersionNumber NetElement::compatibilityVersion() const { + return m_netCompatibilityVersion; +} + +inline void NetElement::setCompatibilityVersion(VersionNumber version) { + m_netCompatibilityVersion = version; +} + +inline bool NetElement::checkWithRules(NetCompatibilityRules const& rules) const { + if (m_netCompatibilityVersion != AnyVersion) + return rules.version() >= m_netCompatibilityVersion; + return true; +} + } diff --git a/source/core/StarNetElementBasicFields.cpp b/source/core/StarNetElementBasicFields.cpp index bbb5c5b..618c4a6 100644 --- a/source/core/StarNetElementBasicFields.cpp +++ b/source/core/StarNetElementBasicFields.cpp @@ -49,8 +49,8 @@ void NetElementEvent::setIgnoreOccurrencesOnNetLoad(bool ignoreOccurrencesOnNetL m_ignoreOccurrencesOnNetLoad = ignoreOccurrencesOnNetLoad; } -void NetElementEvent::netLoad(DataStream& ds) { - NetElementUInt::netLoad(ds); +void NetElementEvent::netLoad(DataStream& ds, NetCompatibilityRules rules) { + NetElementUInt::netLoad(ds, rules); if (m_ignoreOccurrencesOnNetLoad) ignoreOccurrences(); } diff --git a/source/core/StarNetElementBasicFields.hpp b/source/core/StarNetElementBasicFields.hpp index 693c6c3..3b46cc0 100644 --- a/source/core/StarNetElementBasicFields.hpp +++ b/source/core/StarNetElementBasicFields.hpp @@ -38,11 +38,11 @@ public: void disableNetInterpolation() override; void tickNetInterpolation(float dt) override; - void netStore(DataStream& ds) const override; - void netLoad(DataStream& ds) override; + void netStore(DataStream& ds, NetCompatibilityRules rules = {}) const override; + void netLoad(DataStream& ds, NetCompatibilityRules rules) override; - bool writeNetDelta(DataStream& ds, uint64_t fromVersion) const override; - void readNetDelta(DataStream& ds, float interpolationTime = 0.0f) override; + bool writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules = {}) const override; + void readNetDelta(DataStream& ds, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override; protected: virtual void readData(DataStream& ds, T& t) const = 0; @@ -107,7 +107,7 @@ public: void ignoreOccurrences(); void setIgnoreOccurrencesOnNetLoad(bool ignoreOccurrencesOnNetLoad); - void netLoad(DataStream& ds) override; + void netLoad(DataStream& ds, NetCompatibilityRules rules) override; protected: void updated() override; @@ -211,7 +211,8 @@ void NetElementBasicField<T>::tickNetInterpolation(float dt) { } template <typename T> -void NetElementBasicField<T>::netStore(DataStream& ds) const { +void NetElementBasicField<T>::netStore(DataStream& ds, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return; if (m_pendingInterpolatedValues && !m_pendingInterpolatedValues->empty()) writeData(ds, m_pendingInterpolatedValues->last().second); else @@ -219,7 +220,8 @@ void NetElementBasicField<T>::netStore(DataStream& ds) const { } template <typename T> -void NetElementBasicField<T>::netLoad(DataStream& ds) { +void NetElementBasicField<T>::netLoad(DataStream& ds, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; readData(ds, m_value); m_latestUpdateVersion = m_netVersion ? m_netVersion->current() : 0; updated(); @@ -228,7 +230,8 @@ void NetElementBasicField<T>::netLoad(DataStream& ds) { } template <typename T> -bool NetElementBasicField<T>::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { +bool NetElementBasicField<T>::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return false; if (m_latestUpdateVersion < fromVersion) return false; @@ -236,11 +239,13 @@ bool NetElementBasicField<T>::writeNetDelta(DataStream& ds, uint64_t fromVersion writeData(ds, m_pendingInterpolatedValues->last().second); else writeData(ds, m_value); + return true; } template <typename T> -void NetElementBasicField<T>::readNetDelta(DataStream& ds, float interpolationTime) { +void NetElementBasicField<T>::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; T t; readData(ds, t); m_latestUpdateVersion = m_netVersion ? m_netVersion->current() : 0; diff --git a/source/core/StarNetElementContainers.hpp b/source/core/StarNetElementContainers.hpp index 62cce86..2e16b30 100644 --- a/source/core/StarNetElementContainers.hpp +++ b/source/core/StarNetElementContainers.hpp @@ -26,11 +26,12 @@ public: void disableNetInterpolation() override; void tickNetInterpolation(float dt) override; - void netStore(DataStream& ds) const override; - void netLoad(DataStream& ds) override; + void netStore(DataStream& ds, NetCompatibilityRules rules = {}) const override; + void netLoad(DataStream& ds, NetCompatibilityRules rules) override; - bool writeNetDelta(DataStream& ds, uint64_t fromVersion) const override; - void readNetDelta(DataStream& ds, float interpolationTime = 0.0f) override; + bool shouldWriteNetDelta(uint64_t fromVersion, NetCompatibilityRules rules = {}) const; + bool writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules = {}) const override; + void readNetDelta(DataStream& ds, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override; mapped_type const& get(key_type const& key) const; mapped_type const* ptr(key_type const& key) const; @@ -77,6 +78,8 @@ public: template <typename MapType> void setContents(MapType const& values); + uint64_t changeDataLastVersion() const; + 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. @@ -152,7 +155,8 @@ void NetElementMapWrapper<BaseMap>::tickNetInterpolation(float dt) { } template <typename BaseMap> -void NetElementMapWrapper<BaseMap>::netStore(DataStream& ds) const { +void NetElementMapWrapper<BaseMap>::netStore(DataStream& ds, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return; ds.writeVlqU(BaseMap::size() + m_pendingChangeData.size()); for (auto const& pair : *this) writeChange(ds, SetChange{pair.first, pair.second}); @@ -162,7 +166,8 @@ void NetElementMapWrapper<BaseMap>::netStore(DataStream& ds) const { } template <typename BaseMap> -void NetElementMapWrapper<BaseMap>::netLoad(DataStream& ds) { +void NetElementMapWrapper<BaseMap>::netLoad(DataStream& ds, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; m_changeData.clear(); m_changeDataLastVersion = m_netVersion ? m_netVersion->current() : 0; m_pendingChangeData.clear(); @@ -181,13 +186,27 @@ void NetElementMapWrapper<BaseMap>::netLoad(DataStream& ds) { } template <typename BaseMap> -bool NetElementMapWrapper<BaseMap>::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { +bool NetElementMapWrapper<BaseMap>::shouldWriteNetDelta(uint64_t fromVersion, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return false; + if (fromVersion < m_changeDataLastVersion) + return true; + + for (auto const& p : m_changeData) + if (p.first >= fromVersion) + return true; + + return false; +} + +template <typename BaseMap> +bool NetElementMapWrapper<BaseMap>::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return false; bool deltaWritten = false; if (fromVersion < m_changeDataLastVersion) { deltaWritten = true; ds.writeVlqU(1); - netStore(ds); + netStore(ds, rules); } else { for (auto const& p : m_changeData) { @@ -206,13 +225,14 @@ bool NetElementMapWrapper<BaseMap>::writeNetDelta(DataStream& ds, uint64_t fromV } template <typename BaseMap> -void NetElementMapWrapper<BaseMap>::readNetDelta(DataStream& ds, float interpolationTime) { +void NetElementMapWrapper<BaseMap>::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; while (true) { uint64_t code = ds.readVlqU(); if (code == 0) { break; } else if (code == 1) { - netLoad(ds); + netLoad(ds, rules); } else if (code == 2) { auto change = readChange(ds); addChangeData(change); @@ -382,6 +402,11 @@ void NetElementMapWrapper<BaseMap>::setContents(MapType const& values) { } template <typename BaseMap> +uint64_t NetElementMapWrapper<BaseMap>::changeDataLastVersion() const { + return m_changeDataLastVersion; +} + +template <typename BaseMap> void NetElementMapWrapper<BaseMap>::writeChange(DataStream& ds, ElementChange const& change) { if (auto sc = change.template ptr<SetChange>()) { ds.write<uint8_t>(0); diff --git a/source/core/StarNetElementDynamicGroup.hpp b/source/core/StarNetElementDynamicGroup.hpp index 4f3a2d3..dbd0c12 100644 --- a/source/core/StarNetElementDynamicGroup.hpp +++ b/source/core/StarNetElementDynamicGroup.hpp @@ -45,11 +45,11 @@ public: void disableNetInterpolation() override; void tickNetInterpolation(float dt) override; - void netStore(DataStream& ds) const override; - void netLoad(DataStream& ds) override; + void netStore(DataStream& ds, NetCompatibilityRules rules = {}) const override; + void netLoad(DataStream& ds, NetCompatibilityRules rules) override; - bool writeNetDelta(DataStream& ds, uint64_t fromVersion) const override; - void readNetDelta(DataStream& ds, float interpolationTime = 0.0f) override; + bool writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules = {}) const override; + void readNetDelta(DataStream& ds, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override; void blankNetDelta(float interpolationTime = 0.0f) override; private: @@ -89,7 +89,7 @@ template <typename Element> auto NetElementDynamicGroup<Element>::addNetElement(ElementPtr element) -> ElementId { readyElement(element); DataStreamBuffer storeBuffer; - element->netStore(storeBuffer); + element->netStore(storeBuffer, {}); auto id = m_idMap.add(std::move(element)); addChangeData(ElementAddition(id, storeBuffer.takeData())); @@ -134,7 +134,7 @@ void NetElementDynamicGroup<Element>::initNetVersion(NetElementVersion const* ve for (auto& pair : m_idMap) { pair.second->initNetVersion(m_netVersion); DataStreamBuffer storeBuffer; - pair.second->netStore(storeBuffer); + pair.second->netStore(storeBuffer, {}); addChangeData(ElementAddition(pair.first, storeBuffer.takeData())); } } @@ -162,19 +162,22 @@ void NetElementDynamicGroup<Element>::tickNetInterpolation(float dt) { } template <typename Element> -void NetElementDynamicGroup<Element>::netStore(DataStream& ds) const { +void NetElementDynamicGroup<Element>::netStore(DataStream& ds, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return; ds.writeVlqU(m_idMap.size()); + m_buffer.setStreamCompatibilityVersion(rules); for (auto& pair : m_idMap) { ds.writeVlqU(pair.first); - pair.second->netStore(m_buffer); + pair.second->netStore(m_buffer, rules); ds.write(m_buffer.data()); m_buffer.clear(); } } template <typename Element> -void NetElementDynamicGroup<Element>::netLoad(DataStream& ds) { +void NetElementDynamicGroup<Element>::netLoad(DataStream& ds, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; m_changeData.clear(); m_changeDataLastVersion = m_netVersion ? m_netVersion->current() : 0; m_idMap.clear(); @@ -188,7 +191,7 @@ void NetElementDynamicGroup<Element>::netLoad(DataStream& ds) { DataStreamBuffer storeBuffer(ds.read<ByteArray>()); ElementPtr element = make_shared<Element>(); - element->netLoad(storeBuffer); + element->netLoad(storeBuffer, rules); readyElement(element); m_idMap.add(id, std::move(element)); @@ -197,10 +200,11 @@ void NetElementDynamicGroup<Element>::netLoad(DataStream& ds) { } template <typename Element> -bool NetElementDynamicGroup<Element>::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { +bool NetElementDynamicGroup<Element>::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return false; if (fromVersion < m_changeDataLastVersion) { ds.write<bool>(true); - netStore(ds); + netStore(ds, rules); return true; } else { @@ -220,8 +224,9 @@ bool NetElementDynamicGroup<Element>::writeNetDelta(DataStream& ds, uint64_t fro } } + m_buffer.setStreamCompatibilityVersion(rules); for (auto& p : m_idMap) { - if (p.second->writeNetDelta(m_buffer, fromVersion)) { + if (p.second->writeNetDelta(m_buffer, fromVersion, rules)) { willWrite(); ds.writeVlqU(p.first + 1); ds.writeBytes(m_buffer.data()); @@ -237,10 +242,11 @@ bool NetElementDynamicGroup<Element>::writeNetDelta(DataStream& ds, uint64_t fro } template <typename Element> -void NetElementDynamicGroup<Element>::readNetDelta(DataStream& ds, float interpolationTime) { +void NetElementDynamicGroup<Element>::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; bool isFull = ds.read<bool>(); if (isFull) { - netLoad(ds); + netLoad(ds, rules); } else { while (true) { uint64_t code = ds.readVlqU(); @@ -256,7 +262,7 @@ void NetElementDynamicGroup<Element>::readNetDelta(DataStream& ds, float interpo } else if (auto addition = changeUpdate.template ptr<ElementAddition>()) { ElementPtr element = make_shared<Element>(); DataStreamBuffer storeBuffer(std::move(get<1>(*addition))); - element->netLoad(storeBuffer); + element->netLoad(storeBuffer, rules); readyElement(element); m_idMap.add(get<0>(*addition), std::move(element)); } else if (auto removal = changeUpdate.template ptr<ElementRemoval>()) { @@ -265,7 +271,7 @@ void NetElementDynamicGroup<Element>::readNetDelta(DataStream& ds, float interpo } else { ElementId elementId = code - 1; auto const& element = m_idMap.get(elementId); - element->readNetDelta(ds, interpolationTime); + element->readNetDelta(ds, interpolationTime, rules); if (m_interpolationEnabled) m_receivedDeltaIds.add(elementId); } diff --git a/source/core/StarNetElementExt.hpp b/source/core/StarNetElementExt.hpp new file mode 100644 index 0000000..50802fe --- /dev/null +++ b/source/core/StarNetElementExt.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include "StarNetElement.hpp" + +namespace Star { + +template <typename BaseNetElement> +class NetElementOverride : public BaseNetElement { +public: + void netStore(DataStream& ds, NetCompatibilityRules rules = {}) const override; + void netLoad(DataStream& ds, NetCompatibilityRules rules) override; + + bool writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules = {}) const override; + void readNetDelta(DataStream& ds, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override; + + typedef std::function<void(DataStream&, NetCompatibilityRules)> NetStorer; + typedef std::function<void(DataStream&, NetCompatibilityRules)> NetLoader; + typedef std::function<bool(DataStream&, uint64_t, NetCompatibilityRules)> NetDeltaWriter; + typedef std::function<void(DataStream&, float, NetCompatibilityRules)> NetDeltaReader; + + void setNetStorer(NetStorer); + void setNetLoader(NetLoader); + void setNetDeltaWriter(NetDeltaWriter); + void setNetDeltaReader(NetDeltaReader); + void setOverrides(NetStorer netStorer, NetLoader netLoader, + NetDeltaWriter netDeltaWriter, NetDeltaReader netDeltaReader); + +private: + NetStorer m_netStorer; + NetLoader m_netLoader; + + NetDeltaWriter m_netDeltaWriter; + NetDeltaReader m_netDeltaReader; +}; + +template <typename BaseNetElement> +void NetElementOverride<BaseNetElement>::netStore(DataStream& ds, NetCompatibilityRules rules) const { + if (m_netStorer) + m_netStorer(ds, rules); + else + BaseNetElement::netStore(ds, rules); +} + +template <typename BaseNetElement> +void NetElementOverride<BaseNetElement>::netLoad(DataStream& ds, NetCompatibilityRules rules) { + if (m_netLoader) + m_netLoader(ds, rules); + else + BaseNetElement::netLoad(ds, rules); +} + +template <typename BaseNetElement> +bool NetElementOverride<BaseNetElement>::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const { + if (m_netDeltaWriter) + return m_netDeltaWriter(ds, fromVersion, rules); + else + return BaseNetElement::writeNetDelta(ds, fromVersion, rules); +} + +template <typename BaseNetElement> +void NetElementOverride<BaseNetElement>::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) { + if (m_netDeltaReader) + m_netDeltaReader(ds, interpolationTime, rules); + else + BaseNetElement::readNetDelta(ds, interpolationTime, rules); +} + +template <typename BaseNetElement> +inline void NetElementOverride<BaseNetElement>::setNetStorer(NetStorer f) { m_netStorer = std::move(f); } +template <typename BaseNetElement> +inline void NetElementOverride<BaseNetElement>::setNetLoader(NetLoader f) { m_netLoader = std::move(f); } +template <typename BaseNetElement> +inline void NetElementOverride<BaseNetElement>::setNetDeltaWriter(NetDeltaWriter f) { m_netDeltaWriter = std::move(f); } +template <typename BaseNetElement> +inline void NetElementOverride<BaseNetElement>::setNetDeltaReader(NetDeltaReader f) { m_netDeltaReader = std::move(f); } + +template <typename BaseNetElement> +inline void NetElementOverride<BaseNetElement>::setOverrides(NetStorer netStorer, NetLoader netLoader, NetDeltaWriter netDeltaWriter, NetDeltaReader netDeltaReader) { + m_netStorer = std::move(netStorer); + m_netLoader = std::move(netLoader); + m_netDeltaWriter = std::move(netDeltaWriter); + m_netDeltaReader = std::move(netDeltaReader); +} + +}
\ No newline at end of file diff --git a/source/core/StarNetElementFloatFields.hpp b/source/core/StarNetElementFloatFields.hpp index 57f74a9..6c8fbcf 100644 --- a/source/core/StarNetElementFloatFields.hpp +++ b/source/core/StarNetElementFloatFields.hpp @@ -36,11 +36,11 @@ public: void disableNetInterpolation() override; void tickNetInterpolation(float dt) override; - void netStore(DataStream& ds) const override; - void netLoad(DataStream& ds) override; + void netStore(DataStream& ds, NetCompatibilityRules rules = {}) const override; + void netLoad(DataStream& ds, NetCompatibilityRules rules) override; - bool writeNetDelta(DataStream& ds, uint64_t fromVersion) const override; - void readNetDelta(DataStream& ds, float interpolationTime = 0.0f) override; + bool writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules = {}) const override; + void readNetDelta(DataStream& ds, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override; void blankNetDelta(float interpolationTime = 0.0f) override; private: @@ -131,7 +131,8 @@ void NetElementFloating<T>::tickNetInterpolation(float dt) { } template <typename T> -void NetElementFloating<T>::netStore(DataStream& ds) const { +void NetElementFloating<T>::netStore(DataStream& ds, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return; if (m_interpolationDataPoints) writeValue(ds, m_interpolationDataPoints->last().second); else @@ -139,7 +140,8 @@ void NetElementFloating<T>::netStore(DataStream& ds) const { } template <typename T> -void NetElementFloating<T>::netLoad(DataStream& ds) { +void NetElementFloating<T>::netLoad(DataStream& ds, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; m_value = readValue(ds); m_latestUpdateVersion = m_netVersion ? m_netVersion->current() : 0; if (m_interpolationDataPoints) { @@ -149,7 +151,8 @@ void NetElementFloating<T>::netLoad(DataStream& ds) { } template <typename T> -bool NetElementFloating<T>::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { +bool NetElementFloating<T>::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return false; if (m_latestUpdateVersion < fromVersion) return false; @@ -162,7 +165,7 @@ bool NetElementFloating<T>::writeNetDelta(DataStream& ds, uint64_t fromVersion) } template <typename T> -void NetElementFloating<T>::readNetDelta(DataStream& ds, float interpolationTime) { +void NetElementFloating<T>::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) { T t = readValue(ds); m_latestUpdateVersion = m_netVersion ? m_netVersion->current() : 0; diff --git a/source/core/StarNetElementGroup.cpp b/source/core/StarNetElementGroup.cpp index 249c978..6626e2b 100644 --- a/source/core/StarNetElementGroup.cpp +++ b/source/core/StarNetElementGroup.cpp @@ -21,14 +21,18 @@ void NetElementGroup::initNetVersion(NetElementVersion const* version) { p.first->initNetVersion(m_version); } -void NetElementGroup::netStore(DataStream& ds) const { +void NetElementGroup::netStore(DataStream& ds, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return; for (auto& p : m_elements) - p.first->netStore(ds); + if (p.first->checkWithRules(rules)) + p.first->netStore(ds, rules); } -void NetElementGroup::netLoad(DataStream& ds) { +void NetElementGroup::netLoad(DataStream& ds, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; for (auto& p : m_elements) - p.first->netLoad(ds); + if (p.first->checkWithRules(rules)) + p.first->netLoad(ds, rules); } void NetElementGroup::enableNetInterpolation(float extrapolationHint) { @@ -56,17 +60,23 @@ void NetElementGroup::tickNetInterpolation(float dt) { } } -bool NetElementGroup::writeNetDelta(DataStream& ds, uint64_t fromStep) const { +bool NetElementGroup::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return false; if (m_elements.size() == 0) { return false; } else if (m_elements.size() == 1) { - return m_elements[0].first->writeNetDelta(ds, fromStep); + return m_elements[0].first->writeNetDelta(ds, fromVersion, rules); } else { bool deltaWritten = false; - for (uint64_t i = 0; i < m_elements.size(); ++i) { - if (m_elements[i].first->writeNetDelta(m_buffer, fromStep)) { + uint64_t i = 0; + m_buffer.setStreamCompatibilityVersion(rules); + for (auto& element : m_elements) { + if (!element.first->checkWithRules(rules)) + continue; + ++i; + if (element.first->writeNetDelta(m_buffer, fromVersion, rules)) { deltaWritten = true; - ds.writeVlqU(i + 1); + ds.writeVlqU(i); ds.writeBytes(m_buffer.data()); m_buffer.clear(); } @@ -77,23 +87,28 @@ bool NetElementGroup::writeNetDelta(DataStream& ds, uint64_t fromStep) const { } } -void NetElementGroup::readNetDelta(DataStream& ds, float interpolationTime) { +void NetElementGroup::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; if (m_elements.size() == 0) { throw IOException("readNetDelta called on empty NetElementGroup"); } else if (m_elements.size() == 1) { - m_elements[0].first->readNetDelta(ds, interpolationTime); + m_elements[0].first->readNetDelta(ds, interpolationTime, rules); } else { uint64_t readIndex = ds.readVlqU(); - for (uint64_t i = 0; i < m_elements.size(); ++i) { + uint64_t i = 0; + for (auto& element : m_elements) { + if (!element.first->checkWithRules(rules)) + continue; if (readIndex == 0 || readIndex - 1 > i) { if (m_interpolationEnabled) m_elements[i].first->blankNetDelta(interpolationTime); } else if (readIndex - 1 == i) { - m_elements[i].first->readNetDelta(ds, interpolationTime); + m_elements[i].first->readNetDelta(ds, interpolationTime, rules); readIndex = ds.readVlqU(); } else { throw IOException("group indexes out of order in NetElementGroup::readNetDelta"); } + ++i; } } } diff --git a/source/core/StarNetElementGroup.hpp b/source/core/StarNetElementGroup.hpp index fd1bf58..1904a33 100644 --- a/source/core/StarNetElementGroup.hpp +++ b/source/core/StarNetElementGroup.hpp @@ -24,15 +24,15 @@ public: void initNetVersion(NetElementVersion const* version = nullptr) override; - void netStore(DataStream& ds) const override; - void netLoad(DataStream& ds) override; + void netStore(DataStream& ds, NetCompatibilityRules rules = {}) const override; + void netLoad(DataStream& ds, NetCompatibilityRules rules) 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.0f) override; + bool writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules = {}) const override; + void readNetDelta(DataStream& ds, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override; void blankNetDelta(float interpolationTime) override; NetElementVersion const* netVersion() const; diff --git a/source/core/StarNetElementSignal.hpp b/source/core/StarNetElementSignal.hpp index 9faa127..61a108d 100644 --- a/source/core/StarNetElementSignal.hpp +++ b/source/core/StarNetElementSignal.hpp @@ -16,15 +16,15 @@ public: void initNetVersion(NetElementVersion const* version = nullptr) override; - void netStore(DataStream& ds) const override; - void netLoad(DataStream& ds) override; + void netStore(DataStream& ds, NetCompatibilityRules rules = {}) const override; + void netLoad(DataStream& ds, NetCompatibilityRules rules) 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; + bool writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules = {}) const override; + void readNetDelta(DataStream& ds, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override; void send(Signal signal); List<Signal> receive(); @@ -55,10 +55,10 @@ void NetElementSignal<Signal>::initNetVersion(NetElementVersion const* version) } template <typename Signal> -void NetElementSignal<Signal>::netStore(DataStream&) const {} +void NetElementSignal<Signal>::netStore(DataStream&, NetCompatibilityRules) const {} template <typename Signal> -void NetElementSignal<Signal>::netLoad(DataStream&) { +void NetElementSignal<Signal>::netLoad(DataStream&, NetCompatibilityRules) { } template <typename Signal> @@ -83,7 +83,8 @@ void NetElementSignal<Signal>::tickNetInterpolation(float dt) { } template <typename Signal> -bool NetElementSignal<Signal>::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { +bool NetElementSignal<Signal>::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return false; size_t numToWrite = 0; for (auto const& p : m_signals) { if (p.version >= fromVersion) @@ -103,7 +104,8 @@ bool NetElementSignal<Signal>::writeNetDelta(DataStream& ds, uint64_t fromVersio } template <typename Signal> -void NetElementSignal<Signal>::readNetDelta(DataStream& ds, float interpolationTime) { +void NetElementSignal<Signal>::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; size_t numToRead = ds.readVlqU(); for (size_t i = 0; i < numToRead; ++i) { Signal s; diff --git a/source/core/StarNetElementSyncGroup.cpp b/source/core/StarNetElementSyncGroup.cpp index ac8da92..cd98638 100644 --- a/source/core/StarNetElementSyncGroup.cpp +++ b/source/core/StarNetElementSyncGroup.cpp @@ -26,23 +26,27 @@ void NetElementSyncGroup::tickNetInterpolation(float dt) { } } -void NetElementSyncGroup::netStore(DataStream& ds) const { +void NetElementSyncGroup::netStore(DataStream& ds, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return; const_cast<NetElementSyncGroup*>(this)->netElementsNeedStore(); - return NetElementGroup::netStore(ds); + return NetElementGroup::netStore(ds, rules); } -void NetElementSyncGroup::netLoad(DataStream& ds) { - NetElementGroup::netLoad(ds); +void NetElementSyncGroup::netLoad(DataStream& ds, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; + NetElementGroup::netLoad(ds, rules); netElementsNeedLoad(true); } -bool NetElementSyncGroup::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { +bool NetElementSyncGroup::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return false; const_cast<NetElementSyncGroup*>(this)->netElementsNeedStore(); - return NetElementGroup::writeNetDelta(ds, fromVersion); + return NetElementGroup::writeNetDelta(ds, fromVersion, rules); } -void NetElementSyncGroup::readNetDelta(DataStream& ds, float interpolationTime) { - NetElementGroup::readNetDelta(ds, interpolationTime); +void NetElementSyncGroup::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; + NetElementGroup::readNetDelta(ds, interpolationTime, rules); m_hasRecentChanges = true; m_recentDeltaTime = interpolationTime; diff --git a/source/core/StarNetElementSyncGroup.hpp b/source/core/StarNetElementSyncGroup.hpp index b01200b..30dafe8 100644 --- a/source/core/StarNetElementSyncGroup.hpp +++ b/source/core/StarNetElementSyncGroup.hpp @@ -13,11 +13,11 @@ public: void disableNetInterpolation() override; void tickNetInterpolation(float dt) override; - void netStore(DataStream& ds) const override; - void netLoad(DataStream& ds) override; + void netStore(DataStream& ds, NetCompatibilityRules rules = {}) const override; + void netLoad(DataStream& ds, NetCompatibilityRules rules) override; - bool writeNetDelta(DataStream& ds, uint64_t fromStep) const override; - void readNetDelta(DataStream& ds, float interpolationTime = 0.0f) override; + bool writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules = {}) const override; + void readNetDelta(DataStream& ds, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override; void blankNetDelta(float interpolationTime = 0.0f) override; protected: diff --git a/source/core/StarNetElementTop.hpp b/source/core/StarNetElementTop.hpp index 454b34f..cf3cfb4 100644 --- a/source/core/StarNetElementTop.hpp +++ b/source/core/StarNetElementTop.hpp @@ -1,5 +1,4 @@ -#ifndef STAR_NET_ELEMENT_TOP_HPP -#define STAR_NET_ELEMENT_TOP_HPP +#pragma once #include "StarNetElement.hpp" @@ -12,10 +11,11 @@ class NetElementTop : public BaseNetElement { public: NetElementTop(); - // Returns the state update, combined with the version code that should be - // passed to the next call to writeState. If 'fromVersion' is 0, then this - // is a full write for an initial read of a slave NetElementTop. - pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0); + // Writes the state update to the given DataStream then returns the version + // code that should be passed to the next call to writeState. If + // 'fromVersion' is 0, then this is a full write for an initial read of a + // slave NetElementTop. + pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {}); // Reads a state produced by a call to writeState, optionally with the // interpolation delay time for the data contained in this state update. If // the state is a full update rather than a delta, the interoplation delay @@ -23,7 +23,7 @@ public: // readState, *unless* extrapolation is enabled. If extrapolation is // enabled, reading a blank update calls 'blankNetDelta' which is necessary // to not improperly extrapolate past the end of incoming deltas. - void readNetState(ByteArray data, float interpolationTime = 0.0); + void readNetState(ByteArray data, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}); private: using BaseNetElement::initNetVersion; @@ -32,6 +32,7 @@ private: using BaseNetElement::writeNetDelta; using BaseNetElement::readNetDelta; using BaseNetElement::blankNetDelta; + using BaseNetElement::checkWithRules; NetElementVersion m_netVersion; }; @@ -42,41 +43,34 @@ NetElementTop<BaseNetElement>::NetElementTop() { } template <typename BaseNetElement> -pair<ByteArray, uint64_t> NetElementTop<BaseNetElement>::writeNetState(uint64_t fromVersion) { +pair<ByteArray, uint64_t> NetElementTop<BaseNetElement>::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) { + DataStreamBuffer ds; + ds.setStreamCompatibilityVersion(rules); if (fromVersion == 0) { - DataStreamBuffer ds; ds.write<bool>(true); - BaseNetElement::netStore(ds); - m_netVersion.increment(); - return {ds.takeData(), m_netVersion.current()}; - + BaseNetElement::netStore(ds, rules); + return {ds.takeData(), m_netVersion.increment()}; } else { - DataStreamBuffer ds; ds.write<bool>(false); - if (!BaseNetElement::writeNetDelta(ds, fromVersion)) { + if (!BaseNetElement::writeNetDelta(ds, fromVersion, rules)) return {ByteArray(), m_netVersion.current()}; - } else { - m_netVersion.increment(); - return {ds.takeData(), m_netVersion.current()}; - } + else + return {ds.takeData(), m_netVersion.increment()}; } } template <typename BaseNetElement> -void NetElementTop<BaseNetElement>::readNetState(ByteArray data, float interpolationTime) { +void NetElementTop<BaseNetElement>::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) { if (data.empty()) { BaseNetElement::blankNetDelta(interpolationTime); - } else { DataStreamBuffer ds(std::move(data)); - + ds.setStreamCompatibilityVersion(rules); if (ds.read<bool>()) - BaseNetElement::netLoad(ds); + BaseNetElement::netLoad(ds, rules); else - BaseNetElement::readNetDelta(ds, interpolationTime); + BaseNetElement::readNetDelta(ds, interpolationTime, rules); } } -} - -#endif +}
\ No newline at end of file diff --git a/source/core/StarVersion.cpp.in b/source/core/StarVersion.cpp.in new file mode 100644 index 0000000..0529c19 --- /dev/null +++ b/source/core/StarVersion.cpp.in @@ -0,0 +1,9 @@ +#include "StarVersion.hpp" + +namespace Star { + +char const* const StarVersionString = "1.4.4"; +char const* const StarSourceIdentifierString = "${STAR_SOURCE_IDENTIFIER}"; +char const* const StarArchitectureString = "${STAR_SYSTEM} ${STAR_ARCHITECTURE}"; + +} diff --git a/source/core/StarVersion.hpp b/source/core/StarVersion.hpp new file mode 100644 index 0000000..ad65875 --- /dev/null +++ b/source/core/StarVersion.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "StarConfig.hpp" + +namespace Star { + +extern char const* const StarVersionString; +extern char const* const StarSourceIdentifierString; +extern char const* const StarArchitectureString; + +typedef uint32_t VersionNumber; + +} |