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

summaryrefslogtreecommitdiff
path: root/source/core
diff options
context:
space:
mode:
Diffstat (limited to 'source/core')
-rw-r--r--source/core/CMakeLists.txt7
-rw-r--r--source/core/StarBTreeDatabase.cpp218
-rw-r--r--source/core/StarBTreeDatabase.hpp13
-rw-r--r--source/core/StarDataStream.cpp4
-rw-r--r--source/core/StarDataStream.hpp5
-rw-r--r--source/core/StarDataStreamDevices.hpp4
-rw-r--r--source/core/StarFont.cpp21
-rw-r--r--source/core/StarImageProcessing.cpp2
-rw-r--r--source/core/StarNetCompatibility.cpp7
-rw-r--r--source/core/StarNetCompatibility.hpp55
-rw-r--r--source/core/StarNetElement.cpp4
-rw-r--r--source/core/StarNetElement.hpp32
-rw-r--r--source/core/StarNetElementBasicFields.cpp4
-rw-r--r--source/core/StarNetElementBasicFields.hpp23
-rw-r--r--source/core/StarNetElementContainers.hpp45
-rw-r--r--source/core/StarNetElementDynamicGroup.hpp40
-rw-r--r--source/core/StarNetElementExt.hpp85
-rw-r--r--source/core/StarNetElementFloatFields.hpp19
-rw-r--r--source/core/StarNetElementGroup.cpp41
-rw-r--r--source/core/StarNetElementGroup.hpp8
-rw-r--r--source/core/StarNetElementSignal.hpp18
-rw-r--r--source/core/StarNetElementSyncGroup.cpp20
-rw-r--r--source/core/StarNetElementSyncGroup.hpp8
-rw-r--r--source/core/StarNetElementTop.hpp48
-rw-r--r--source/core/StarVersion.cpp.in9
-rw-r--r--source/core/StarVersion.hpp13
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;
+
+}