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

summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/base/CMakeLists.txt4
-rw-r--r--source/base/StarAssets.cpp2
-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.cpp20
-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.in (renamed from source/base/StarVersion.cpp.in)0
-rw-r--r--source/core/StarVersion.hpp (renamed from source/base/StarVersion.hpp)0
-rw-r--r--source/frontend/StarTeamBar.hpp7
-rw-r--r--source/game/StarClientContext.cpp15
-rw-r--r--source/game/StarClientContext.hpp8
-rw-r--r--source/game/StarEntityFactory.cpp44
-rw-r--r--source/game/StarEntityFactory.hpp4
-rw-r--r--source/game/StarHumanoid.cpp86
-rw-r--r--source/game/StarHumanoid.hpp2
-rw-r--r--source/game/StarItemDrop.cpp15
-rw-r--r--source/game/StarItemDrop.hpp8
-rw-r--r--source/game/StarMonster.cpp12
-rw-r--r--source/game/StarMonster.hpp6
-rw-r--r--source/game/StarMonsterDatabase.cpp10
-rw-r--r--source/game/StarMonsterDatabase.hpp6
-rw-r--r--source/game/StarNetPackets.hpp1
-rw-r--r--source/game/StarNpc.cpp14
-rw-r--r--source/game/StarNpc.hpp7
-rw-r--r--source/game/StarNpcDatabase.cpp10
-rw-r--r--source/game/StarNpcDatabase.hpp8
-rw-r--r--source/game/StarObject.cpp12
-rw-r--r--source/game/StarObject.hpp6
-rw-r--r--source/game/StarObjectDatabase.cpp3
-rw-r--r--source/game/StarObjectDatabase.hpp2
-rw-r--r--source/game/StarPlant.cpp18
-rw-r--r--source/game/StarPlant.hpp8
-rw-r--r--source/game/StarPlantDrop.cpp13
-rw-r--r--source/game/StarPlantDrop.hpp8
-rw-r--r--source/game/StarPlayer.cpp14
-rw-r--r--source/game/StarPlayer.hpp8
-rw-r--r--source/game/StarPlayerFactory.cpp4
-rw-r--r--source/game/StarPlayerFactory.hpp2
-rw-r--r--source/game/StarProjectile.cpp13
-rw-r--r--source/game/StarProjectile.hpp8
-rw-r--r--source/game/StarProjectileDatabase.cpp5
-rw-r--r--source/game/StarProjectileDatabase.hpp2
-rw-r--r--source/game/StarServerClientContext.cpp9
-rw-r--r--source/game/StarServerClientContext.hpp4
-rw-r--r--source/game/StarSky.cpp8
-rw-r--r--source/game/StarSky.hpp4
-rw-r--r--source/game/StarStagehand.cpp13
-rw-r--r--source/game/StarStagehand.hpp8
-rw-r--r--source/game/StarStatusController.cpp77
-rw-r--r--source/game/StarStatusController.hpp19
-rw-r--r--source/game/StarSystemWorld.cpp16
-rw-r--r--source/game/StarSystemWorld.hpp8
-rw-r--r--source/game/StarSystemWorldServer.cpp4
-rw-r--r--source/game/StarTechController.cpp18
-rw-r--r--source/game/StarTechController.hpp8
-rw-r--r--source/game/StarToolUser.cpp32
-rw-r--r--source/game/StarToolUser.hpp8
-rw-r--r--source/game/StarUniverseClient.cpp36
-rw-r--r--source/game/StarUniverseClient.hpp1
-rw-r--r--source/game/StarUniverseServer.cpp13
-rw-r--r--source/game/StarVehicle.cpp8
-rw-r--r--source/game/StarVehicle.hpp4
-rw-r--r--source/game/StarVehicleDatabase.cpp9
-rw-r--r--source/game/StarVehicleDatabase.hpp4
-rw-r--r--source/game/StarWeather.cpp8
-rw-r--r--source/game/StarWeather.hpp4
-rw-r--r--source/game/StarWorldClient.cpp28
-rw-r--r--source/game/StarWorldClientState.cpp14
-rw-r--r--source/game/StarWorldClientState.hpp7
-rw-r--r--source/game/StarWorldServer.cpp32
-rw-r--r--source/game/StarWorldServer.hpp4
-rw-r--r--source/game/StarWorldServerThread.cpp4
-rw-r--r--source/game/StarWorldServerThread.hpp2
-rw-r--r--source/game/interfaces/StarActivatableItem.hpp7
-rw-r--r--source/game/interfaces/StarEntity.cpp5
-rw-r--r--source/game/interfaces/StarEntity.hpp5
-rw-r--r--source/game/scripting/StarRootLuaBindings.cpp4
-rw-r--r--source/windowing/StarButtonWidget.cpp6
-rw-r--r--source/windowing/StarPaneManager.cpp71
-rw-r--r--source/windowing/StarPaneManager.hpp5
100 files changed, 1041 insertions, 601 deletions
diff --git a/source/base/CMakeLists.txt b/source/base/CMakeLists.txt
index e813e45..e77d9ff 100644
--- a/source/base/CMakeLists.txt
+++ b/source/base/CMakeLists.txt
@@ -18,7 +18,6 @@ SET (star_base_HEADERS
StarMixer.hpp
StarPackedAssetSource.hpp
StarRootBase.hpp
- StarVersion.hpp
StarVersionOptionParser.hpp
StarWorldGeometry.hpp
scripting/StarImageLuaBindings.hpp
@@ -40,8 +39,7 @@ SET (star_base_SOURCES
scripting/StarImageLuaBindings.cpp
)
-CONFIGURE_FILE (StarVersion.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/StarVersion.cpp)
-ADD_LIBRARY (star_base OBJECT ${star_base_SOURCES} ${star_base_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/StarVersion.cpp)
+ADD_LIBRARY (star_base OBJECT ${star_base_SOURCES} ${star_base_HEADERS})
IF(STAR_PRECOMPILED_HEADERS)
TARGET_PRECOMPILE_HEADERS (star_base REUSE_FROM star_core)
diff --git a/source/base/StarAssets.cpp b/source/base/StarAssets.cpp
index a188291..4a10aab 100644
--- a/source/base/StarAssets.cpp
+++ b/source/base/StarAssets.cpp
@@ -68,7 +68,7 @@ static void validatePath(AssetPath const& components, bool canContainSubPath, bo
throw AssetException::format("Path '{}' cannot contain directives", components);
}
-static void validatePath(StringView const& path, bool canContainSubPath, bool canContainDirectives) {
+static void validatePath(StringView path, bool canContainSubPath, bool canContainDirectives) {
std::string_view const& str = path.utf8();
size_t end = str.find_first_of(":?");
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..d854d52 100644
--- a/source/core/StarFont.cpp
+++ b/source/core/StarFont.cpp
@@ -30,8 +30,6 @@ FTContext ftContext;
struct FontImpl {
FT_Face face;
- unsigned loadedPixelSize = 0;
- String::Char loadedChar = 0;
};
FontPtr Font::loadFont(String const& fileName, unsigned pixelSize) {
@@ -97,19 +95,11 @@ tuple<Image, Vec2I, bool> Font::render(String::Char c) {
throw FontException("Font::render called on uninitialized font.");
FT_Face face = m_fontImpl->face;
+ if (FT_Load_Glyph(face, FT_Get_Char_Index(face, c), FontLoadFlags) != 0)
+ return {};
- 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)
- return {};
-
- // convert to an anti-aliased bitmap
- if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL) != 0)
- return {};
- }
-
- m_fontImpl->loadedPixelSize = m_pixelSize;
- m_fontImpl->loadedChar = c;
+ if (FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL) != 0)
+ return {};
FT_GlyphSlot slot = face->glyph;
if (!slot->bitmap.buffer)
@@ -134,7 +124,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/base/StarVersion.cpp.in b/source/core/StarVersion.cpp.in
index 0529c19..0529c19 100644
--- a/source/base/StarVersion.cpp.in
+++ b/source/core/StarVersion.cpp.in
diff --git a/source/base/StarVersion.hpp b/source/core/StarVersion.hpp
index ad65875..ad65875 100644
--- a/source/base/StarVersion.hpp
+++ b/source/core/StarVersion.hpp
diff --git a/source/frontend/StarTeamBar.hpp b/source/frontend/StarTeamBar.hpp
index ca50452..2ca40d6 100644
--- a/source/frontend/StarTeamBar.hpp
+++ b/source/frontend/StarTeamBar.hpp
@@ -1,5 +1,4 @@
-#ifndef STAR_TEAMBAR_HPP
-#define STAR_TEAMBAR_HPP
+#pragma once
#include "StarPane.hpp"
#include "StarUuid.hpp"
@@ -113,6 +112,4 @@ private:
friend class TeamMemberMenu;
};
-}
-
-#endif
+} \ No newline at end of file
diff --git a/source/game/StarClientContext.cpp b/source/game/StarClientContext.cpp
index a91c40e..3061368 100644
--- a/source/game/StarClientContext.cpp
+++ b/source/game/StarClientContext.cpp
@@ -77,11 +77,12 @@ ShipUpgrades ClientContext::shipUpgrades() const {
return m_shipUpgrades.get();
}
-void ClientContext::readUpdate(ByteArray data) {
+void ClientContext::readUpdate(ByteArray data, NetCompatibilityRules rules) {
if (data.empty())
return;
DataStreamBuffer ds(std::move(data));
+ ds.setStreamCompatibilityVersion(rules);
m_rpc->receive(ds.read<ByteArray>());
@@ -89,10 +90,10 @@ void ClientContext::readUpdate(ByteArray data) {
if (!shipUpdates.empty())
m_newShipUpdates.merge(DataStreamBuffer::deserialize<WorldChunks>(std::move(shipUpdates)), true);
- m_netGroup.readNetState(ds.read<ByteArray>());
+ m_netGroup.readNetState(ds.read<ByteArray>(), 0.0f, rules);
}
-ByteArray ClientContext::writeUpdate() {
+ByteArray ClientContext::writeUpdate(NetCompatibilityRules rules) {
return m_rpc->send();
}
@@ -104,4 +105,12 @@ ConnectionId ClientContext::connectionId() const {
return m_connectionId;
}
+void ClientContext::setNetCompatibilityRules(NetCompatibilityRules netCompatibilityRules) {
+ m_netCompatibilityRules = netCompatibilityRules;
+}
+
+NetCompatibilityRules ClientContext::netCompatibilityRules() const {
+ return m_netCompatibilityRules;
+}
+
}
diff --git a/source/game/StarClientContext.hpp b/source/game/StarClientContext.hpp
index 5cdd727..9c08d32 100644
--- a/source/game/StarClientContext.hpp
+++ b/source/game/StarClientContext.hpp
@@ -40,16 +40,20 @@ public:
WorldChunks newShipUpdates();
ShipUpgrades shipUpgrades() const;
- void readUpdate(ByteArray data);
- ByteArray writeUpdate();
+ void readUpdate(ByteArray data, NetCompatibilityRules rules);
+ ByteArray writeUpdate(NetCompatibilityRules rules);
void setConnectionId(ConnectionId connectionId);
ConnectionId connectionId() const;
+ void setNetCompatibilityRules(NetCompatibilityRules netCompatibilityRules);
+ NetCompatibilityRules netCompatibilityRules() const;
+
private:
Uuid m_serverUuid;
Uuid m_playerUuid;
ConnectionId m_connectionId = 0;
+ NetCompatibilityRules m_netCompatibilityRules;
JsonRpcPtr m_rpc;
diff --git a/source/game/StarEntityFactory.cpp b/source/game/StarEntityFactory.cpp
index 333dec1..0202ea7 100644
--- a/source/game/StarEntityFactory.cpp
+++ b/source/game/StarEntityFactory.cpp
@@ -40,57 +40,57 @@ EntityFactory::EntityFactory() {
m_versioningDatabase = root.versioningDatabase();
}
-ByteArray EntityFactory::netStoreEntity(EntityPtr const& entity) const {
+ByteArray EntityFactory::netStoreEntity(EntityPtr const& entity, NetCompatibilityRules rules) const {
RecursiveMutexLocker locker(m_mutex);
if (auto player = as<Player>(entity)) {
- return player->netStore();
+ return player->netStore(rules);
} else if (auto monster = as<Monster>(entity)) {
- return monster->netStore();
+ return monster->netStore(rules);
} else if (auto object = as<Object>(entity)) {
- return object->netStore();
+ return object->netStore(rules);
} else if (auto plant = as<Plant>(entity)) {
- return plant->netStore();
+ return plant->netStore(rules);
} else if (auto plantDrop = as<PlantDrop>(entity)) {
- return plantDrop->netStore();
+ return plantDrop->netStore(rules);
} else if (auto projectile = as<Projectile>(entity)) {
- return projectile->netStore();
+ return projectile->netStore(rules);
} else if (auto itemDrop = as<ItemDrop>(entity)) {
- return itemDrop->netStore();
+ return itemDrop->netStore(rules);
} else if (auto npc = as<Npc>(entity)) {
- return npc->netStore();
+ return npc->netStore(rules);
} else if (auto stagehand = as<Stagehand>(entity)) {
- return stagehand->netStore();
+ return stagehand->netStore(rules);
} else if (auto vehicle = as<Vehicle>(entity)) {
- return m_vehicleDatabase->netStore(vehicle);
+ return m_vehicleDatabase->netStore(vehicle, rules);
} else {
throw EntityFactoryException::format("Don't know how to make net store for entity type '{}'", EntityTypeNames.getRight(entity->entityType()));
}
}
-EntityPtr EntityFactory::netLoadEntity(EntityType type, ByteArray const& netStore) const {
+EntityPtr EntityFactory::netLoadEntity(EntityType type, ByteArray const& netStore, NetCompatibilityRules rules) const {
RecursiveMutexLocker locker(m_mutex);
if (type == EntityType::Player) {
- return m_playerFactory->netLoadPlayer(netStore);
+ return m_playerFactory->netLoadPlayer(netStore, rules);
} else if (type == EntityType::Monster) {
- return m_monsterDatabase->netLoadMonster(netStore);
+ return m_monsterDatabase->netLoadMonster(netStore, rules);
} else if (type == EntityType::Object) {
- return m_objectDatabase->netLoadObject(netStore);
+ return m_objectDatabase->netLoadObject(netStore, rules);
} else if (type == EntityType::Plant) {
- return make_shared<Plant>(netStore);
+ return make_shared<Plant>(netStore, rules);
} else if (type == EntityType::PlantDrop) {
- return make_shared<PlantDrop>(netStore);
+ return make_shared<PlantDrop>(netStore, rules);
} else if (type == EntityType::Projectile) {
- return m_projectileDatabase->netLoadProjectile(netStore);
+ return m_projectileDatabase->netLoadProjectile(netStore, rules);
} else if (type == EntityType::ItemDrop) {
- return make_shared<ItemDrop>(netStore);
+ return make_shared<ItemDrop>(netStore, rules);
} else if (type == EntityType::Npc) {
- return m_npcDatabase->netLoadNpc(netStore);
+ return m_npcDatabase->netLoadNpc(netStore, rules);
} else if (type == EntityType::Stagehand) {
- return make_shared<Stagehand>(netStore);
+ return make_shared<Stagehand>(netStore, rules);
} else if (type == EntityType::Vehicle) {
- return m_vehicleDatabase->netLoad(netStore);
+ return m_vehicleDatabase->netLoad(netStore, rules);
} else {
throw EntityFactoryException::format("Don't know how to create entity type '{}' from net store", EntityTypeNames.getRight(type));
}
diff --git a/source/game/StarEntityFactory.hpp b/source/game/StarEntityFactory.hpp
index 09d4e99..7d5c014 100644
--- a/source/game/StarEntityFactory.hpp
+++ b/source/game/StarEntityFactory.hpp
@@ -20,8 +20,8 @@ class EntityFactory {
public:
EntityFactory();
- ByteArray netStoreEntity(EntityPtr const& entity) const;
- EntityPtr netLoadEntity(EntityType type, ByteArray const& netStore) const;
+ ByteArray netStoreEntity(EntityPtr const& entity, NetCompatibilityRules rules = {}) const;
+ EntityPtr netLoadEntity(EntityType type, ByteArray const& netStore, NetCompatibilityRules rules = {}) const;
Json diskStoreEntity(EntityPtr const& entity) const;
EntityPtr diskLoadEntity(EntityType type, Json const& diskStore) const;
diff --git a/source/game/StarHumanoid.cpp b/source/game/StarHumanoid.cpp
index 47368b1..d140881 100644
--- a/source/game/StarHumanoid.cpp
+++ b/source/game/StarHumanoid.cpp
@@ -243,6 +243,49 @@ EnumMap<Humanoid::State> const Humanoid::StateNames{
};
Humanoid::Humanoid(Json const& config) {
+ loadConfig(config);
+
+ m_twoHanded = false;
+ m_primaryHand.holdingItem = false;
+ m_altHand.holdingItem = false;
+
+ m_movingBackwards = false;
+ m_altHand.angle = 0;
+ m_facingDirection = Direction::Left;
+ m_rotation = 0;
+ m_scale = Vec2F::filled(1.f);
+ m_drawVaporTrail = false;
+ m_state = State::Idle;
+ m_emoteState = HumanoidEmote::Idle;
+ m_dance = {};
+
+ m_primaryHand.angle = 0;
+ m_animationTimer = m_emoteAnimationTimer = m_danceTimer = 0.0f;
+}
+
+Humanoid::Humanoid(HumanoidIdentity const& identity)
+ : Humanoid(Root::singleton().speciesDatabase()->species(identity.species)->humanoidConfig()) {
+ setIdentity(identity);
+}
+
+void Humanoid::setIdentity(HumanoidIdentity const& identity) {
+ m_identity = identity;
+ m_headFrameset = getHeadFromIdentity();
+ m_bodyFrameset = getBodyFromIdentity();
+ m_emoteFrameset = getFacialEmotesFromIdentity();
+ m_hairFrameset = getHairFromIdentity();
+ m_facialHairFrameset = getFacialHairFromIdentity();
+ m_facialMaskFrameset = getFacialMaskFromIdentity();
+ m_backArmFrameset = getBackArmFromIdentity();
+ m_frontArmFrameset = getFrontArmFromIdentity();
+ m_vaporTrailFrameset = getVaporTrailFrameset();
+}
+
+HumanoidIdentity const& Humanoid::identity() const {
+ return m_identity;
+}
+
+void Humanoid::loadConfig(Json const& config) {
m_timing = HumanoidTiming(config.getObject("humanoidTiming"));
m_globalOffset = jsonToVec2F(config.get("globalOffset")) / TilePixels;
@@ -271,10 +314,13 @@ Humanoid::Humanoid(Json const& config) {
m_armWalkSeq = jsonToIntList(config.get("armWalkSeq"));
m_armRunSeq = jsonToIntList(config.get("armRunSeq"));
+ m_walkBob.clear();
for (auto const& v : config.get("walkBob").toArray())
m_walkBob.append(v.toDouble() / TilePixels);
+ m_runBob.clear();
for (auto const& v : config.get("runBob").toArray())
m_runBob.append(v.toDouble() / TilePixels);
+ m_swimBob.clear();
for (auto const& v : config.get("swimBob").toArray())
m_swimBob.append(v.toDouble() / TilePixels);
@@ -290,46 +336,6 @@ Humanoid::Humanoid(Json const& config) {
m_particleEmitters = config.get("particleEmitters");
m_defaultMovementParameters = config.get("movementParameters");
-
- m_twoHanded = false;
- m_primaryHand.holdingItem = false;
- m_altHand.holdingItem = false;
-
- m_movingBackwards = false;
- m_altHand.angle = 0;
- m_facingDirection = Direction::Left;
- m_rotation = 0;
- m_scale = Vec2F::filled(1.f);
- m_drawVaporTrail = false;
- m_state = State::Idle;
- m_emoteState = HumanoidEmote::Idle;
- m_dance = {};
- m_emoteAnimationTimer = 0;
-
- m_primaryHand.angle = 0;
- m_animationTimer = 0.0f;
-}
-
-Humanoid::Humanoid(HumanoidIdentity const& identity)
- : Humanoid(Root::singleton().speciesDatabase()->species(identity.species)->humanoidConfig()) {
- setIdentity(identity);
-}
-
-void Humanoid::setIdentity(HumanoidIdentity const& identity) {
- m_identity = identity;
- m_headFrameset = getHeadFromIdentity();
- m_bodyFrameset = getBodyFromIdentity();
- m_emoteFrameset = getFacialEmotesFromIdentity();
- m_hairFrameset = getHairFromIdentity();
- m_facialHairFrameset = getFacialHairFromIdentity();
- m_facialMaskFrameset = getFacialMaskFromIdentity();
- m_backArmFrameset = getBackArmFromIdentity();
- m_frontArmFrameset = getFrontArmFromIdentity();
- m_vaporTrailFrameset = getVaporTrailFrameset();
-}
-
-HumanoidIdentity const& Humanoid::identity() const {
- return m_identity;
}
void Humanoid::setHeadArmorDirectives(Directives directives) {
diff --git a/source/game/StarHumanoid.hpp b/source/game/StarHumanoid.hpp
index 283f7e9..c83ddb5 100644
--- a/source/game/StarHumanoid.hpp
+++ b/source/game/StarHumanoid.hpp
@@ -125,6 +125,8 @@ public:
void setIdentity(HumanoidIdentity const& identity);
HumanoidIdentity const& identity() const;
+ void loadConfig(Json const& config);
+
// All of the image identifiers here are meant to be image *base* names, with
// a collection of frames specific to each piece. If an image is set to
// empty string, it is disabled.
diff --git a/source/game/StarItemDrop.cpp b/source/game/StarItemDrop.cpp
index bc3d15f..4d9dab1 100644
--- a/source/game/StarItemDrop.cpp
+++ b/source/game/StarItemDrop.cpp
@@ -92,9 +92,9 @@ ItemDrop::ItemDrop(Json const& diskStore)
m_itemDescriptor.set(m_item->descriptor());
}
-ItemDrop::ItemDrop(ByteArray store)
- : ItemDrop() {
+ItemDrop::ItemDrop(ByteArray store, NetCompatibilityRules rules) : ItemDrop() {
DataStreamBuffer ds(std::move(store));
+ ds.setStreamCompatibilityVersion(rules);
Root::singleton().itemDatabase()->loadItem(ds.read<ItemDescriptor>(), m_item);
ds.read(m_eternal);
@@ -116,8 +116,9 @@ Json ItemDrop::diskStore() const {
};
}
-ByteArray ItemDrop::netStore() const {
+ByteArray ItemDrop::netStore(NetCompatibilityRules rules) const {
DataStreamBuffer ds;
+ ds.setStreamCompatibilityVersion(rules);
ds.write(itemSafeDescriptor(m_item));
ds.write(m_eternal);
@@ -146,12 +147,12 @@ String ItemDrop::description() const {
return m_item->description();
}
-pair<ByteArray, uint64_t> ItemDrop::writeNetState(uint64_t fromVersion) {
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> ItemDrop::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void ItemDrop::readNetState(ByteArray data, float interpolationTime) {
- m_netGroup.readNetState(std::move(data), interpolationTime);
+void ItemDrop::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
void ItemDrop::enableInterpolation(float extrapolationHint) {
diff --git a/source/game/StarItemDrop.hpp b/source/game/StarItemDrop.hpp
index 3d6e931..c551c01 100644
--- a/source/game/StarItemDrop.hpp
+++ b/source/game/StarItemDrop.hpp
@@ -27,10 +27,10 @@ public:
ItemDrop(ItemPtr item);
ItemDrop(Json const& diskStore);
- ItemDrop(ByteArray netStore);
+ ItemDrop(ByteArray netStore, NetCompatibilityRules rules = {});
Json diskStore() const;
- ByteArray netStore() const;
+ ByteArray netStore(NetCompatibilityRules rules = {}) const;
EntityType entityType() const override;
@@ -39,8 +39,8 @@ public:
String description() const override;
- pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0) override;
- void readNetState(ByteArray data, float interpolationTime = 0.0f) override;
+ pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {}) override;
+ void readNetState(ByteArray data, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override;
void enableInterpolation(float extrapolationHint = 0.0f) override;
void disableInterpolation() override;
diff --git a/source/game/StarMonster.cpp b/source/game/StarMonster.cpp
index 05eaeb3..d9ce986 100644
--- a/source/game/StarMonster.cpp
+++ b/source/game/StarMonster.cpp
@@ -110,8 +110,8 @@ Json Monster::diskStore() const {
};
}
-ByteArray Monster::netStore() {
- return Root::singleton().monsterDatabase()->writeMonsterVariant(m_monsterVariant);
+ByteArray Monster::netStore(NetCompatibilityRules rules) {
+ return Root::singleton().monsterDatabase()->writeMonsterVariant(m_monsterVariant, rules);
}
EntityType Monster::entityType() const {
@@ -210,12 +210,12 @@ Vec2F Monster::velocity() const {
return m_movementController->velocity();
}
-pair<ByteArray, uint64_t> Monster::writeNetState(uint64_t fromVersion) {
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> Monster::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void Monster::readNetState(ByteArray data, float interpolationTime) {
- m_netGroup.readNetState(std::move(data), interpolationTime);
+void Monster::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
void Monster::enableInterpolation(float extrapolationHint) {
diff --git a/source/game/StarMonster.hpp b/source/game/StarMonster.hpp
index d6cae3e..5c964e3 100644
--- a/source/game/StarMonster.hpp
+++ b/source/game/StarMonster.hpp
@@ -42,7 +42,7 @@ public:
Monster(Json const& diskStore);
Json diskStore() const;
- ByteArray netStore();
+ ByteArray netStore(NetCompatibilityRules rules = {});
EntityType entityType() const override;
ClientEntityMode clientEntityMode() const override;
@@ -60,8 +60,8 @@ public:
RectF collisionArea() const override;
- pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0) override;
- void readNetState(ByteArray data, float interpolationTime = 0.0f) override;
+ pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {}) override;
+ void readNetState(ByteArray data, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override;
void enableInterpolation(float extrapolationHint) override;
void disableInterpolation() override;
diff --git a/source/game/StarMonsterDatabase.cpp b/source/game/StarMonsterDatabase.cpp
index 963cf87..2b86d44 100644
--- a/source/game/StarMonsterDatabase.cpp
+++ b/source/game/StarMonsterDatabase.cpp
@@ -170,8 +170,9 @@ MonsterVariant MonsterDatabase::monsterVariant(String const& typeName, uint64_t
});
}
-ByteArray MonsterDatabase::writeMonsterVariant(MonsterVariant const& variant) const {
+ByteArray MonsterDatabase::writeMonsterVariant(MonsterVariant const& variant, NetCompatibilityRules rules) const {
DataStreamBuffer ds;
+ ds.setStreamCompatibilityVersion(rules);
ds.write(variant.type);
ds.write(variant.seed);
@@ -180,8 +181,9 @@ ByteArray MonsterDatabase::writeMonsterVariant(MonsterVariant const& variant) co
return ds.data();
}
-MonsterVariant MonsterDatabase::readMonsterVariant(ByteArray const& data) const {
+MonsterVariant MonsterDatabase::readMonsterVariant(ByteArray const& data, NetCompatibilityRules rules) const {
DataStreamBuffer ds(data);
+ ds.setStreamCompatibilityVersion(rules);
String type = ds.read<String>();
uint64_t seed = ds.read<uint64_t>();
@@ -216,8 +218,8 @@ MonsterPtr MonsterDatabase::diskLoadMonster(Json const& diskStore) const {
return make_shared<Monster>(diskStore);
}
-MonsterPtr MonsterDatabase::netLoadMonster(ByteArray const& netStore) const {
- return make_shared<Monster>(readMonsterVariant(netStore));
+MonsterPtr MonsterDatabase::netLoadMonster(ByteArray const& netStore, NetCompatibilityRules rules) const {
+ return make_shared<Monster>(readMonsterVariant(netStore, rules));
}
List<Drawable> MonsterDatabase::monsterPortrait(MonsterVariant const& variant) const {
diff --git a/source/game/StarMonsterDatabase.hpp b/source/game/StarMonsterDatabase.hpp
index 9147542..0220267 100644
--- a/source/game/StarMonsterDatabase.hpp
+++ b/source/game/StarMonsterDatabase.hpp
@@ -96,8 +96,8 @@ public:
MonsterVariant randomMonster(String const& typeName, Json const& uniqueParameters = JsonObject()) const;
MonsterVariant monsterVariant(String const& typeName, uint64_t seed, Json const& uniqueParameters = JsonObject()) const;
- ByteArray writeMonsterVariant(MonsterVariant const& variant) const;
- MonsterVariant readMonsterVariant(ByteArray const& data) const;
+ ByteArray writeMonsterVariant(MonsterVariant const& variant, NetCompatibilityRules rules = {}) const;
+ MonsterVariant readMonsterVariant(ByteArray const& data, NetCompatibilityRules rules = {}) const;
Json writeMonsterVariantToJson(MonsterVariant const& mVar) const;
MonsterVariant readMonsterVariantFromJson(Json const& variant) const;
@@ -106,7 +106,7 @@ public:
// whatever world they're spawned in.
MonsterPtr createMonster(MonsterVariant monsterVariant, Maybe<float> level = {}, Json uniqueParameters = {}) const;
MonsterPtr diskLoadMonster(Json const& diskStore) const;
- MonsterPtr netLoadMonster(ByteArray const& netStore) const;
+ MonsterPtr netLoadMonster(ByteArray const& netStore, NetCompatibilityRules rules = {}) const;
List<Drawable> monsterPortrait(MonsterVariant const& variant) const;
diff --git a/source/game/StarNetPackets.hpp b/source/game/StarNetPackets.hpp
index 6181b30..87a01cf 100644
--- a/source/game/StarNetPackets.hpp
+++ b/source/game/StarNetPackets.hpp
@@ -14,6 +14,7 @@
#include "StarWiring.hpp"
#include "StarClientContext.hpp"
#include "StarSystemWorld.hpp"
+#include "StarNetCompatibility.hpp"
namespace Star {
diff --git a/source/game/StarNpc.cpp b/source/game/StarNpc.cpp
index c68fd08..388f6e5 100644
--- a/source/game/StarNpc.cpp
+++ b/source/game/StarNpc.cpp
@@ -152,8 +152,8 @@ Json Npc::diskStore() const {
};
}
-ByteArray Npc::netStore() {
- return Root::singleton().npcDatabase()->writeNpcVariant(m_npcVariant);
+ByteArray Npc::netStore(NetCompatibilityRules rules) {
+ return Root::singleton().npcDatabase()->writeNpcVariant(m_npcVariant, rules);
}
EntityType Npc::entityType() const {
@@ -252,7 +252,7 @@ RectF Npc::collisionArea() const {
return m_movementController->collisionPoly().boundBox();
}
-pair<ByteArray, uint64_t> Npc::writeNetState(uint64_t fromVersion) {
+pair<ByteArray, uint64_t> Npc::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
// client-side npcs error nearby vanilla NPC scripts because callScriptedEntity
// for now, scrungle the collision poly to avoid their queries. hacky :(
if (m_npcVariant.overrides && m_npcVariant.overrides.getBool("overrideNetPoly", false)) {
@@ -260,18 +260,18 @@ pair<ByteArray, uint64_t> Npc::writeNetState(uint64_t fromVersion) {
if (*mode == EntityMode::Master && connectionForEntity(entityId()) != ServerConnectionId) {
PolyF poly = m_movementController->collisionPoly();
m_movementController->setCollisionPoly({ { 0.0f, -3.402823466e+38F }});
- auto result = m_netGroup.writeNetState(fromVersion);
+ auto result = m_netGroup.writeNetState(fromVersion, rules);
m_movementController->setCollisionPoly(poly);
return result;
}
}
}
- return m_netGroup.writeNetState(fromVersion);
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void Npc::readNetState(ByteArray data, float interpolationTime) {
- m_netGroup.readNetState(std::move(data), interpolationTime);
+void Npc::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
String Npc::description() const {
diff --git a/source/game/StarNpc.hpp b/source/game/StarNpc.hpp
index ab8bc0f..0b22702 100644
--- a/source/game/StarNpc.hpp
+++ b/source/game/StarNpc.hpp
@@ -42,11 +42,12 @@ class Npc
public virtual PhysicsEntity,
public virtual EmoteEntity {
public:
+ Npc(ByteArray const& netStore, NetCompatibilityRules rules = {});
Npc(NpcVariant const& npcVariant);
Npc(NpcVariant const& npcVariant, Json const& initialState);
Json diskStore() const;
- ByteArray netStore();
+ ByteArray netStore(NetCompatibilityRules rules = {});
EntityType entityType() const override;
ClientEntityMode clientEntityMode() const override;
@@ -66,8 +67,8 @@ public:
RectF collisionArea() const override;
- pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0) override;
- void readNetState(ByteArray data, float interpolationTime = 0.0f) override;
+ pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {}) override;
+ void readNetState(ByteArray data, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override;
void enableInterpolation(float extrapolationHint = 0.0f) override;
void disableInterpolation() override;
diff --git a/source/game/StarNpcDatabase.cpp b/source/game/StarNpcDatabase.cpp
index bd70df5..392b29d 100644
--- a/source/game/StarNpcDatabase.cpp
+++ b/source/game/StarNpcDatabase.cpp
@@ -157,8 +157,9 @@ NpcVariant NpcDatabase::generateNpcVariant(
return variant;
}
-ByteArray NpcDatabase::writeNpcVariant(NpcVariant const& variant) const {
+ByteArray NpcDatabase::writeNpcVariant(NpcVariant const& variant, NetCompatibilityRules rules) const {
DataStreamBuffer ds;
+ ds.setStreamCompatibilityVersion(rules);
ds.write(variant.species);
ds.write(variant.typeName);
@@ -179,8 +180,9 @@ ByteArray NpcDatabase::writeNpcVariant(NpcVariant const& variant) const {
return ds.data();
}
-NpcVariant NpcDatabase::readNpcVariant(ByteArray const& data) const {
+NpcVariant NpcDatabase::readNpcVariant(ByteArray const& data, NetCompatibilityRules rules) const {
DataStreamBuffer ds(data);
+ ds.setStreamCompatibilityVersion(rules);
NpcVariant variant;
@@ -325,8 +327,8 @@ NpcPtr NpcDatabase::diskLoadNpc(Json const& diskStore) const {
return make_shared<Npc>(npcVariant, diskStore);
}
-NpcPtr NpcDatabase::netLoadNpc(ByteArray const& netStore) const {
- return make_shared<Npc>(readNpcVariant(netStore));
+NpcPtr NpcDatabase::netLoadNpc(ByteArray const& netStore, NetCompatibilityRules rules) const {
+ return make_shared<Npc>(readNpcVariant(netStore, rules));
}
List<Drawable> NpcDatabase::npcPortrait(NpcVariant const& npcVariant, PortraitMode mode) const {
diff --git a/source/game/StarNpcDatabase.hpp b/source/game/StarNpcDatabase.hpp
index 01dec97..a97d0f5 100644
--- a/source/game/StarNpcDatabase.hpp
+++ b/source/game/StarNpcDatabase.hpp
@@ -58,15 +58,15 @@ public:
NpcVariant generateNpcVariant(String const& species, String const& typeName, float level) const;
NpcVariant generateNpcVariant(String const& species, String const& typeName, float level, uint64_t seed, Json const& overrides) const;
- ByteArray writeNpcVariant(NpcVariant const& variant) const;
- NpcVariant readNpcVariant(ByteArray const& data) const;
+ ByteArray writeNpcVariant(NpcVariant const& variant, NetCompatibilityRules rules = {}) const;
+ NpcVariant readNpcVariant(ByteArray const& data, NetCompatibilityRules rules = {}) const;
Json writeNpcVariantToJson(NpcVariant const& variant) const;
NpcVariant readNpcVariantFromJson(Json const& data) const;
NpcPtr createNpc(NpcVariant const& npcVariant) const;
- NpcPtr diskLoadNpc(Json const& diskStoree) const;
- NpcPtr netLoadNpc(ByteArray const& netStore) const;
+ NpcPtr diskLoadNpc(Json const& diskStore) const;
+ NpcPtr netLoadNpc(ByteArray const& netStore, NetCompatibilityRules rules = {}) const;
List<Drawable> npcPortrait(NpcVariant const& npcVariant, PortraitMode mode) const;
diff --git a/source/game/StarObject.cpp b/source/game/StarObject.cpp
index ece0b20..66f7917 100644
--- a/source/game/StarObject.cpp
+++ b/source/game/StarObject.cpp
@@ -118,8 +118,9 @@ Json Object::diskStore() const {
return writeStoredData().setAll({{"name", m_config->name}, {"parameters", m_parameters.baseMap()}});
}
-ByteArray Object::netStore() {
+ByteArray Object::netStore(NetCompatibilityRules rules) {
DataStreamBuffer ds;
+ ds.setStreamCompatibilityVersion(rules);
ds.write(m_config->name);
ds.write<Json>(m_parameters.baseMap());
return ds.takeData();
@@ -297,13 +298,12 @@ RectF Object::metaBoundBox() const {
}
}
-pair<ByteArray, uint64_t> Object::writeNetState(uint64_t fromVersion) {
- DataStreamBuffer ds;
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> Object::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void Object::readNetState(ByteArray delta, float interpolationTime) {
- m_netGroup.readNetState(std::move(delta), interpolationTime);
+void Object::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
Vec2I Object::tilePosition() const {
diff --git a/source/game/StarObject.hpp b/source/game/StarObject.hpp
index a797842..e768ca8 100644
--- a/source/game/StarObject.hpp
+++ b/source/game/StarObject.hpp
@@ -37,7 +37,7 @@ public:
Object(ObjectConfigConstPtr config, Json const& parameters = JsonObject());
Json diskStore() const;
- ByteArray netStore();
+ ByteArray netStore(NetCompatibilityRules rules = {});
virtual EntityType entityType() const override;
virtual ClientEntityMode clientEntityMode() const override;
@@ -48,8 +48,8 @@ public:
virtual Vec2F position() const override;
virtual RectF metaBoundBox() const override;
- virtual pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0) override;
- virtual void readNetState(ByteArray data, float interpolationTime = 0.0f) override;
+ virtual pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {}) override;
+ virtual void readNetState(ByteArray data, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override;
virtual String description() const override;
diff --git a/source/game/StarObjectDatabase.cpp b/source/game/StarObjectDatabase.cpp
index 8ed56e4..71eb166 100644
--- a/source/game/StarObjectDatabase.cpp
+++ b/source/game/StarObjectDatabase.cpp
@@ -380,8 +380,9 @@ ObjectPtr ObjectDatabase::diskLoadObject(Json const& diskStore) const {
return object;
}
-ObjectPtr ObjectDatabase::netLoadObject(ByteArray const& netStore) const {
+ObjectPtr ObjectDatabase::netLoadObject(ByteArray const& netStore, NetCompatibilityRules rules) const {
DataStreamBuffer ds(netStore);
+ ds.setStreamCompatibilityVersion(rules);
String name = ds.read<String>();
Json parameters = ds.read<Json>();
return createObject(name, parameters);
diff --git a/source/game/StarObjectDatabase.hpp b/source/game/StarObjectDatabase.hpp
index ca44c2e..b431445 100644
--- a/source/game/StarObjectDatabase.hpp
+++ b/source/game/StarObjectDatabase.hpp
@@ -198,7 +198,7 @@ public:
ObjectPtr createObject(String const& objectName, Json const& objectParameters = JsonObject()) const;
ObjectPtr diskLoadObject(Json const& diskStore) const;
- ObjectPtr netLoadObject(ByteArray const& netStore) const;
+ ObjectPtr netLoadObject(ByteArray const& netStore, NetCompatibilityRules rules = {}) const;
bool canPlaceObject(World const* world, Vec2I const& position, String const& objectName) const;
// If the object is placeable in the given position, creates the given object
diff --git a/source/game/StarPlant.cpp b/source/game/StarPlant.cpp
index 7b4e426..32e1575 100644
--- a/source/game/StarPlant.cpp
+++ b/source/game/StarPlant.cpp
@@ -411,8 +411,9 @@ Json Plant::diskStore() const {
};
}
-ByteArray Plant::netStore() const {
+ByteArray Plant::netStore(NetCompatibilityRules rules) const {
DataStreamBuffer ds;
+ ds.setStreamCompatibilityVersion(rules);
ds.viwrite(m_tilePosition[0]);
ds.viwrite(m_tilePosition[1]);
ds.write(m_ceiling);
@@ -423,7 +424,7 @@ ByteArray Plant::netStore() const {
ds.write(m_ephemeral);
ds.write(m_tileDamageParameters);
ds.write(m_fallsWhenDead);
- m_tileDamageStatus.netStore(ds);
+ m_tileDamageStatus.netStore(ds, rules);
ds.write(writePieces());
return ds.takeData();
@@ -534,7 +535,7 @@ Plant::Plant(Json const& diskStore) : Plant() {
setupNetStates();
}
-Plant::Plant(ByteArray const& netStore) : Plant() {
+Plant::Plant(ByteArray const& netStore, NetCompatibilityRules rules) : Plant() {
m_broken = false;
m_tilePosition = Vec2I();
m_ceiling = false;
@@ -545,6 +546,7 @@ Plant::Plant(ByteArray const& netStore) : Plant() {
m_piecesUpdated = true;
DataStreamBuffer ds(netStore);
+ ds.setStreamCompatibilityVersion(rules);
ds.viread(m_tilePosition[0]);
ds.viread(m_tilePosition[1]);
ds.read(m_ceiling);
@@ -555,7 +557,7 @@ Plant::Plant(ByteArray const& netStore) : Plant() {
ds.read(m_ephemeral);
ds.read(m_tileDamageParameters);
ds.read(m_fallsWhenDead);
- m_tileDamageStatus.netLoad(ds);
+ m_tileDamageStatus.netLoad(ds, rules);
readPieces(ds.read<ByteArray>());
setupNetStates();
@@ -586,12 +588,12 @@ void Plant::init(World* world, EntityId entityId, EntityMode mode) {
m_tilePosition = world->geometry().xwrap(m_tilePosition);
}
-pair<ByteArray, uint64_t> Plant::writeNetState(uint64_t fromVersion) {
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> Plant::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void Plant::readNetState(ByteArray data, float interpolationTime) {
- m_netGroup.readNetState(std::move(data), interpolationTime);
+void Plant::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
void Plant::enableInterpolation(float extrapolationHint) {
diff --git a/source/game/StarPlant.hpp b/source/game/StarPlant.hpp
index 53aed5f..ddb9501 100644
--- a/source/game/StarPlant.hpp
+++ b/source/game/StarPlant.hpp
@@ -57,10 +57,10 @@ public:
Plant(GrassVariant const& config, uint64_t seed);
Plant(BushVariant const& config, uint64_t seed);
Plant(Json const& diskStore);
- Plant(ByteArray const& netStore);
+ Plant(ByteArray const& netStore, NetCompatibilityRules rules = {});
Json diskStore() const;
- ByteArray netStore() const;
+ ByteArray netStore(NetCompatibilityRules rules = {}) const;
EntityType entityType() const override;
@@ -68,8 +68,8 @@ public:
virtual String description() const override;
- pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0) override;
- void readNetState(ByteArray data, float interpolationTime = 0.0f) override;
+ pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {}) override;
+ void readNetState(ByteArray data, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override;
void enableInterpolation(float extrapolationHint) override;
void disableInterpolation() override;
diff --git a/source/game/StarPlantDrop.cpp b/source/game/StarPlantDrop.cpp
index ccb57d8..b41705d 100644
--- a/source/game/StarPlantDrop.cpp
+++ b/source/game/StarPlantDrop.cpp
@@ -86,11 +86,12 @@ PlantDrop::PlantDrop(List<Plant::PlantPiece> pieces, Vec2F const& position, Vec2
m_collisionRect = fullBounds;
}
-PlantDrop::PlantDrop(ByteArray const& netStore) {
+PlantDrop::PlantDrop(ByteArray const& netStore, NetCompatibilityRules rules) {
m_netGroup.addNetElement(&m_movementController);
m_netGroup.addNetElement(&m_spawnedDrops);
DataStreamBuffer ds(netStore);
+ ds.setStreamCompatibilityVersion(rules);
ds >> m_time;
ds >> m_master;
ds >> m_description;
@@ -113,7 +114,7 @@ PlantDrop::PlantDrop(ByteArray const& netStore) {
m_spawnedDropEffects = true;
}
-ByteArray PlantDrop::netStore() {
+ByteArray PlantDrop::netStore(NetCompatibilityRules rules) {
DataStreamBuffer ds;
ds << m_time;
ds << m_master;
@@ -358,12 +359,12 @@ void PlantDrop::render(RenderCallback* renderCallback) {
}
}
-pair<ByteArray, uint64_t> PlantDrop::writeNetState(uint64_t fromVersion) {
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> PlantDrop::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void PlantDrop::readNetState(ByteArray data, float interpolationTime) {
- m_netGroup.readNetState(std::move(data), interpolationTime);
+void PlantDrop::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
void PlantDrop::enableInterpolation(float extrapolationHint) {
diff --git a/source/game/StarPlantDrop.hpp b/source/game/StarPlantDrop.hpp
index 1b630f4..7528e5d 100644
--- a/source/game/StarPlantDrop.hpp
+++ b/source/game/StarPlantDrop.hpp
@@ -15,9 +15,9 @@ public:
PlantDrop(List<Plant::PlantPiece> pieces, Vec2F const& position, Vec2F const& strikeVector, String const& description,
bool upsideDown, Json stemConfig, Json foliageConfig, Json saplingConfig,
bool master, float random);
- PlantDrop(ByteArray const& netStore);
+ PlantDrop(ByteArray const& netStore, NetCompatibilityRules rules = {});
- ByteArray netStore();
+ ByteArray netStore(NetCompatibilityRules rules = {});
EntityType entityType() const override;
@@ -26,8 +26,8 @@ public:
String description() const override;
- pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0) override;
- void readNetState(ByteArray data, float interpolationTime = 0.0f) override;
+ pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {}) override;
+ void readNetState(ByteArray data, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override;
void enableInterpolation(float extrapolationHint = 0.0f) override;
void disableInterpolation() override;
diff --git a/source/game/StarPlayer.cpp b/source/game/StarPlayer.cpp
index 9c9c482..8ee6d7a 100644
--- a/source/game/StarPlayer.cpp
+++ b/source/game/StarPlayer.cpp
@@ -187,8 +187,9 @@ Player::Player(PlayerConfigPtr config, Uuid uuid) {
m_netGroup.setNeedsStoreCallback(bind(&Player::setNetStates, this));
}
-Player::Player(PlayerConfigPtr config, ByteArray const& netStore) : Player(config) {
+Player::Player(PlayerConfigPtr config, ByteArray const& netStore, NetCompatibilityRules rules) : Player(config) {
DataStreamBuffer ds(netStore);
+ ds.setStreamCompatibilityVersion(rules);
setUniqueId(ds.read<String>());
@@ -1618,12 +1619,12 @@ Direction Player::facingDirection() const {
return m_movementController->facingDirection();
}
-pair<ByteArray, uint64_t> Player::writeNetState(uint64_t fromVersion) {
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> Player::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void Player::readNetState(ByteArray data, float interpolationTime) {
- m_netGroup.readNetState(std::move(data), interpolationTime);
+void Player::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
void Player::enableInterpolation(float) {
@@ -2319,8 +2320,9 @@ Json Player::diskStore() {
};
}
-ByteArray Player::netStore() {
+ByteArray Player::netStore(NetCompatibilityRules rules) {
DataStreamBuffer ds;
+ ds.setStreamCompatibilityVersion(rules);
ds.write(*uniqueId());
ds.write(m_description);
diff --git a/source/game/StarPlayer.hpp b/source/game/StarPlayer.hpp
index 871ca47..f6e32e0 100644
--- a/source/game/StarPlayer.hpp
+++ b/source/game/StarPlayer.hpp
@@ -76,7 +76,7 @@ public:
static EnumMap<State> const StateNames;
Player(PlayerConfigPtr config, Uuid uuid = Uuid());
- Player(PlayerConfigPtr config, ByteArray const& netStore);
+ Player(PlayerConfigPtr config, ByteArray const& netStore, NetCompatibilityRules rules = {});
Player(PlayerConfigPtr config, Json const& diskStore);
void diskLoad(Json const& diskStore);
@@ -92,7 +92,7 @@ public:
QuestManagerPtr questManager() const;
Json diskStore();
- ByteArray netStore();
+ ByteArray netStore(NetCompatibilityRules rules = {});
EntityType entityType() const override;
ClientEntityMode clientEntityMode() const override;
@@ -118,8 +118,8 @@ public:
// relative to current position
RectF collisionArea() const override;
- pair<ByteArray, uint64_t> writeNetState(uint64_t fromStep = 0) override;
- void readNetState(ByteArray data, float interpolationStep = 0.0f) override;
+ pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {}) override;
+ void readNetState(ByteArray data, float interpolationStep = 0.0f, NetCompatibilityRules rules = {}) override;
void enableInterpolation(float extrapolationHint = 0.0f) override;
void disableInterpolation() override;
diff --git a/source/game/StarPlayerFactory.cpp b/source/game/StarPlayerFactory.cpp
index f3dbf29..43854b6 100644
--- a/source/game/StarPlayerFactory.cpp
+++ b/source/game/StarPlayerFactory.cpp
@@ -60,8 +60,8 @@ PlayerPtr PlayerFactory::diskLoadPlayer(Json const& diskStore) const {
return make_shared<Player>(m_config, diskStore);
}
-PlayerPtr PlayerFactory::netLoadPlayer(ByteArray const& netStore) const {
- return make_shared<Player>(m_config, netStore);
+PlayerPtr PlayerFactory::netLoadPlayer(ByteArray const& netStore, NetCompatibilityRules rules) const {
+ return make_shared<Player>(m_config, netStore, rules);
}
}
diff --git a/source/game/StarPlayerFactory.hpp b/source/game/StarPlayerFactory.hpp
index a366221..6712b81 100644
--- a/source/game/StarPlayerFactory.hpp
+++ b/source/game/StarPlayerFactory.hpp
@@ -60,7 +60,7 @@ public:
PlayerPtr create() const;
PlayerPtr diskLoadPlayer(Json const& diskStore) const;
- PlayerPtr netLoadPlayer(ByteArray const& netStore) const;
+ PlayerPtr netLoadPlayer(ByteArray const& netStore, NetCompatibilityRules rules = {}) const;
private:
PlayerConfigPtr m_config;
diff --git a/source/game/StarProjectile.cpp b/source/game/StarProjectile.cpp
index 1503f4c..47cb973 100644
--- a/source/game/StarProjectile.cpp
+++ b/source/game/StarProjectile.cpp
@@ -27,7 +27,7 @@ Projectile::Projectile(ProjectileConfigPtr const& config, Json const& parameters
setup();
}
-Projectile::Projectile(ProjectileConfigPtr const& config, DataStreamBuffer& data) {
+Projectile::Projectile(ProjectileConfigPtr const& config, DataStreamBuffer& data, NetCompatibilityRules rules) {
m_config = config;
data.read(m_parameters);
setup();
@@ -41,8 +41,9 @@ Projectile::Projectile(ProjectileConfigPtr const& config, DataStreamBuffer& data
setTeam(data.read<EntityDamageTeam>());
}
-ByteArray Projectile::netStore() const {
+ByteArray Projectile::netStore(NetCompatibilityRules rules) const {
DataStreamBuffer ds;
+ ds.setStreamCompatibilityVersion(rules);
ds.write(m_config->typeName);
ds.write(m_parameters);
@@ -141,12 +142,12 @@ Vec2F Projectile::velocity() const {
return m_movementController->velocity();
}
-pair<ByteArray, uint64_t> Projectile::writeNetState(uint64_t fromVersion) {
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> Projectile::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void Projectile::readNetState(ByteArray data, float interpolationTime) {
- m_netGroup.readNetState(std::move(data), interpolationTime);
+void Projectile::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
void Projectile::enableInterpolation(float extrapolationHint) {
diff --git a/source/game/StarProjectile.hpp b/source/game/StarProjectile.hpp
index 2aec4b4..fdafdbd 100644
--- a/source/game/StarProjectile.hpp
+++ b/source/game/StarProjectile.hpp
@@ -22,9 +22,9 @@ STAR_CLASS(Projectile);
class Projectile : public virtual Entity, public virtual ScriptedEntity, public virtual PhysicsEntity, public virtual StatusEffectEntity {
public:
Projectile(ProjectileConfigPtr const& config, Json const& parameters);
- Projectile(ProjectileConfigPtr const& config, DataStreamBuffer& netState);
+ Projectile(ProjectileConfigPtr const& config, DataStreamBuffer& netState, NetCompatibilityRules rules = {});
- ByteArray netStore() const;
+ ByteArray netStore(NetCompatibilityRules rules = {}) const;
EntityType entityType() const override;
@@ -43,8 +43,8 @@ public:
ClientEntityMode clientEntityMode() const override;
bool masterOnly() const override;
- pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0) override;
- void readNetState(ByteArray data, float interpolationTime = 0.0f) override;
+ pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {}) override;
+ void readNetState(ByteArray data, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override;
void enableInterpolation(float extrapolationHint = 0.0f) override;
void disableInterpolation() override;
diff --git a/source/game/StarProjectileDatabase.cpp b/source/game/StarProjectileDatabase.cpp
index c2a0c26..bb7fbc7 100644
--- a/source/game/StarProjectileDatabase.cpp
+++ b/source/game/StarProjectileDatabase.cpp
@@ -58,10 +58,11 @@ float ProjectileDatabase::gravityMultiplier(String const& type) const {
return config->movementSettings.getFloat("gravityMultiplier", 1);
}
-ProjectilePtr ProjectileDatabase::netLoadProjectile(ByteArray const& netStore) const {
+ProjectilePtr ProjectileDatabase::netLoadProjectile(ByteArray const& netStore, NetCompatibilityRules rules) const {
DataStreamBuffer ds(netStore);
+ ds.setStreamCompatibilityVersion(rules);
String typeName = ds.read<String>();
- return make_shared<Projectile>(m_configs.get(typeName), ds);
+ return make_shared<Projectile>(m_configs.get(typeName), ds, rules);
}
ProjectileConfigPtr ProjectileDatabase::readConfig(String const& path) {
diff --git a/source/game/StarProjectileDatabase.hpp b/source/game/StarProjectileDatabase.hpp
index d3cdd3d..dbd7299 100644
--- a/source/game/StarProjectileDatabase.hpp
+++ b/source/game/StarProjectileDatabase.hpp
@@ -111,7 +111,7 @@ public:
float gravityMultiplier(String const& type) const;
ProjectilePtr createProjectile(String const& type, Json const& parameters = JsonObject()) const;
- ProjectilePtr netLoadProjectile(ByteArray const& netStore) const;
+ ProjectilePtr netLoadProjectile(ByteArray const& netStore, NetCompatibilityRules rules = {}) const;
private:
ProjectileConfigPtr readConfig(String const& path);
diff --git a/source/game/StarServerClientContext.cpp b/source/game/StarServerClientContext.cpp
index 838dae0..fcbffbd 100644
--- a/source/game/StarServerClientContext.cpp
+++ b/source/game/StarServerClientContext.cpp
@@ -10,10 +10,11 @@
namespace Star {
-ServerClientContext::ServerClientContext(ConnectionId clientId, Maybe<HostAddress> remoteAddress, Uuid playerUuid,
+ServerClientContext::ServerClientContext(ConnectionId clientId, Maybe<HostAddress> remoteAddress, NetCompatibilityRules netRules, Uuid playerUuid,
String playerName, String playerSpecies, bool canBecomeAdmin, WorldChunks initialShipChunks)
: m_clientId(clientId),
m_remoteAddress(remoteAddress),
+ m_netRules(netRules),
m_playerUuid(playerUuid),
m_playerName(playerName),
m_playerSpecies(playerSpecies),
@@ -88,6 +89,10 @@ bool ServerClientContext::canBecomeAdmin() const {
return m_canBecomeAdmin;
}
+NetCompatibilityRules ServerClientContext::netRules() const {
+ return m_netRules;
+}
+
String ServerClientContext::descriptiveName() const {
RecursiveMutexLocker locker(m_mutex);
String hostName = m_remoteAddress ? toString(*m_remoteAddress) : "local";
@@ -184,7 +189,7 @@ ByteArray ServerClientContext::writeUpdate() {
shipChunksUpdate = DataStreamBuffer::serialize(take(m_shipChunksUpdate));
ByteArray netGroupUpdate;
- tie(netGroupUpdate, m_netVersion) = m_netGroup.writeNetState(m_netVersion);
+ tie(netGroupUpdate, m_netVersion) = m_netGroup.writeNetState(m_netVersion, m_netRules);
if (rpcUpdate.empty() && shipChunksUpdate.empty() && netGroupUpdate.empty())
return {};
diff --git a/source/game/StarServerClientContext.hpp b/source/game/StarServerClientContext.hpp
index 9dbab43..7c54765 100644
--- a/source/game/StarServerClientContext.hpp
+++ b/source/game/StarServerClientContext.hpp
@@ -19,7 +19,7 @@ STAR_CLASS(ServerClientContext);
class ServerClientContext {
public:
- ServerClientContext(ConnectionId clientId, Maybe<HostAddress> remoteAddress, Uuid playerUuid,
+ ServerClientContext(ConnectionId clientId, Maybe<HostAddress> remoteAddress, NetCompatibilityRules netRules, Uuid playerUuid,
String playerName, String playerSpecies, bool canBecomeAdmin, WorldChunks initialShipChunks);
ConnectionId clientId() const;
@@ -28,6 +28,7 @@ public:
String const& playerName() const;
String const& playerSpecies() const;
bool canBecomeAdmin() const;
+ NetCompatibilityRules netRules() const;
String descriptiveName() const;
// Register additional rpc methods from other server side services.
@@ -87,6 +88,7 @@ public:
private:
ConnectionId const m_clientId;
Maybe<HostAddress> const m_remoteAddress;
+ NetCompatibilityRules m_netRules;
Uuid const m_playerUuid;
String const m_playerName;
String const m_playerSpecies;
diff --git a/source/game/StarSky.cpp b/source/game/StarSky.cpp
index d9ac03a..51e5b73 100644
--- a/source/game/StarSky.cpp
+++ b/source/game/StarSky.cpp
@@ -66,12 +66,12 @@ void Sky::jumpTo(SkyParameters skyParameters) {
m_skyParametersUpdated = true;
}
-pair<ByteArray, uint64_t> Sky::writeUpdate(uint64_t fromVersion) {
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> Sky::writeUpdate(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void Sky::readUpdate(ByteArray data) {
- m_netGroup.readNetState(std::move(data));
+void Sky::readUpdate(ByteArray data, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(std::move(data), 0.0f, rules);
}
void Sky::stateUpdate() {
diff --git a/source/game/StarSky.hpp b/source/game/StarSky.hpp
index 4f6ec0c..6065a9d 100644
--- a/source/game/StarSky.hpp
+++ b/source/game/StarSky.hpp
@@ -30,8 +30,8 @@ public:
void jumpTo(SkyParameters SkyParameters);
- pair<ByteArray, uint64_t> writeUpdate(uint64_t fromVersion = 0);
- void readUpdate(ByteArray data);
+ pair<ByteArray, uint64_t> writeUpdate(uint64_t fromVersion = 0, NetCompatibilityRules rules = {});
+ void readUpdate(ByteArray data, NetCompatibilityRules rules = {});
// handles flying and warp state transitions
void stateUpdate();
diff --git a/source/game/StarStagehand.cpp b/source/game/StarStagehand.cpp
index 83b7673..d85f77b 100644
--- a/source/game/StarStagehand.cpp
+++ b/source/game/StarStagehand.cpp
@@ -14,8 +14,7 @@ Stagehand::Stagehand(Json const& config)
readConfig(config);
}
-Stagehand::Stagehand(ByteArray const& netStore)
- : Stagehand() {
+Stagehand::Stagehand(ByteArray const& netStore, NetCompatibilityRules rules) : Stagehand() {
readConfig(DataStreamBuffer::deserialize<Json>(netStore));
}
@@ -31,7 +30,7 @@ Json Stagehand::diskStore() const {
return saveData.set("scriptStorage", m_scriptComponent.getScriptStorage());
}
-ByteArray Stagehand::netStore() {
+ByteArray Stagehand::netStore(NetCompatibilityRules rules) {
return DataStreamBuffer::serialize(m_config);
}
@@ -77,12 +76,12 @@ RectF Stagehand::metaBoundBox() const {
return m_boundBox;
}
-pair<ByteArray, uint64_t> Stagehand::writeNetState(uint64_t fromVersion) {
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> Stagehand::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void Stagehand::readNetState(ByteArray data, float) {
- m_netGroup.readNetState(std::move(data));
+void Stagehand::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
void Stagehand::update(float dt, uint64_t) {
diff --git a/source/game/StarStagehand.hpp b/source/game/StarStagehand.hpp
index 4297370..4403946 100644
--- a/source/game/StarStagehand.hpp
+++ b/source/game/StarStagehand.hpp
@@ -15,10 +15,10 @@ STAR_CLASS(Stagehand);
class Stagehand : public virtual ScriptedEntity {
public:
Stagehand(Json const& config);
- Stagehand(ByteArray const& netStore);
+ Stagehand(ByteArray const& netStore, NetCompatibilityRules rules = {});
Json diskStore() const;
- ByteArray netStore();
+ ByteArray netStore(NetCompatibilityRules rules = {});
void init(World* world, EntityId entityId, EntityMode mode) override;
void uninit() override;
@@ -31,8 +31,8 @@ public:
RectF metaBoundBox() const override;
- pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0) override;
- void readNetState(ByteArray data, float interpolationTime = 0.0f) override;
+ pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {}) override;
+ void readNetState(ByteArray data, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override;
void update(float dt, uint64_t currentStep) override;
diff --git a/source/game/StarStatusController.cpp b/source/game/StarStatusController.cpp
index 5a87874..325fc17 100644
--- a/source/game/StarStatusController.cpp
+++ b/source/game/StarStatusController.cpp
@@ -18,7 +18,32 @@ StatusController::StatusController(Json const& config) : m_statCollection(config
m_parentEntity = nullptr;
m_movementController = nullptr;
- m_statusProperties.set(config.getObject("statusProperties", {}));
+ m_statusProperties.reset(config.getObject("statusProperties", {}));
+ m_statusProperties.setOverrides(
+ [&](DataStream& ds, NetCompatibilityRules rules) {
+ if (rules.version() <= 1) ds << m_statusProperties.baseMap();
+ else m_statusProperties.NetElementHashMap<String, Json>::netStore(ds, rules);
+ },
+ [&](DataStream& ds, NetCompatibilityRules rules) {
+ if (rules.version() <= 1) m_statusProperties.reset(ds.read<JsonObject>());
+ else m_statusProperties.NetElementHashMap<String, Json>::netLoad(ds, rules);
+ },
+ [&](DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) {
+ if (rules.version() <= 1) {
+ if (m_statusProperties.shouldWriteNetDelta(fromVersion, rules)) {
+ ds << m_statusProperties.baseMap();
+ return true;
+ }
+ return false;
+ }
+ return m_statusProperties.NetElementHashMap<String, Json>::writeNetDelta(ds, fromVersion, rules);
+ },
+ [&](DataStream& ds, float interp, NetCompatibilityRules rules) {
+ if (rules.version() <= 1) m_statusProperties.reset(ds.read<JsonObject>());
+ else m_statusProperties.NetElementHashMap<String, Json>::readNetDelta(ds, interp, rules);
+ }
+ );
+
m_minimumLiquidStatusEffectPercentage = config.getFloat("minimumLiquidStatusEffectPercentage");
m_appliesEnvironmentStatusEffects = config.getBool("appliesEnvironmentStatusEffects");
m_appliesWeatherStatusEffects = config.getBool("appliesWeatherStatusEffects");
@@ -72,7 +97,7 @@ Json StatusController::diskStore() const {
}
return JsonObject{
- {"statusProperties", m_statusProperties.get()},
+ {"statusProperties", m_statusProperties.baseMap()},
{"persistentEffectCategories", std::move(persistentEffectCategories)},
{"ephemeralEffects", std::move(ephemeralEffects)},
{"resourceValues", std::move(resourceValues)},
@@ -84,7 +109,7 @@ void StatusController::diskLoad(Json const& store) {
clearAllPersistentEffects();
clearEphemeralEffects();
- m_statusProperties.set(store.getObject("statusProperties"));
+ m_statusProperties.reset(store.getObject("statusProperties"));
for (auto const& p : store.getObject("persistentEffectCategories", {}))
addPersistentEffects(p.first, p.second.toArray().transformed(jsonToPersistentStatusEffect));
@@ -103,17 +128,11 @@ void StatusController::diskLoad(Json const& store) {
}
Json StatusController::statusProperty(String const& name, Json const& def) const {
- return m_statusProperties.get().value(name, def);
+ return m_statusProperties.value(name, def);
}
void StatusController::setStatusProperty(String const& name, Json value) {
- m_statusProperties.update([&](JsonObject& statusProperties) {
- if (statusProperties[name] != value) {
- statusProperties[name] = std::move(value);
- return true;
- }
- return false;
- });
+ m_statusProperties.set(name, value);
}
StringList StatusController::statNames() const {
@@ -415,15 +434,17 @@ void StatusController::initNetVersion(NetElementVersion const* version) {
m_netGroup.initNetVersion(version);
}
-void StatusController::netStore(DataStream& ds) const {
- m_netGroup.netStore(ds);
+void StatusController::netStore(DataStream& ds, NetCompatibilityRules rules) const {
+ if (!checkWithRules(rules)) return;
+ m_netGroup.netStore(ds, rules);
}
-void StatusController::netLoad(DataStream& ds) {
+void StatusController::netLoad(DataStream& ds, NetCompatibilityRules rules) {
+ if (!checkWithRules(rules)) return;
clearAllPersistentEffects();
clearEphemeralEffects();
- m_netGroup.netLoad(ds);
+ m_netGroup.netLoad(ds, rules);
}
void StatusController::enableNetInterpolation(float extrapolationHint) {
@@ -438,12 +459,12 @@ void StatusController::tickNetInterpolation(float dt) {
m_netGroup.tickNetInterpolation(dt);
}
-bool StatusController::writeNetDelta(DataStream& ds, uint64_t fromStep) const {
- return m_netGroup.writeNetDelta(ds, fromStep);
+bool StatusController::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const {
+ return m_netGroup.writeNetDelta(ds, fromVersion, rules);
}
-void StatusController::readNetDelta(DataStream& ds, float interpolationTime) {
- m_netGroup.readNetDelta(ds, interpolationTime);
+void StatusController::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetDelta(ds, interpolationTime, rules);
}
void StatusController::blankNetDelta(float interpolationTime) {
@@ -576,15 +597,17 @@ void StatusController::EffectAnimator::initNetVersion(NetElementVersion const* v
animator.initNetVersion(version);
}
-void StatusController::EffectAnimator::netStore(DataStream& ds) const {
+void StatusController::EffectAnimator::netStore(DataStream& ds, NetCompatibilityRules rules) const {
+ if (!checkWithRules(rules)) return;
ds.write(animationConfig);
- animator.netStore(ds);
+ animator.netStore(ds, rules);
}
-void StatusController::EffectAnimator::netLoad(DataStream& ds) {
+void StatusController::EffectAnimator::netLoad(DataStream& ds, NetCompatibilityRules rules) {
+ if (!checkWithRules(rules)) return;
ds.read(animationConfig);
animator = animationConfig ? NetworkedAnimator(*animationConfig) : NetworkedAnimator();
- animator.netLoad(ds);
+ animator.netLoad(ds, rules);
}
void StatusController::EffectAnimator::enableNetInterpolation(float extrapolationHint) {
@@ -599,12 +622,12 @@ void StatusController::EffectAnimator::tickNetInterpolation(float dt) {
animator.tickNetInterpolation(dt);
}
-bool StatusController::EffectAnimator::writeNetDelta(DataStream& ds, uint64_t fromVersion) const {
- return animator.writeNetDelta(ds, fromVersion);
+bool StatusController::EffectAnimator::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const {
+ return animator.writeNetDelta(ds, fromVersion, rules);
}
-void StatusController::EffectAnimator::readNetDelta(DataStream& ds, float interpolationTime) {
- animator.readNetDelta(ds, interpolationTime);
+void StatusController::EffectAnimator::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) {
+ animator.readNetDelta(ds, interpolationTime, rules);
}
void StatusController::EffectAnimator::blankNetDelta(float interpolationTime) {
diff --git a/source/game/StarStatusController.hpp b/source/game/StarStatusController.hpp
index 810a2c9..039cc3b 100644
--- a/source/game/StarStatusController.hpp
+++ b/source/game/StarStatusController.hpp
@@ -2,6 +2,7 @@
#include "StarObserverStream.hpp"
#include "StarNetElementSystem.hpp"
+#include "StarNetElementExt.hpp"
#include "StarStatCollection.hpp"
#include "StarStatusEffectDatabase.hpp"
#include "StarDamage.hpp"
@@ -103,15 +104,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 blankNetDelta(float interpolationTime) override;
void tickMaster(float dt);
@@ -136,15 +137,15 @@ private:
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 blankNetDelta(float interpolationTime) override;
Maybe<String> animationConfig;
@@ -203,7 +204,7 @@ private:
NetElementGroup m_netGroup;
StatCollection m_statCollection;
- NetElementData<JsonObject> m_statusProperties;
+ NetElementOverride<NetElementHashMap<String, Json>> m_statusProperties;
NetElementData<DirectivesGroup> m_parentDirectives;
UniqueEffectMetadataGroup m_uniqueEffectMetadata;
diff --git a/source/game/StarSystemWorld.cpp b/source/game/StarSystemWorld.cpp
index c7e1213..9823b8f 100644
--- a/source/game/StarSystemWorld.cpp
+++ b/source/game/StarSystemWorld.cpp
@@ -453,12 +453,12 @@ void SystemObject::serverUpdate(SystemWorldServer* system, float dt) {
}
}
-pair<ByteArray, uint64_t> SystemObject::writeNetState(uint64_t fromVersion) {
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> SystemObject::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void SystemObject::readNetState(ByteArray data, float interpolationTime) {
- m_netGroup.readNetState(std::move(data), interpolationTime);
+void SystemObject::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
ByteArray SystemObject::netStore() const {
@@ -615,12 +615,12 @@ void SystemClientShip::serverUpdate(SystemWorld* system, float dt) {
}
}
-pair<ByteArray, uint64_t> SystemClientShip::writeNetState(uint64_t fromVersion) {
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> SystemClientShip::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void SystemClientShip::readNetState(ByteArray data, float interpolationTime) {
- m_netGroup.readNetState(std::move(data), interpolationTime);
+void SystemClientShip::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
ByteArray SystemClientShip::netStore() const {
diff --git a/source/game/StarSystemWorld.hpp b/source/game/StarSystemWorld.hpp
index db12833..cde3323 100644
--- a/source/game/StarSystemWorld.hpp
+++ b/source/game/StarSystemWorld.hpp
@@ -155,8 +155,8 @@ public:
void clientUpdate(float dt);
void serverUpdate(SystemWorldServer* system, float dt);
- pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion);
- void readNetState(ByteArray data, float interpolationTime);
+ pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion, NetCompatibilityRules rules = {});
+ void readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules = {});
ByteArray netStore() const;
Json diskStore() const;
@@ -198,8 +198,8 @@ public:
void clientUpdate(float dt);
void serverUpdate(SystemWorld* system, float dt);
- pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion);
- void readNetState(ByteArray data, float interpolationTime);
+ pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion, NetCompatibilityRules rules = {});
+ void readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules = {});
ByteArray netStore() const;
private:
diff --git a/source/game/StarSystemWorldServer.cpp b/source/game/StarSystemWorldServer.cpp
index 3b847eb..201b90b 100644
--- a/source/game/StarSystemWorldServer.cpp
+++ b/source/game/StarSystemWorldServer.cpp
@@ -266,7 +266,7 @@ void SystemWorldServer::queueUpdatePackets() {
HashMap<Uuid, ByteArray> shipUpdates;
for (auto ship : m_ships.values()) {
uint64_t version = versions->ships.maybe(ship->uuid()).value(0);
- auto shipUpdate = ship->writeNetState(version);
+ auto shipUpdate = ship->writeNetState(version, {});
versions->ships.set(ship->uuid(), shipUpdate.second);
if (!shipUpdate.first.empty())
shipUpdates.set(ship->uuid(), shipUpdate.first);
@@ -275,7 +275,7 @@ void SystemWorldServer::queueUpdatePackets() {
HashMap<Uuid, ByteArray> objectUpdates;
for (auto object : m_objects.values()) {
uint64_t version = versions->objects.maybe(object->uuid()).value(0);
- auto objectUpdate = object->writeNetState(version);
+ auto objectUpdate = object->writeNetState(version, {});
versions->objects.set(object->uuid(), objectUpdate.second);
if (!objectUpdate.first.empty())
objectUpdates.set(object->uuid(), objectUpdate.first);
diff --git a/source/game/StarTechController.cpp b/source/game/StarTechController.cpp
index 90926de..4c0ed55 100644
--- a/source/game/StarTechController.cpp
+++ b/source/game/StarTechController.cpp
@@ -358,15 +358,17 @@ void TechController::TechAnimator::initNetVersion(NetElementVersion const* versi
netGroup.initNetVersion(version);
}
-void TechController::TechAnimator::netStore(DataStream& ds) const {
+void TechController::TechAnimator::netStore(DataStream& ds, NetCompatibilityRules rules) const {
+ if (!checkWithRules(rules)) return;
ds << animationConfig;
- netGroup.netStore(ds);
+ netGroup.netStore(ds, rules);
}
-void TechController::TechAnimator::netLoad(DataStream& ds) {
+void TechController::TechAnimator::netLoad(DataStream& ds, NetCompatibilityRules rules) {
+ if (!checkWithRules(rules)) return;
ds >> animationConfig;
animator = animationConfig ? NetworkedAnimator(*animationConfig) : NetworkedAnimator();
- netGroup.netLoad(ds);
+ netGroup.netLoad(ds, rules);
}
void TechController::TechAnimator::enableNetInterpolation(float extrapolationHint) {
@@ -381,12 +383,12 @@ void TechController::TechAnimator::tickNetInterpolation(float dt) {
netGroup.tickNetInterpolation(dt);
}
-bool TechController::TechAnimator::writeNetDelta(DataStream& ds, uint64_t fromVersion) const {
- return netGroup.writeNetDelta(ds, fromVersion);
+bool TechController::TechAnimator::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const {
+ return netGroup.writeNetDelta(ds, fromVersion, rules);
}
-void TechController::TechAnimator::readNetDelta(DataStream& ds, float interpolationTime) {
- netGroup.readNetDelta(ds, interpolationTime);
+void TechController::TechAnimator::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) {
+ netGroup.readNetDelta(ds, interpolationTime, rules);
}
void TechController::TechAnimator::blankNetDelta(float interpolationTime) {
diff --git a/source/game/StarTechController.hpp b/source/game/StarTechController.hpp
index 3d642df..c399cb5 100644
--- a/source/game/StarTechController.hpp
+++ b/source/game/StarTechController.hpp
@@ -90,15 +90,15 @@ private:
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 blankNetDelta(float interpolationTime) override;
// If setting invisible, stops all playing audio
diff --git a/source/game/StarToolUser.cpp b/source/game/StarToolUser.cpp
index 4135dd0..be239c1 100644
--- a/source/game/StarToolUser.cpp
+++ b/source/game/StarToolUser.cpp
@@ -612,15 +612,17 @@ void ToolUser::NetItem::initNetVersion(NetElementVersion const* version) {
netItem->initNetVersion(m_netVersion);
}
-void ToolUser::NetItem::netStore(DataStream& ds) const {
+void ToolUser::NetItem::netStore(DataStream& ds, NetCompatibilityRules rules) const {
+ if (!checkWithRules(rules)) return;
const_cast<NetItem*>(this)->updateItemDescriptor();
- m_itemDescriptor.netStore(ds);
+ m_itemDescriptor.netStore(ds, rules);
if (auto netItem = as<NetElement>(m_item.get()))
- netItem->netStore(ds);
+ netItem->netStore(ds, rules);
}
-void ToolUser::NetItem::netLoad(DataStream& ds) {
- m_itemDescriptor.netLoad(ds);
+void ToolUser::NetItem::netLoad(DataStream& ds, NetCompatibilityRules rules) {
+ if (!checkWithRules(rules)) return;
+ m_itemDescriptor.netLoad(ds, rules);
auto itemDatabase = Root::singleton().itemDatabase();
if (itemDatabase->loadItem(m_itemDescriptor.get(), m_item)) {
@@ -633,7 +635,7 @@ void ToolUser::NetItem::netLoad(DataStream& ds) {
}
if (auto netItem = as<NetElement>(m_item.get()))
- netItem->netLoad(ds);
+ netItem->netLoad(ds, rules);
}
void ToolUser::NetItem::enableNetInterpolation(float extrapolationHint) {
@@ -657,23 +659,24 @@ void ToolUser::NetItem::tickNetInterpolation(float dt) {
}
}
-bool ToolUser::NetItem::writeNetDelta(DataStream& ds, uint64_t fromVersion) const {
+bool ToolUser::NetItem::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const {
+ if (!checkWithRules(rules)) return false;
bool deltaWritten = false;
const_cast<NetItem*>(this)->updateItemDescriptor();
m_buffer.clear();
- if (m_itemDescriptor.writeNetDelta(m_buffer, fromVersion)) {
+ if (m_itemDescriptor.writeNetDelta(m_buffer, fromVersion, rules)) {
deltaWritten = true;
ds.write<uint8_t>(1);
ds.writeBytes(m_buffer.data());
if (auto netItem = as<NetElement>(m_item.get())) {
ds.write<uint8_t>(2);
- netItem->netStore(ds);
+ netItem->netStore(ds, rules);
}
}
if (auto netItem = as<NetElement>(m_item.get())) {
m_buffer.clear();
- if (netItem->writeNetDelta(m_buffer, fromVersion)) {
+ if (netItem->writeNetDelta(m_buffer, fromVersion, rules)) {
deltaWritten = true;
ds.write<uint8_t>(3);
ds.writeBytes(m_buffer.data());
@@ -685,13 +688,14 @@ bool ToolUser::NetItem::writeNetDelta(DataStream& ds, uint64_t fromVersion) cons
return deltaWritten;
}
-void ToolUser::NetItem::readNetDelta(DataStream& ds, float interpolationTime) {
+void ToolUser::NetItem::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) {
+ if (!checkWithRules(rules)) return;
while (true) {
uint8_t code = ds.read<uint8_t>();
if (code == 0) {
break;
} else if (code == 1) {
- m_itemDescriptor.readNetDelta(ds);
+ m_itemDescriptor.readNetDelta(ds, 0.0f, rules);
if (!m_item || !m_item->matches(m_itemDescriptor.get(), true)) {
auto itemDatabase = Root::singleton().itemDatabase();
if (itemDatabase->loadItem(m_itemDescriptor.get(), m_item)) {
@@ -705,12 +709,12 @@ void ToolUser::NetItem::readNetDelta(DataStream& ds, float interpolationTime) {
}
} else if (code == 2) {
if (auto netItem = as<NetElement>(m_item.get()))
- netItem->netLoad(ds);
+ netItem->netLoad(ds, rules);
else
throw IOException("Server/Client disagreement about whether an Item is a NetElement in NetItem::readNetDelta");
} else if (code == 3) {
if (auto netItem = as<NetElement>(m_item.get()))
- netItem->readNetDelta(ds, interpolationTime);
+ netItem->readNetDelta(ds, interpolationTime, rules);
else
throw IOException("Server/Client disagreement about whether an Item is a NetElement in NetItem::readNetDelta");
} else {
diff --git a/source/game/StarToolUser.hpp b/source/game/StarToolUser.hpp
index 41fe98f..0b954e2 100644
--- a/source/game/StarToolUser.hpp
+++ b/source/game/StarToolUser.hpp
@@ -81,15 +81,15 @@ private:
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 blankNetDelta(float interpolationTime) override;
ItemPtr const& get() const;
diff --git a/source/game/StarUniverseClient.cpp b/source/game/StarUniverseClient.cpp
index ed59c4b..6038901 100644
--- a/source/game/StarUniverseClient.cpp
+++ b/source/game/StarUniverseClient.cpp
@@ -94,27 +94,38 @@ Maybe<String> UniverseClient::connect(UniverseConnection connection, bool allowA
else if (!protocolResponsePacket->allowed)
return String(strf("Join failed! Server does not support connections with protocol version {}", StarProtocolVersion));
- if (!(m_legacyServer = protocolResponsePacket->compressionMode() != PacketCompressionMode::Enabled)) {
- if (auto compressedSocket = as<CompressedPacketSocket>(&connection.packetSocket())) {
- if (protocolResponsePacket->info) {
- auto compressionName = protocolResponsePacket->info.getString("compression", "None");
+ NetCompatibilityRules compatibilityRules;
+ compatibilityRules.setVersion(LegacyVersion);
+ bool legacyServer = protocolResponsePacket->compressionMode() != PacketCompressionMode::Enabled;
+ if (!legacyServer) {
+ auto compressedSocket = as<CompressedPacketSocket>(&connection.packetSocket());
+ if (protocolResponsePacket->info) {
+ compatibilityRules.setVersion(protocolResponsePacket->info.getUInt("openProtocolVersion", 1));
+ auto compressionName = protocolResponsePacket->info.getString("compression", "None");
+ if (compressedSocket) {
auto compressionMode = NetCompressionModeNames.maybeLeft(compressionName);
if (!compressionMode)
return String(strf("Join failed! Unknown net stream connection type '{}'", compressionName));
Logger::info("UniverseClient: Using '{}' network stream compression", NetCompressionModeNames.getRight(*compressionMode));
compressedSocket->setCompressionStreamEnabled(compressionMode == NetCompressionMode::Zstd);
- } else if (!m_legacyServer) {
+ }
+ } else {
+ compatibilityRules.setVersion(1); // A version of 1 is OpenStarbound prior to the NetElement compatibility stuff
+ if (compressedSocket) {
Logger::info("UniverseClient: Defaulting to Zstd network stream compression (older server version)");
- compressedSocket->setCompressionStreamEnabled(true);// old OpenSB server version always expects it!
+ compressedSocket->setCompressionStreamEnabled(true);
}
}
}
- connection.packetSocket().setLegacy(m_legacyServer);
+ connection.packetSocket().setLegacy(legacyServer);
auto clientConnect = make_shared<ClientConnectPacket>(Root::singleton().assets()->digest(), allowAssetsMismatch, m_mainPlayer->uuid(), m_mainPlayer->name(),
m_mainPlayer->species(), m_playerStorage->loadShipData(m_mainPlayer->uuid()), m_mainPlayer->shipUpgrades(),
m_mainPlayer->log()->introComplete(), account);
- clientConnect->info = JsonObject{ {"brand", "OpenStarbound"} };
+ clientConnect->info = JsonObject{
+ {"brand", "OpenStarbound"},
+ {"openProtocolVersion", OpenProtocolVersion }
+ };
connection.pushSingle(std::move(clientConnect));
connection.sendAll(timeout);
@@ -136,11 +147,12 @@ Maybe<String> UniverseClient::connect(UniverseConnection connection, bool allowA
if (auto success = as<ConnectSuccessPacket>(packet)) {
m_universeClock = make_shared<Clock>();
m_clientContext = make_shared<ClientContext>(success->serverUuid, m_mainPlayer->uuid());
+ m_clientContext->setNetCompatibilityRules(compatibilityRules);
m_teamClient = make_shared<TeamClient>(m_mainPlayer, m_clientContext);
m_mainPlayer->setClientContext(m_clientContext);
m_mainPlayer->setStatistics(m_statistics);
m_worldClient = make_shared<WorldClient>(m_mainPlayer);
- m_worldClient->clientState().setLegacy(m_legacyServer);
+ m_worldClient->clientState().setNetCompatibilityRules(compatibilityRules);
m_worldClient->setAsyncLighting(true);
for (auto& pair : m_luaCallbacks)
m_worldClient->setLuaCallbacks(pair.first, pair.second);
@@ -149,7 +161,7 @@ Maybe<String> UniverseClient::connect(UniverseConnection connection, bool allowA
m_celestialDatabase = make_shared<CelestialSlaveDatabase>(std::move(success->celestialInformation));
m_systemWorldClient = make_shared<SystemWorldClient>(m_universeClock, m_celestialDatabase, m_mainPlayer->universeMap());
- Logger::info("UniverseClient: Joined {} server as client {}", m_legacyServer ? "Starbound" : "OpenStarbound", success->clientId);
+ Logger::info("UniverseClient: Joined {} server as client {}", legacyServer ? "Starbound" : "OpenStarbound", success->clientId);
return {};
} else if (auto failure = as<ConnectFailurePacket>(packet)) {
Logger::error("UniverseClient: Join failed: {}", failure->reason);
@@ -263,7 +275,7 @@ void UniverseClient::update(float dt) {
m_teamClient->update();
- auto contextUpdate = m_clientContext->writeUpdate();
+ auto contextUpdate = m_clientContext->writeUpdate(m_clientContext->netCompatibilityRules());
if (!contextUpdate.empty())
m_connection->pushSingle(make_shared<ClientContextUpdatePacket>(std::move(contextUpdate)));
@@ -650,7 +662,7 @@ void UniverseClient::handlePackets(List<PacketPtr> const& packets) {
for (auto const& packet : packets) {
try {
if (auto clientContextUpdate = as<ClientContextUpdatePacket>(packet)) {
- m_clientContext->readUpdate(clientContextUpdate->updateData);
+ m_clientContext->readUpdate(clientContextUpdate->updateData, m_clientContext->netCompatibilityRules());
m_playerStorage->applyShipUpdates(m_clientContext->playerUuid(), m_clientContext->newShipUpdates());
if (playerIsOriginal())
diff --git a/source/game/StarUniverseClient.hpp b/source/game/StarUniverseClient.hpp
index 985124a..6a85cdb 100644
--- a/source/game/StarUniverseClient.hpp
+++ b/source/game/StarUniverseClient.hpp
@@ -126,7 +126,6 @@ private:
StatisticsPtr m_statistics;
PlayerPtr m_mainPlayer;
- bool m_legacyServer;
bool m_pause;
ClockPtr m_universeClock;
WorldClientPtr m_worldClient;
diff --git a/source/game/StarUniverseServer.cpp b/source/game/StarUniverseServer.cpp
index 28f7862..6961ce4 100644
--- a/source/game/StarUniverseServer.cpp
+++ b/source/game/StarUniverseServer.cpp
@@ -835,7 +835,10 @@ void UniverseServer::warpPlayers() {
// Checking the spawn target validity then adding the client is not
// perfect, it can still become invalid in between, if we fail at
// adding the client we need to warp them back.
- if (toWorld && toWorld->addClient(clientId, warpToWorld.target, !clientContext->remoteAddress(), clientContext->canBecomeAdmin())) {
+ if (toWorld && toWorld->addClient(clientId, warpToWorld.target,
+ !clientContext->remoteAddress(),
+ clientContext->canBecomeAdmin(),
+ clientContext->netRules())) {
clientContext->setPlayerWorld(toWorld);
m_chatProcessor->joinChannel(clientId, printWorldId(warpToWorld.world));
@@ -1574,7 +1577,8 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe<HostA
auto compressionMode = NetCompressionModeNames.maybeLeft(compressionName).value(NetCompressionMode::None);
useCompressionStream = compressionMode == NetCompressionMode::Zstd;
protocolResponse->info = JsonObject{
- {"compression", NetCompressionModeNames.getRight(compressionMode)}
+ {"compression", NetCompressionModeNames.getRight(compressionMode)},
+ {"openProtocolVersion", OpenProtocolVersion}
};
}
connection.pushSingle(protocolResponse);
@@ -1671,7 +1675,10 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe<HostA
String connectionLog = strf("UniverseServer: Logged in account '{}' as player '{}' from address {}",
accountString, clientConnect->playerName, remoteAddressString);
+ NetCompatibilityRules netRules(legacyClient ? LegacyVersion : 1);
if (Json& info = clientConnect->info) {
+ if (auto openProtocolVersion = info.optUInt("openProtocolVersion"))
+ netRules.setVersion(*openProtocolVersion);
if (Json brand = info.get("brand", "custom"))
connectionLog += strf(" ({} client)", brand.toString());
if (info.getBool("legacy", false))
@@ -1698,7 +1705,7 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe<HostA
}
ConnectionId clientId = m_clients.nextId();
- auto clientContext = make_shared<ServerClientContext>(clientId, remoteAddress, clientConnect->playerUuid,
+ auto clientContext = make_shared<ServerClientContext>(clientId, remoteAddress, netRules, clientConnect->playerUuid,
clientConnect->playerName, clientConnect->playerSpecies, administrator, clientConnect->shipChunks);
m_clients.add(clientId, clientContext);
m_connectionServer->addConnection(clientId, std::move(connection));
diff --git a/source/game/StarVehicle.cpp b/source/game/StarVehicle.cpp
index 0aec750..29a57b0 100644
--- a/source/game/StarVehicle.cpp
+++ b/source/game/StarVehicle.cpp
@@ -242,12 +242,12 @@ Vec2F Vehicle::velocity() const {
return m_movementController.velocity();
}
-pair<ByteArray, uint64_t> Vehicle::writeNetState(uint64_t fromVersion) {
- return m_netGroup.writeNetState(fromVersion);
+pair<ByteArray, uint64_t> Vehicle::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
+ return m_netGroup.writeNetState(fromVersion, rules);
}
-void Vehicle::readNetState(ByteArray data, float interpolationTime) {
- m_netGroup.readNetState(std::move(data), interpolationTime);
+void Vehicle::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {
+ m_netGroup.readNetState(data, interpolationTime, rules);
}
void Vehicle::enableInterpolation(float extrapolationHint) {
diff --git a/source/game/StarVehicle.hpp b/source/game/StarVehicle.hpp
index b70d4ae..087137f 100644
--- a/source/game/StarVehicle.hpp
+++ b/source/game/StarVehicle.hpp
@@ -44,8 +44,8 @@ public:
RectF collisionArea() const override;
Vec2F velocity() const;
- pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion) override;
- void readNetState(ByteArray data, float interpolationTime = 0) override;
+ pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {}) override;
+ void readNetState(ByteArray data, float interpolationTime = 0.0f, NetCompatibilityRules rules = {}) override;
void enableInterpolation(float extrapolationHint) override;
void disableInterpolation() override;
diff --git a/source/game/StarVehicleDatabase.cpp b/source/game/StarVehicleDatabase.cpp
index 370aceb..a8739fa 100644
--- a/source/game/StarVehicleDatabase.cpp
+++ b/source/game/StarVehicleDatabase.cpp
@@ -10,7 +10,7 @@ VehicleDatabase::VehicleDatabase() {
auto assets = Root::singleton().assets();
auto& files = assets->scanExtension("vehicle");
assets->queueJsons(files);
- for (auto& file : files) {
+ for (String file : files) {
try {
auto config = assets->json(file);
String name = config.getString("name");
@@ -32,15 +32,18 @@ VehiclePtr VehicleDatabase::create(String const& vehicleName, Json const& extraC
return make_shared<Vehicle>(configPair->second, configPair->first, extraConfig);
}
-ByteArray VehicleDatabase::netStore(VehiclePtr const& vehicle) const {
+ByteArray VehicleDatabase::netStore(VehiclePtr const& vehicle, NetCompatibilityRules rules) const {
DataStreamBuffer ds;
+ ds.setStreamCompatibilityVersion(rules);
+
ds.write(vehicle->baseConfig().getString("name"));
ds.write(vehicle->dynamicConfig());
return ds.takeData();
}
-VehiclePtr VehicleDatabase::netLoad(ByteArray const& netStore) const {
+VehiclePtr VehicleDatabase::netLoad(ByteArray const& netStore, NetCompatibilityRules rules) const {
DataStreamBuffer ds(netStore);
+ ds.setStreamCompatibilityVersion(rules);
String name = ds.read<String>();
auto dynamicConfig = ds.read<Json>();
diff --git a/source/game/StarVehicleDatabase.hpp b/source/game/StarVehicleDatabase.hpp
index ba7bf7c..ed90a7d 100644
--- a/source/game/StarVehicleDatabase.hpp
+++ b/source/game/StarVehicleDatabase.hpp
@@ -13,8 +13,8 @@ public:
VehiclePtr create(String const& vehicleName, Json const& extraConfig = Json()) const;
- ByteArray netStore(VehiclePtr const& vehicle) const;
- VehiclePtr netLoad(ByteArray const& netStore) const;
+ ByteArray netStore(VehiclePtr const& vehicle, NetCompatibilityRules rules) const;
+ VehiclePtr netLoad(ByteArray const& netStore, NetCompatibilityRules rules) const;
Json diskStore(VehiclePtr const& vehicle) const;
VehiclePtr diskLoad(Json const& diskStore) const;
diff --git a/source/game/StarWeather.cpp b/source/game/StarWeather.cpp
index 6b1c6a7..6fe1a53 100644
--- a/source/game/StarWeather.cpp
+++ b/source/game/StarWeather.cpp
@@ -55,9 +55,9 @@ void ServerWeather::setClientVisibleRegions(List<RectI> regions) {
m_clientVisibleRegions = std::move(regions);
}
-pair<ByteArray, uint64_t> ServerWeather::writeUpdate(uint64_t fromVersion) {
+pair<ByteArray, uint64_t> ServerWeather::writeUpdate(uint64_t fromVersion, NetCompatibilityRules rules) {
setNetStates();
- return m_netGroup.writeNetState(fromVersion);
+ return m_netGroup.writeNetState(fromVersion, rules);
}
void ServerWeather::update(double dt) {
@@ -263,9 +263,9 @@ void ClientWeather::setup(WorldGeometry worldGeometry, WeatherEffectsActiveQuery
m_currentTime = 0.0;
}
-void ClientWeather::readUpdate(ByteArray data) {
+void ClientWeather::readUpdate(ByteArray data, NetCompatibilityRules rules) {
if (!data.empty()) {
- m_netGroup.readNetState(std::move(data));
+ m_netGroup.readNetState(data, 0.0f, rules);
getNetStates();
}
}
diff --git a/source/game/StarWeather.hpp b/source/game/StarWeather.hpp
index 7d699ea..4bde334 100644
--- a/source/game/StarWeather.hpp
+++ b/source/game/StarWeather.hpp
@@ -29,7 +29,7 @@ public:
void setClientVisibleRegions(List<RectI> regions);
- pair<ByteArray, uint64_t> writeUpdate(uint64_t fromVersion = 0);
+ pair<ByteArray, uint64_t> writeUpdate(uint64_t fromVersion = 0, NetCompatibilityRules rules = {});
void update(double dt);
@@ -80,7 +80,7 @@ public:
void setup(WorldGeometry worldGeometry, WeatherEffectsActiveQuery weatherEffectsActiveQuery);
- void readUpdate(ByteArray data);
+ void readUpdate(ByteArray data, NetCompatibilityRules rules);
void setVisibleRegion(RectI visibleRegion);
diff --git a/source/game/StarWorldClient.cpp b/source/game/StarWorldClient.cpp
index 8abfcdf..ab5f9bf 100644
--- a/source/game/StarWorldClient.cpp
+++ b/source/game/StarWorldClient.cpp
@@ -172,7 +172,8 @@ void WorldClient::removeEntity(EntityId entityId, bool andDie) {
}
if (auto version = m_masterEntitiesNetVersion.maybeTake(entity->entityId())) {
- ByteArray finalNetState = entity->writeNetState(*version).first;
+ auto netRules = m_clientState.netCompatibilityRules();
+ ByteArray finalNetState = entity->writeNetState(*version, netRules).first;
m_outgoingPackets.append(make_shared<EntityDestroyPacket>(entity->entityId(), std::move(finalNetState), andDie));
}
@@ -771,7 +772,7 @@ void WorldClient::handleIncomingPackets(List<PacketPtr> const& packets) {
}
auto entity = entityFactory->netLoadEntity(entityCreate->entityType, entityCreate->storeData);
- entity->readNetState(entityCreate->firstNetState);
+ entity->readNetState(entityCreate->firstNetState, 0.0f, m_clientState.netCompatibilityRules());
entity->init(this, entityCreate->entityId, EntityMode::Slave);
m_entityMap->addEntity(entity);
@@ -792,13 +793,13 @@ void WorldClient::handleIncomingPackets(List<PacketPtr> const& packets) {
EntityId entityId = entity->entityId();
if (connectionForEntity(entityId) == entityUpdateSet->forConnection) {
starAssert(entity->isSlave());
- entity->readNetState(entityUpdateSet->deltas.value(entityId), interpolationLeadTime);
+ entity->readNetState(entityUpdateSet->deltas.value(entityId), interpolationLeadTime, m_clientState.netCompatibilityRules());
}
});
} else if (auto entityDestroy = as<EntityDestroyPacket>(packet)) {
if (auto entity = m_entityMap->entity(entityDestroy->entityId)) {
- entity->readNetState(entityDestroy->finalNetState, m_interpolationTracker.interpolationLeadTime());
+ entity->readNetState(entityDestroy->finalNetState, m_interpolationTracker.interpolationLeadTime(), m_clientState.netCompatibilityRules());
// Before destroying the entity, we should make sure that the entity is
// using the absolute latest data, so we disable interpolation.
@@ -913,8 +914,8 @@ void WorldClient::handleIncomingPackets(List<PacketPtr> const& packets) {
m_interpolationTracker.receiveTimeUpdate(stepUpdate->remoteTime);
} else if (auto environmentUpdatePacket = as<EnvironmentUpdatePacket>(packet)) {
- m_sky->readUpdate(environmentUpdatePacket->skyDelta);
- m_weather.readUpdate(environmentUpdatePacket->weatherDelta);
+ m_sky->readUpdate(environmentUpdatePacket->skyDelta, m_clientState.netCompatibilityRules());
+ m_weather.readUpdate(environmentUpdatePacket->weatherDelta, m_clientState.netCompatibilityRules());
} else if (auto hit = as<HitRequestPacket>(packet)) {
m_damageManager->pushRemoteHitRequest(hit->remoteHitRequest);
@@ -1233,7 +1234,7 @@ void WorldClient::update(float dt) {
queueUpdatePackets(m_entityUpdateTimer.wrapTick(dt));
- if ((!m_clientState.legacy() && m_currentStep % 3 == 0) || m_pingTime.isNothing()) {
+ if ((!m_clientState.netCompatibilityRules().isLegacy() && m_currentStep % 3 == 0) || m_pingTime.isNothing()) {
m_pingTime = Time::monotonicMilliseconds();
m_outgoingPackets.append(make_shared<PingPacket>(*m_pingTime));
}
@@ -1329,7 +1330,8 @@ void WorldClient::addEntity(EntityPtr const& entity, EntityId entityId) {
notifyEntityCreate(entity);
} else {
auto entityFactory = Root::singleton().entityFactory();
- m_outgoingPackets.append(make_shared<SpawnEntityPacket>(entity->entityType(), entityFactory->netStoreEntity(entity), entity->writeNetState().first));
+ auto netRules = m_clientState.netCompatibilityRules();
+ m_outgoingPackets.append(make_shared<SpawnEntityPacket>(entity->entityType(), entityFactory->netStoreEntity(entity), entity->writeNetState(0, netRules).first));
}
}
@@ -1460,9 +1462,10 @@ void WorldClient::queueUpdatePackets(bool sendEntityUpdates) {
if (sendEntityUpdates) {
auto entityUpdateSet = make_shared<EntityUpdateSetPacket>();
entityUpdateSet->forConnection = *m_clientId;
+ auto netRules = m_clientState.netCompatibilityRules();
m_entityMap->forAllEntities([&](EntityPtr const& entity) {
if (auto version = m_masterEntitiesNetVersion.ptr(entity->entityId())) {
- auto updateAndVersion = entity->writeNetState(*version);
+ auto updateAndVersion = entity->writeNetState(*version, netRules);
if (!updateAndVersion.first.empty())
entityUpdateSet->deltas[entity->entityId()] = std::move(updateAndVersion.first);
*version = updateAndVersion.second;
@@ -1793,13 +1796,13 @@ void WorldClient::initWorld(WorldStartPacket const& startPacket) {
centerClientWindowOnPlayer();
m_sky = make_shared<Sky>();
- m_sky->readUpdate(startPacket.skyData);
+ m_sky->readUpdate(startPacket.skyData, m_clientState.netCompatibilityRules());
m_weather.setup(m_geometry, [this](Vec2I const& pos) {
auto const& tile = m_tileArray->tile(pos);
return !isRealMaterial(tile.background) && !isSolidColliding(tile.getCollision());
});
- m_weather.readUpdate(startPacket.weatherData);
+ m_weather.readUpdate(startPacket.weatherData, m_clientState.netCompatibilityRules());
m_lightingCalculator.setMonochrome(Root::singleton().configuration()->get("monochromeLighting").toBool());
m_lightingCalculator.setParameters(assets->json("/lighting.config:lighting"));
@@ -1874,7 +1877,8 @@ void WorldClient::tryGiveMainPlayerItem(ItemPtr item, bool silent) {
void WorldClient::notifyEntityCreate(EntityPtr const& entity) {
if (entity->isMaster() && !m_masterEntitiesNetVersion.contains(entity->entityId())) {
// Server was unaware of this entity until now
- auto firstNetState = entity->writeNetState();
+ auto netRules = m_clientState.netCompatibilityRules();
+ auto firstNetState = entity->writeNetState(0, netRules);
m_masterEntitiesNetVersion[entity->entityId()] = firstNetState.second;
m_outgoingPackets.append(make_shared<EntityCreatePacket>(entity->entityType(),
Root::singleton().entityFactory()->netStoreEntity(entity), std::move(firstNetState.first), entity->entityId()));
diff --git a/source/game/StarWorldClientState.cpp b/source/game/StarWorldClientState.cpp
index 21552cd..4fbefc9 100644
--- a/source/game/StarWorldClientState.cpp
+++ b/source/game/StarWorldClientState.cpp
@@ -22,8 +22,6 @@ WorldClientState::WorldClientState() {
m_netGroup.addNetElement(&m_playerId);
m_netGroup.addNetElement(&m_clientPresenceEntities);
-
- m_legacy = false;
}
RectI WorldClientState::window() const {
@@ -81,20 +79,20 @@ List<RectI> WorldClientState::monitoringRegions(function<Maybe<RectI>(EntityId)>
ByteArray WorldClientState::writeDelta() {
ByteArray delta;
- tie(delta, m_netVersion) = m_netGroup.writeNetState(m_netVersion);
+ tie(delta, m_netVersion) = m_netGroup.writeNetState(m_netVersion, m_netCompatibilityRules);
return delta;
}
void WorldClientState::readDelta(ByteArray delta) {
- m_netGroup.readNetState(std::move(delta));
+ m_netGroup.readNetState(std::move(delta), 0.0f, m_netCompatibilityRules);
}
-void WorldClientState::setLegacy(bool legacy) {
- m_legacy = legacy;
+void WorldClientState::setNetCompatibilityRules(NetCompatibilityRules netCompatibilityRules) {
+ m_netCompatibilityRules = netCompatibilityRules;
}
-bool WorldClientState::legacy() const {
- return m_legacy;
+NetCompatibilityRules WorldClientState::netCompatibilityRules() const {
+ return m_netCompatibilityRules;
}
void WorldClientState::reset() {
diff --git a/source/game/StarWorldClientState.hpp b/source/game/StarWorldClientState.hpp
index 920fef4..746b7c5 100644
--- a/source/game/StarWorldClientState.hpp
+++ b/source/game/StarWorldClientState.hpp
@@ -36,9 +36,8 @@ public:
ByteArray writeDelta();
void readDelta(ByteArray delta);
- // Whether the client is connected to a legacy server.
- void setLegacy(bool legacy);
- bool legacy() const;
+ void setNetCompatibilityRules(NetCompatibilityRules netCompatibilityRules);
+ NetCompatibilityRules netCompatibilityRules() const;
void reset();
@@ -57,7 +56,7 @@ private:
NetElementInt m_playerId;
NetElementData<List<EntityId>> m_clientPresenceEntities;
- bool m_legacy;
+ NetCompatibilityRules m_netCompatibilityRules;
};
}
diff --git a/source/game/StarWorldServer.cpp b/source/game/StarWorldServer.cpp
index a78025c..fbc23a9 100644
--- a/source/game/StarWorldServer.cpp
+++ b/source/game/StarWorldServer.cpp
@@ -209,7 +209,7 @@ bool WorldServer::spawnTargetValid(SpawnTarget const& spawnTarget) const {
return true;
}
-bool WorldServer::addClient(ConnectionId clientId, SpawnTarget const& spawnTarget, bool isLocal, bool isAdmin) {
+bool WorldServer::addClient(ConnectionId clientId, SpawnTarget const& spawnTarget, bool isLocal, bool isAdmin, NetCompatibilityRules netRules) {
if (m_clientInfo.contains(clientId))
return false;
@@ -246,6 +246,7 @@ bool WorldServer::addClient(ConnectionId clientId, SpawnTarget const& spawnTarge
auto& clientInfo = m_clientInfo.add(clientId, make_shared<ClientInfo>(clientId, tracker));
clientInfo->local = isLocal;
clientInfo->admin = isAdmin;
+ clientInfo->clientState.setNetCompatibilityRules(netRules);
auto worldStartPacket = make_shared<WorldStartPacket>();
auto& templateData = worldStartPacket->templateData = m_worldTemplate->store();
@@ -254,8 +255,8 @@ bool WorldServer::addClient(ConnectionId clientId, SpawnTarget const& spawnTarge
&& Root::singletonPtr()->configuration()->getPath("compatibility.customDungeonWorld").optBool().value(false))
worldStartPacket->templateData = worldStartPacket->templateData.setPath("worldParameters.primaryDungeon", "testarena");
- tie(worldStartPacket->skyData, clientInfo->skyNetVersion) = m_sky->writeUpdate();
- tie(worldStartPacket->weatherData, clientInfo->weatherNetVersion) = m_weather.writeUpdate();
+ tie(worldStartPacket->skyData, clientInfo->skyNetVersion) = m_sky->writeUpdate(0, netRules);
+ tie(worldStartPacket->weatherData, clientInfo->weatherNetVersion) = m_weather.writeUpdate(0, netRules);
worldStartPacket->playerStart = playerStart;
worldStartPacket->playerRespawn = m_playerStart;
worldStartPacket->respawnInWorld = m_respawnInWorld;
@@ -381,7 +382,7 @@ void WorldServer::handleIncomingPackets(ConnectionId clientId, List<PacketPtr> c
} else if (auto sepacket = as<SpawnEntityPacket>(packet)) {
auto entity = entityFactory->netLoadEntity(sepacket->entityType, std::move(sepacket->storeData));
- entity->readNetState(std::move(sepacket->firstNetState));
+ entity->readNetState(std::move(sepacket->firstNetState), 0.0f, clientInfo->clientState.netCompatibilityRules());
addEntity(std::move(entity));
} else if (auto rdpacket = as<RequestDropPacket>(packet)) {
@@ -434,7 +435,7 @@ void WorldServer::handleIncomingPackets(ConnectionId clientId, List<PacketPtr> c
}
auto entity = entityFactory->netLoadEntity(entityCreate->entityType, entityCreate->storeData);
- entity->readNetState(entityCreate->firstNetState);
+ entity->readNetState(entityCreate->firstNetState, 0.0f, clientInfo->clientState.netCompatibilityRules());
entity->init(this, entityCreate->entityId, EntityMode::Slave);
m_entityMap->addEntity(entity);
@@ -448,14 +449,14 @@ void WorldServer::handleIncomingPackets(ConnectionId clientId, List<PacketPtr> c
EntityId entityId = entity->entityId();
if (connectionForEntity(entityId) == clientId) {
starAssert(entity->isSlave());
- entity->readNetState(entityUpdateSet->deltas.value(entityId), interpolationLeadTime);
+ entity->readNetState(entityUpdateSet->deltas.value(entityId), interpolationLeadTime, clientInfo->clientState.netCompatibilityRules());
}
});
clientInfo->pendingForward = true;
} else if (auto entityDestroy = as<EntityDestroyPacket>(packet)) {
if (auto entity = m_entityMap->entity(entityDestroy->entityId)) {
- entity->readNetState(entityDestroy->finalNetState, clientInfo->interpolationTracker.interpolationLeadTime());
+ entity->readNetState(entityDestroy->finalNetState, clientInfo->interpolationTracker.interpolationLeadTime(), clientInfo->clientState.netCompatibilityRules());
// Before destroying the entity, we should make sure that the entity is
// using the absolute latest data, so we disable interpolation.
entity->disableInterpolation();
@@ -1789,10 +1790,10 @@ void WorldServer::queueUpdatePackets(ConnectionId clientId, bool sendRemoteUpdat
if (shouldRunThisStep("environmentUpdate")) {
ByteArray skyDelta;
- tie(skyDelta, clientInfo->skyNetVersion) = m_sky->writeUpdate(clientInfo->skyNetVersion);
+ tie(skyDelta, clientInfo->skyNetVersion) = m_sky->writeUpdate(clientInfo->skyNetVersion, clientInfo->clientState.netCompatibilityRules());
ByteArray weatherDelta;
- tie(weatherDelta, clientInfo->weatherNetVersion) = m_weather.writeUpdate(clientInfo->weatherNetVersion);
+ tie(weatherDelta, clientInfo->weatherNetVersion) = m_weather.writeUpdate(clientInfo->weatherNetVersion, clientInfo->clientState.netCompatibilityRules());
if (!skyDelta.empty() || !weatherDelta.empty())
clientInfo->outgoingPackets.append(make_shared<EnvironmentUpdatePacket>(std::move(skyDelta), std::move(weatherDelta)));
@@ -1866,12 +1867,14 @@ void WorldServer::queueUpdatePackets(ConnectionId clientId, bool sendRemoteUpdat
EntityId entityId = monitoredEntity->entityId();
ConnectionId connectionId = connectionForEntity(entityId);
if (connectionId != clientId) {
+ auto netRules = clientInfo->clientState.netCompatibilityRules();
if (auto version = clientInfo->clientSlavesNetVersion.ptr(entityId)) {
if (auto updateSetPacket = updateSetPackets.value(connectionId)) {
auto pair = make_pair(entityId, *version);
- auto i = m_netStateCache.find(pair);
- if (i == m_netStateCache.end())
- i = m_netStateCache.insert(pair, monitoredEntity->writeNetState(*version)).first;
+ auto& cache = m_netStateCache[netRules];
+ auto i = cache.find(pair);
+ if (i == cache.end())
+ i = cache.insert(pair, monitoredEntity->writeNetState(*version, netRules)).first;
const auto& netState = i->second;
if (!netState.first.empty())
updateSetPacket->deltas[entityId] = netState.first;
@@ -1879,7 +1882,7 @@ void WorldServer::queueUpdatePackets(ConnectionId clientId, bool sendRemoteUpdat
}
} else if (!monitoredEntity->masterOnly()) {
// Client was unaware of this entity until now
- auto firstUpdate = monitoredEntity->writeNetState();
+ auto firstUpdate = monitoredEntity->writeNetState(0, netRules);
clientInfo->clientSlavesNetVersion.add(entityId, firstUpdate.second);
clientInfo->outgoingPackets.append(make_shared<EntityCreatePacket>(monitoredEntity->entityType(),
entityFactory->netStoreEntity(monitoredEntity), std::move(firstUpdate.first), entityId));
@@ -2075,7 +2078,8 @@ void WorldServer::removeEntity(EntityId entityId, bool andDie) {
for (auto const& pair : m_clientInfo) {
auto& clientInfo = pair.second;
if (auto version = clientInfo->clientSlavesNetVersion.maybeTake(entity->entityId())) {
- ByteArray finalDelta = entity->writeNetState(*version).first;
+ auto netRules = clientInfo->clientState.netCompatibilityRules();
+ ByteArray finalDelta = entity->writeNetState(*version, netRules).first;
clientInfo->outgoingPackets.append(make_shared<EntityDestroyPacket>(entity->entityId(), std::move(finalDelta), andDie));
}
}
diff --git a/source/game/StarWorldServer.hpp b/source/game/StarWorldServer.hpp
index 9e319e5..ea650ac 100644
--- a/source/game/StarWorldServer.hpp
+++ b/source/game/StarWorldServer.hpp
@@ -87,7 +87,7 @@ public:
// Returns false if the client id already exists, or the spawn target is
// invalid.
- bool addClient(ConnectionId clientId, SpawnTarget const& spawnTarget, bool isLocal, bool isAdmin = false);
+ bool addClient(ConnectionId clientId, SpawnTarget const& spawnTarget, bool isLocal, bool isAdmin = false, NetCompatibilityRules netRules = {});
// Removes client, sends the WorldStopPacket, and returns any pending packets
// for that client
@@ -374,7 +374,7 @@ private:
CollisionGenerator m_collisionGenerator;
List<CollisionBlock> m_workingCollisionBlocks;
- HashMap<pair<EntityId, uint64_t>, pair<ByteArray, uint64_t>> m_netStateCache;
+ HashMap<NetCompatibilityRules, HashMap<pair<EntityId, uint64_t>, pair<ByteArray, uint64_t>>> m_netStateCache;
OrderedHashMap<ConnectionId, shared_ptr<ClientInfo>> m_clientInfo;
GameTimer m_entityUpdateTimer;
diff --git a/source/game/StarWorldServerThread.cpp b/source/game/StarWorldServerThread.cpp
index 1e1f51a..45959ea 100644
--- a/source/game/StarWorldServerThread.cpp
+++ b/source/game/StarWorldServerThread.cpp
@@ -66,10 +66,10 @@ bool WorldServerThread::spawnTargetValid(SpawnTarget const& spawnTarget) {
}
}
-bool WorldServerThread::addClient(ConnectionId clientId, SpawnTarget const& spawnTarget, bool isLocal, bool isAdmin) {
+bool WorldServerThread::addClient(ConnectionId clientId, SpawnTarget const& spawnTarget, bool isLocal, bool isAdmin, NetCompatibilityRules netRules) {
try {
RecursiveMutexLocker locker(m_mutex);
- if (m_worldServer->addClient(clientId, spawnTarget, isLocal, isAdmin)) {
+ if (m_worldServer->addClient(clientId, spawnTarget, isLocal, isAdmin, netRules)) {
m_clients.add(clientId);
return true;
}
diff --git a/source/game/StarWorldServerThread.hpp b/source/game/StarWorldServerThread.hpp
index ae1e666..3223912 100644
--- a/source/game/StarWorldServerThread.hpp
+++ b/source/game/StarWorldServerThread.hpp
@@ -38,7 +38,7 @@ public:
bool spawnTargetValid(SpawnTarget const& spawnTarget);
- bool addClient(ConnectionId clientId, SpawnTarget const& spawnTarget, bool isLocal, bool isAdmin = false);
+ bool addClient(ConnectionId clientId, SpawnTarget const& spawnTarget, bool isLocal, bool isAdmin = false, NetCompatibilityRules netRules = {});
// Returns final outgoing packets
List<PacketPtr> removeClient(ConnectionId clientId);
diff --git a/source/game/interfaces/StarActivatableItem.hpp b/source/game/interfaces/StarActivatableItem.hpp
index c8802c0..6501d0b 100644
--- a/source/game/interfaces/StarActivatableItem.hpp
+++ b/source/game/interfaces/StarActivatableItem.hpp
@@ -1,5 +1,4 @@
-#ifndef STAR_ACTIVATABLE_ITEM_HPP
-#define STAR_ACTIVATABLE_ITEM_HPP
+#pragma once
#include "StarConfig.hpp"
@@ -16,6 +15,4 @@ public:
virtual void activate() = 0;
};
-}
-
-#endif
+} \ No newline at end of file
diff --git a/source/game/interfaces/StarEntity.cpp b/source/game/interfaces/StarEntity.cpp
index cbc968f..150fcbd 100644
--- a/source/game/interfaces/StarEntity.cpp
+++ b/source/game/interfaces/StarEntity.cpp
@@ -1,5 +1,6 @@
#include "StarEntity.hpp"
#include "StarDamageManager.hpp"
+#include "StarNetCompatibility.hpp"
namespace Star {
@@ -43,11 +44,11 @@ void Entity::uninit() {
m_entityId = NullEntityId;
}
-pair<ByteArray, uint64_t> Entity::writeNetState(uint64_t) {
+pair<ByteArray, uint64_t> Entity::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) {
return {ByteArray(), 0};
}
-void Entity::readNetState(ByteArray, float) {}
+void Entity::readNetState(ByteArray data, float interpolationTime, NetCompatibilityRules rules) {}
void Entity::enableInterpolation(float) {}
diff --git a/source/game/interfaces/StarEntity.hpp b/source/game/interfaces/StarEntity.hpp
index 7da2a41..56bd5ac 100644
--- a/source/game/interfaces/StarEntity.hpp
+++ b/source/game/interfaces/StarEntity.hpp
@@ -3,6 +3,7 @@
#include "StarCasting.hpp"
#include "StarDamage.hpp"
#include "StarLightSource.hpp"
+#include "StarDataStream.hpp"
namespace Star {
@@ -63,11 +64,11 @@ public:
// uninitalized. Should return the delta to be written to the slave, along
// with the version to pass into writeDeltaState on the next call. The first
// delta written to a slave entity will always be the delta starting with 0.
- virtual pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0);
+ virtual pair<ByteArray, uint64_t> writeNetState(uint64_t fromVersion = 0, NetCompatibilityRules rules = {});
// Will be called with deltas written by writeDeltaState, including if the
// delta is empty. interpolationTime will be provided if interpolation is
// enabled.
- virtual void readNetState(ByteArray data, float interpolationTime = 0.0);
+ virtual void readNetState(ByteArray data, float interpolationTime = 0.0f, NetCompatibilityRules rules = {});
virtual void enableInterpolation(float extrapolationHint);
virtual void disableInterpolation();
diff --git a/source/game/scripting/StarRootLuaBindings.cpp b/source/game/scripting/StarRootLuaBindings.cpp
index 50d18e8..0ce7866 100644
--- a/source/game/scripting/StarRootLuaBindings.cpp
+++ b/source/game/scripting/StarRootLuaBindings.cpp
@@ -237,14 +237,14 @@ LuaCallbacks LuaBindings::makeRootCallbacks() {
callbacks.registerCallback("getConfigurationPath", [root](String const& path) -> Json {
- if (path.beginsWith("title"))
+ if (path.empty() || path.beginsWith("title"))
throw ConfigurationException(strf("cannot get {}", path));
else
return root->configuration()->getPath(path);
});
callbacks.registerCallback("setConfigurationPath", [root](String const& path, Json const& value) {
- if (path.beginsWith("safeScripts"))
+ if (path.empty() || path.beginsWith("safeScripts"))
throw ConfigurationException(strf("cannot set {}", path));
else
root->configuration()->setPath(path, value);
diff --git a/source/windowing/StarButtonWidget.cpp b/source/windowing/StarButtonWidget.cpp
index 258ac55..0412ae5 100644
--- a/source/windowing/StarButtonWidget.cpp
+++ b/source/windowing/StarButtonWidget.cpp
@@ -165,6 +165,12 @@ void ButtonWidget::mouseOut() {
void ButtonWidget::mouseReturnStillDown() {
Widget::mouseReturnStillDown();
+ if (!isPressed()) {
+ auto assets = Root::singleton().assets();
+ auto sound = Random::randValueFrom(m_clickSounds, "");
+ if (!sound.empty())
+ context()->playAudio(sound);
+ }
m_hovered = true;
m_pressed = true;
}
diff --git a/source/windowing/StarPaneManager.cpp b/source/windowing/StarPaneManager.cpp
index 4f4cee7..d8a727a 100644
--- a/source/windowing/StarPaneManager.cpp
+++ b/source/windowing/StarPaneManager.cpp
@@ -17,11 +17,9 @@ EnumMap<PaneLayer> const PaneLayerNames{
PaneManager::PaneManager()
: m_context(GuiContext::singletonPtr()), m_prevInterfaceScale(1) {
auto assets = Root::singleton().assets();
- m_tooltipMouseoverTime = assets->json("/panes.config:tooltipMouseoverTime").toFloat();
m_tooltipMouseoverRadius = assets->json("/panes.config:tooltipMouseoverRadius").toFloat();
m_tooltipMouseOffset = jsonToVec2I(assets->json("/panes.config:tooltipMouseoverOffset"));
-
- m_tooltipShowTimer = m_tooltipMouseoverTime;
+ m_tooltipShowTimer = GameTimer(assets->json("/panes.config:tooltipMouseoverTime").toFloat());
}
void PaneManager::displayPane(PaneLayer paneLayer, PanePtr const& pane, DismissCallback onDismiss) {
@@ -124,7 +122,9 @@ PanePtr PaneManager::getPaneAt(Set<PaneLayer> const& paneLayers, Vec2I const& po
PanePtr PaneManager::getPaneAt(Vec2I const& position) const {
for (auto const& layerPair : m_displayedPanes) {
for (auto const& panePair : layerPair.second) {
- if (panePair.first->inWindow(position) && panePair.first->active())
+ if (panePair.first != m_activeTooltip
+ && panePair.first->inWindow(position)
+ && panePair.first->active())
return panePair.first;
}
}
@@ -184,13 +184,13 @@ bool PaneManager::sendInputEvent(InputEvent const& event) {
}
}
- if (event.is<MouseButtonDownEvent>() || vmag(m_tooltipInitialPosition - m_tooltipLastMousePos) > m_tooltipMouseoverRadius) {
- m_tooltipShowTimer = m_tooltipMouseoverTime;
+ if (event.is<MouseButtonDownEvent>()) {
+ m_tooltipShowTimer.reset();
if (m_activeTooltip) {
dismiss(m_activeTooltip);
m_activeTooltip.reset();
m_tooltipParentPane.reset();
- m_tooltipShowTimer = m_tooltipMouseoverTime;
+ m_tooltipShowTimer.reset();
}
}
@@ -270,37 +270,46 @@ void PaneManager::render() {
}
void PaneManager::update(float dt) {
- m_tooltipShowTimer -= GlobalTimestep;
- if (m_tooltipShowTimer < 0 && !m_activeTooltip) {
- if (auto parentPane = getPaneAt(m_tooltipLastMousePos)) {
- if (auto tooltip = parentPane->createTooltip(m_tooltipLastMousePos)) {
+ auto newTooltipParentPane = getPaneAt(m_tooltipLastMousePos);
+
+ bool updateTooltip = m_tooltipShowTimer.tick(dt) || (m_activeTooltip && (
+ vmag(m_tooltipInitialPosition - m_tooltipLastMousePos) > m_tooltipMouseoverRadius
+ || m_tooltipParentPane != newTooltipParentPane
+ || !m_tooltipParentPane->inWindow(m_tooltipLastMousePos)));
+
+ if (updateTooltip) {
+ if (m_activeTooltip) {
+ dismiss(m_activeTooltip);
+ m_activeTooltip.reset();
+ m_tooltipParentPane.reset();
+ }
+
+ m_tooltipShowTimer.reset();
+ if (newTooltipParentPane) {
+ if (auto tooltip = newTooltipParentPane->createTooltip(m_tooltipLastMousePos)) {
m_activeTooltip = std::move(tooltip);
- m_tooltipParentPane = std::move(parentPane);
+ m_tooltipParentPane = std::move(newTooltipParentPane);
m_tooltipInitialPosition = m_tooltipLastMousePos;
displayPane(PaneLayer::Tooltip, m_activeTooltip);
+ }
+ }
+ }
- Vec2I offsetDirection = Vec2I::filled(1);
- Vec2I offsetAdjust = Vec2I();
+ if (m_activeTooltip) {
+ Vec2I offsetDirection = Vec2I::filled(1);
+ Vec2I offsetAdjust = Vec2I();
- if (m_tooltipLastMousePos[0] + m_tooltipMouseOffset[0] + m_activeTooltip->size()[0] > (int)m_context->windowWidth() / m_context->interfaceScale()) {
- offsetDirection[0] = -1;
- offsetAdjust[0] = -m_activeTooltip->size()[0];
- }
+ if (m_tooltipLastMousePos[0] + m_tooltipMouseOffset[0] + m_activeTooltip->size()[0] > (int)m_context->windowWidth() / m_context->interfaceScale()) {
+ offsetDirection[0] = -1;
+ offsetAdjust[0] = -m_activeTooltip->size()[0];
+ }
- if (m_tooltipLastMousePos[1] + m_tooltipMouseOffset[1] - m_activeTooltip->size()[1] < 0)
- offsetDirection[1] = -1;
- else
- offsetAdjust[1] = -m_activeTooltip->size()[1];
+ if (m_tooltipLastMousePos[1] + m_tooltipMouseOffset[1] - m_activeTooltip->size()[1] < 0)
+ offsetDirection[1] = -1;
+ else
+ offsetAdjust[1] = -m_activeTooltip->size()[1];
- m_activeTooltip->setPosition(m_tooltipLastMousePos + (offsetAdjust + m_tooltipMouseOffset.piecewiseMultiply(offsetDirection)));
- } else {
- m_tooltipShowTimer = m_tooltipMouseoverTime;
- }
- }
- } else if (m_activeTooltip && !m_tooltipParentPane->isDisplayed()) {
- dismiss(m_activeTooltip);
- m_activeTooltip.reset();
- m_tooltipParentPane.reset();
+ m_activeTooltip->setPosition(m_tooltipLastMousePos + (offsetAdjust + m_tooltipMouseOffset.piecewiseMultiply(offsetDirection)));
}
for (auto const& layerPair : m_displayedPanes) {
diff --git a/source/windowing/StarPaneManager.hpp b/source/windowing/StarPaneManager.hpp
index ffa46c7..3d507f3 100644
--- a/source/windowing/StarPaneManager.hpp
+++ b/source/windowing/StarPaneManager.hpp
@@ -3,6 +3,7 @@
#include "StarPane.hpp"
#include "StarOrderedMap.hpp"
#include "StarBiMap.hpp"
+#include "StarGameTimers.hpp"
namespace Star {
@@ -92,11 +93,9 @@ private:
WidgetPtr m_backgroundWidget;
- float m_tooltipMouseoverTime;
float m_tooltipMouseoverRadius;
Vec2I m_tooltipMouseOffset;
-
- float m_tooltipShowTimer;
+ GameTimer m_tooltipShowTimer;
Vec2I m_tooltipLastMousePos;
Vec2I m_tooltipInitialPosition;
PanePtr m_activeTooltip;