diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..60097c8 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,231 @@ +name: Build + +on: + workflow_dispatch: + + pull_request: + branches: + - "*" + +jobs: + build_windows: + name: Build OpenStarbound Windows x64 + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: 'recursive' + + - name: Install CMake & Ninja + uses: lukka/get-cmake@latest + with: + cmakeVersion: 3.29.2 + + - name: sccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + variant: sccache + key: ${{ github.job }}-${{ runner.os }} + max-size: 1000M + + - uses: ilammy/msvc-dev-cmd@v1 + + - name: vcpkg + uses: lukka/run-vcpkg@v11 + id: runvcpkg + with: + vcpkgJsonGlob: '**/source/vcpkg.json' + vcpkgConfigurationJsonGlob: '**/source/vcpkg-configuration.json' + + - name: Run CMake + uses: lukka/run-cmake@v10 + with: + cmakeListsTxtPath: '${{ github.workspace }}/source/CMakeLists.txt' + configurePreset: 'windows-release' + buildPreset: 'windows-release' + testPreset: 'windows-release' + + - name: Run Post-Build Task + working-directory: ${{ github.workspace }} + run: scripts\ci\windows\post_build.bat + + - name: Assemble Files + working-directory: ${{ github.workspace }} + run: scripts\ci\windows\assemble.bat + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: OpenStarbound-Windows-All-DevOnly + path: dist/* + + - name: Upload Client + uses: actions/upload-artifact@v4 + with: + name: OpenStarbound-Windows-Client + path: client_distribution/* + + - name: Upload Server + uses: actions/upload-artifact@v4 + with: + name: OpenStarbound-Windows-Server + path: server_distribution/* + + - name: Create Installer + working-directory: ${{ github.workspace }} + run: | + & "C:\Program Files (x86)\Inno Setup 6\iscc.exe" /Oinstaller scripts\inno\setup.iss + + - name: Upload Installer + uses: actions/upload-artifact@v4 + with: + name: OpenStarbound-Windows-Installer + path: installer/* + + build_linux: + name: Build OpenStarbound Linux x86_64 + runs-on: ubuntu-20.04 + + steps: + - name: Install Packages + run: | + sudo apt-get update + sudo apt-get install -y pkg-config libxmu-dev libxi-dev libgl-dev libglu1-mesa-dev libsdl2-dev + + - name: Install CMake & Ninja + uses: lukka/get-cmake@latest + with: + cmakeVersion: 3.29.2 + + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + + - name: sccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + variant: sccache + key: ${{ github.job }}-${{ runner.os }} + max-size: 250M + + - name: vcpkg + uses: lukka/run-vcpkg@v11 + id: runvcpkg + with: + vcpkgJsonGlob: '**/source/vcpkg.json' + vcpkgConfigurationJsonGlob: '**/source/vcpkg-configuration.json' + + - name: Run CMake + uses: lukka/run-cmake@v10 + with: + cmakeListsTxtPath: '${{ github.workspace }}/source/CMakeLists.txt' + configurePreset: 'linux-release' + buildPreset: 'linux-release' + testPreset: 'linux-release' + + - name: Assemble Files + working-directory: ${{ github.workspace }} + run: scripts/ci/linux/assemble.sh + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: OpenStarbound-Linux + path: dist.tar + + - name: Upload Client Files + uses: actions/upload-artifact@v4 + with: + name: OpenStarbound-Linux-Client + path: client.tar + + - name: Upload Server Files + uses: actions/upload-artifact@v4 + with: + name: OpenStarbound-Linux-Server + path: server.tar + + build-mac-intel: + name: Build OpenStarbound macOS x86_64 + runs-on: macos-13 + + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + + - name: Install CMake & Ninja + uses: lukka/get-cmake@latest + with: + cmakeVersion: 3.29.0 + + - name: sccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + variant: sccache + key: ${{ github.job }}-Intel-${{ runner.os }} + max-size: 250M + + - name: vcpkg + uses: lukka/run-vcpkg@v11 + id: runvcpkg + with: + vcpkgJsonGlob: '**/source/vcpkg.json' + vcpkgConfigurationJsonGlob: '**/source/vcpkg-configuration.json' + + - name: Run CMake + uses: lukka/run-cmake@v10 + with: + cmakeListsTxtPath: '${{ github.workspace }}/source/CMakeLists.txt' + configurePreset: 'macos-release' + buildPreset: 'macos-release' + testPreset: 'macos-release' + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: OpenStarbound-macOS-Intel + path: dist/* + + build-mac-arm: + name: Build OpenStarbound macOS arm64 + runs-on: macos-14 + + steps: + - uses: actions/checkout@v4 + with: + submodules: 'recursive' + + - name: Install CMake & Ninja + uses: lukka/get-cmake@latest + with: + cmakeVersion: 3.29.2 + + - name: sccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + variant: sccache + key: ${{ github.job }}-ARM-${{ runner.os }} + max-size: 250M + + - name: vcpkg + uses: lukka/run-vcpkg@v11 + id: runvcpkg + with: + vcpkgJsonGlob: '**/source/vcpkg.json' + vcpkgConfigurationJsonGlob: '**/source/vcpkg-configuration.json' + + - name: Run CMake + uses: lukka/run-cmake@v10 + with: + cmakeListsTxtPath: '${{ github.workspace }}/source/CMakeLists.txt' + configurePreset: 'macos-arm-release' + buildPreset: 'macos-arm-release' + testPreset: 'macos-arm-release' + + - name: Upload Artifacts + uses: actions/upload-artifact@v4 + with: + name: OpenStarbound-macOS-Silicon + path: dist/* \ No newline at end of file diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml deleted file mode 100644 index 307b30f..0000000 --- a/.github/workflows/build_linux.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Ubuntu Linux - -on: - push: - branches: - - "*" - tags: - - "*" - - pull_request: - branches: - - "*" - -jobs: - build: - name: OpenStarbound Linux x86_64 - runs-on: ubuntu-20.04 - - steps: - - name: Install Packages - run: | - sudo apt-get update - sudo apt-get install -y pkg-config libxmu-dev libxi-dev libgl-dev libglu1-mesa-dev libsdl2-dev - - - name: Install CMake & Ninja - uses: lukka/get-cmake@latest - with: - cmakeVersion: 3.29.2 - - - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - - name: sccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - variant: sccache - key: ${{ github.job }}-${{ runner.os }} - max-size: 250M - - - name: vcpkg - uses: lukka/run-vcpkg@v11 - id: runvcpkg - with: - vcpkgJsonGlob: '**/source/vcpkg.json' - vcpkgConfigurationJsonGlob: '**/source/vcpkg-configuration.json' - - - name: Run CMake - uses: lukka/run-cmake@v10 - with: - cmakeListsTxtPath: '${{ github.workspace }}/source/CMakeLists.txt' - configurePreset: 'linux-release' - buildPreset: 'linux-release' - testPreset: 'linux-release' - - - name: Assemble Files - working-directory: ${{ github.workspace }} - run: scripts/ci/linux/assemble.sh - - - name: Upload Artifacts - uses: actions/upload-artifact@v4 - with: - name: OpenStarbound-Linux - path: dist.tar - - - name: Upload Client Files - uses: actions/upload-artifact@v4 - with: - name: OpenStarbound-Linux-Client - path: client.tar - - - name: Upload Server Files - uses: actions/upload-artifact@v4 - with: - name: OpenStarbound-Linux-Server - path: server.tar \ No newline at end of file diff --git a/.github/workflows/build_macos.yml b/.github/workflows/build_macos.yml deleted file mode 100644 index 1cfd12c..0000000 --- a/.github/workflows/build_macos.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: macOS - -on: - push: - branches: - - "*" - tags: - - "*" - - pull_request: - branches: - - "*" - -jobs: - build-intel: - name: OpenStarbound macOS x86_64 - runs-on: macos-13 - - steps: - - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - - name: Install CMake & Ninja - uses: lukka/get-cmake@latest - with: - cmakeVersion: 3.29.0 - - - name: sccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - variant: sccache - key: ${{ github.job }}-Intel-${{ runner.os }} - max-size: 250M - - - name: vcpkg - uses: lukka/run-vcpkg@v11 - id: runvcpkg - with: - vcpkgJsonGlob: '**/source/vcpkg.json' - vcpkgConfigurationJsonGlob: '**/source/vcpkg-configuration.json' - - - name: Run CMake - uses: lukka/run-cmake@v10 - with: - cmakeListsTxtPath: '${{ github.workspace }}/source/CMakeLists.txt' - configurePreset: 'macos-release' - buildPreset: 'macos-release' - testPreset: 'macos-release' - - - name: Upload Artifacts - uses: actions/upload-artifact@v4 - with: - name: OpenStarbound-macOS-Intel - path: dist/* - - build-arm: - name: OpenStarbound macOS arm64 - runs-on: macos-14 - - steps: - - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - - name: Install CMake & Ninja - uses: lukka/get-cmake@latest - with: - cmakeVersion: 3.29.2 - - - name: sccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - variant: sccache - key: ${{ github.job }}-ARM-${{ runner.os }} - max-size: 250M - - - name: vcpkg - uses: lukka/run-vcpkg@v11 - id: runvcpkg - with: - vcpkgJsonGlob: '**/source/vcpkg.json' - vcpkgConfigurationJsonGlob: '**/source/vcpkg-configuration.json' - - - name: Run CMake - uses: lukka/run-cmake@v10 - with: - cmakeListsTxtPath: '${{ github.workspace }}/source/CMakeLists.txt' - configurePreset: 'macos-arm-release' - buildPreset: 'macos-arm-release' - testPreset: 'macos-arm-release' - - - name: Upload Artifacts - uses: actions/upload-artifact@v4 - with: - name: OpenStarbound-macOS-Silicon - path: dist/* \ No newline at end of file diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml deleted file mode 100644 index d3ee36b..0000000 --- a/.github/workflows/build_windows.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: Windows - -on: - push: - branches: - - "*" - tags: - - "*" - - pull_request: - branches: - - "*" - -jobs: - build: - name: Build OpenStarbound Windows x64 - runs-on: windows-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: 'recursive' - - - name: Install CMake & Ninja - uses: lukka/get-cmake@latest - with: - cmakeVersion: 3.29.2 - - - name: sccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - variant: sccache - key: ${{ github.job }}-${{ runner.os }} - max-size: 1000M - - - uses: ilammy/msvc-dev-cmd@v1 - - - name: vcpkg - uses: lukka/run-vcpkg@v11 - id: runvcpkg - with: - vcpkgJsonGlob: '**/source/vcpkg.json' - vcpkgConfigurationJsonGlob: '**/source/vcpkg-configuration.json' - - - name: Run CMake - uses: lukka/run-cmake@v10 - with: - cmakeListsTxtPath: '${{ github.workspace }}/source/CMakeLists.txt' - configurePreset: 'windows-release' - buildPreset: 'windows-release' - testPreset: 'windows-release' - - - name: Run Post-Build Task - working-directory: ${{ github.workspace }} - run: scripts\ci\windows\post_build.bat - - - name: Assemble Files - working-directory: ${{ github.workspace }} - run: scripts\ci\windows\assemble.bat - - - name: Upload Artifacts - uses: actions/upload-artifact@v4 - with: - name: OpenStarbound-Windows-All-DevOnly - path: dist/* - - - name: Upload Client - uses: actions/upload-artifact@v4 - with: - name: OpenStarbound-Windows-Client - path: client_distribution/* - - - name: Upload Server - uses: actions/upload-artifact@v4 - with: - name: OpenStarbound-Windows-Server - path: server_distribution/* - - - name: Create Installer - working-directory: ${{ github.workspace }} - run: | - & "C:\Program Files (x86)\Inno Setup 6\iscc.exe" /Oinstaller scripts\inno\setup.iss - - - name: Upload Installer - uses: actions/upload-artifact@v4 - with: - name: OpenStarbound-Windows-Installer - path: installer/* diff --git a/source/base/StarAssets.cpp b/source/base/StarAssets.cpp index c47b71c..f81a4ee 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..bb8bd22 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 diff --git a/source/core/StarDataStream.cpp b/source/core/StarDataStream.cpp index 18456b6..1224f54 100644 --- a/source/core/StarDataStream.cpp +++ b/source/core/StarDataStream.cpp @@ -35,6 +35,13 @@ void DataStream::setStreamCompatibilityVersion(unsigned streamCompatibilityVersi m_streamCompatibilityVersion = streamCompatibilityVersion; } +void DataStream::setStreamCompatibilityVersion(NetCompatibilityRules const& rules) { + if (rules.isLegacy) + m_streamCompatibilityVersion = 1; + else + m_streamCompatibilityVersion = CurrentStreamVersion; +} + 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/StarNetCompatibility.hpp b/source/core/StarNetCompatibility.hpp new file mode 100644 index 0000000..730546e --- /dev/null +++ b/source/core/StarNetCompatibility.hpp @@ -0,0 +1,41 @@ +#pragma once +#include "StarDataStream.hpp" + +namespace Star { + + +enum class NetCompatibilityFilter { + None = 0, + Old = 1, + New = 2 +}; + +struct NetCompatibilityRules { + NetCompatibilityRules() = default; + NetCompatibilityRules(uint64_t) = delete; + NetCompatibilityRules(bool legacy); + + bool checkFilter(NetCompatibilityFilter const& filter) const; + + bool isLegacy = false; +}; + +inline NetCompatibilityRules::NetCompatibilityRules(bool legacy) : isLegacy(legacy) {} + +inline bool NetCompatibilityRules::checkFilter(NetCompatibilityFilter const& filter) const { + if (filter == NetCompatibilityFilter::None) + return true; + else if (isLegacy) + return filter == NetCompatibilityFilter::Old; + else + return filter == NetCompatibilityFilter::New; +} + +template <> +struct hash { + size_t operator()(NetCompatibilityRules const& s) const { + return s.isLegacy; + } +}; + +} \ 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..393dd3d 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,32 @@ 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); + + NetCompatibilityFilter netCompatibilityFilter() const; + void setNetCompatibilityFilter(NetCompatibilityFilter netFilter); + bool checkWithRules(NetCompatibilityRules const& rules) const; +private: + NetCompatibilityFilter m_netCompatibilityFilter = NetCompatibilityFilter::None; }; +inline NetCompatibilityFilter NetElement::netCompatibilityFilter() const { + return m_netCompatibilityFilter; +} + +inline void NetElement::setNetCompatibilityFilter(NetCompatibilityFilter netFilter) { + m_netCompatibilityFilter = netFilter; +} + +inline bool NetElement::checkWithRules(NetCompatibilityRules const& rules) const { + return rules.checkFilter(m_netCompatibilityFilter); +} + } 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::tickNetInterpolation(float dt) { } template -void NetElementBasicField::netStore(DataStream& ds) const { +void NetElementBasicField::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::netStore(DataStream& ds) const { } template -void NetElementBasicField::netLoad(DataStream& ds) { +void NetElementBasicField::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::netLoad(DataStream& ds) { } template -bool NetElementBasicField::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { +bool NetElementBasicField::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::writeNetDelta(DataStream& ds, uint64_t fromVersion writeData(ds, m_pendingInterpolatedValues->last().second); else writeData(ds, m_value); + return true; } template -void NetElementBasicField::readNetDelta(DataStream& ds, float interpolationTime) { +void NetElementBasicField::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 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::tickNetInterpolation(float dt) { } template -void NetElementMapWrapper::netStore(DataStream& ds) const { +void NetElementMapWrapper::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::netStore(DataStream& ds) const { } template -void NetElementMapWrapper::netLoad(DataStream& ds) { +void NetElementMapWrapper::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::netLoad(DataStream& ds) { } template -bool NetElementMapWrapper::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { +bool NetElementMapWrapper::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 +bool NetElementMapWrapper::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::writeNetDelta(DataStream& ds, uint64_t fromV } template -void NetElementMapWrapper::readNetDelta(DataStream& ds, float interpolationTime) { +void NetElementMapWrapper::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); @@ -381,6 +401,11 @@ void NetElementMapWrapper::setContents(MapType const& values) { reset(BaseMap::from(values)); } +template +uint64_t NetElementMapWrapper::changeDataLastVersion() const { + return m_changeDataLastVersion; +} + template void NetElementMapWrapper::writeChange(DataStream& ds, ElementChange const& change) { if (auto sc = change.template ptr()) { 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 auto NetElementDynamicGroup::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::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::tickNetInterpolation(float dt) { } template -void NetElementDynamicGroup::netStore(DataStream& ds) const { +void NetElementDynamicGroup::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 -void NetElementDynamicGroup::netLoad(DataStream& ds) { +void NetElementDynamicGroup::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::netLoad(DataStream& ds) { DataStreamBuffer storeBuffer(ds.read()); ElementPtr element = make_shared(); - element->netLoad(storeBuffer); + element->netLoad(storeBuffer, rules); readyElement(element); m_idMap.add(id, std::move(element)); @@ -197,10 +200,11 @@ void NetElementDynamicGroup::netLoad(DataStream& ds) { } template -bool NetElementDynamicGroup::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { +bool NetElementDynamicGroup::writeNetDelta(DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) const { + if (!checkWithRules(rules)) return false; if (fromVersion < m_changeDataLastVersion) { ds.write(true); - netStore(ds); + netStore(ds, rules); return true; } else { @@ -220,8 +224,9 @@ bool NetElementDynamicGroup::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::writeNetDelta(DataStream& ds, uint64_t fro } template -void NetElementDynamicGroup::readNetDelta(DataStream& ds, float interpolationTime) { +void NetElementDynamicGroup::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) { + if (!checkWithRules(rules)) return; bool isFull = ds.read(); if (isFull) { - netLoad(ds); + netLoad(ds, rules); } else { while (true) { uint64_t code = ds.readVlqU(); @@ -256,7 +262,7 @@ void NetElementDynamicGroup::readNetDelta(DataStream& ds, float interpo } else if (auto addition = changeUpdate.template ptr()) { ElementPtr element = make_shared(); 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()) { @@ -265,7 +271,7 @@ void NetElementDynamicGroup::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 +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 NetStorer; + typedef std::function NetLoader; + typedef std::function NetDeltaWriter; + typedef std::function 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 +void NetElementOverride::netStore(DataStream& ds, NetCompatibilityRules rules) const { + if (m_netStorer) + m_netStorer(ds, rules); + else + BaseNetElement::netStore(ds, rules); +} + +template +void NetElementOverride::netLoad(DataStream& ds, NetCompatibilityRules rules) { + if (m_netLoader) + m_netLoader(ds, rules); + else + BaseNetElement::netLoad(ds, rules); +} + +template +bool NetElementOverride::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 +void NetElementOverride::readNetDelta(DataStream& ds, float interpolationTime, NetCompatibilityRules rules) { + if (m_netDeltaReader) + m_netDeltaReader(ds, interpolationTime, rules); + else + BaseNetElement::readNetDelta(ds, interpolationTime, rules); +} + +template +inline void NetElementOverride::setNetStorer(NetStorer f) { m_netStorer = std::move(f); } +template +inline void NetElementOverride::setNetLoader(NetLoader f) { m_netLoader = std::move(f); } +template +inline void NetElementOverride::setNetDeltaWriter(NetDeltaWriter f) { m_netDeltaWriter = std::move(f); } +template +inline void NetElementOverride::setNetDeltaReader(NetDeltaReader f) { m_netDeltaReader = std::move(f); } + +template +inline void NetElementOverride::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::tickNetInterpolation(float dt) { } template -void NetElementFloating::netStore(DataStream& ds) const { +void NetElementFloating::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::netStore(DataStream& ds) const { } template -void NetElementFloating::netLoad(DataStream& ds) { +void NetElementFloating::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::netLoad(DataStream& ds) { } template -bool NetElementFloating::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { +bool NetElementFloating::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::writeNetDelta(DataStream& ds, uint64_t fromVersion) } template -void NetElementFloating::readNetDelta(DataStream& ds, float interpolationTime) { +void NetElementFloating::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 receive(); @@ -55,10 +55,10 @@ void NetElementSignal::initNetVersion(NetElementVersion const* version) } template -void NetElementSignal::netStore(DataStream&) const {} +void NetElementSignal::netStore(DataStream&, NetCompatibilityRules) const {} template -void NetElementSignal::netLoad(DataStream&) { +void NetElementSignal::netLoad(DataStream&, NetCompatibilityRules) { } template @@ -83,7 +83,8 @@ void NetElementSignal::tickNetInterpolation(float dt) { } template -bool NetElementSignal::writeNetDelta(DataStream& ds, uint64_t fromVersion) const { +bool NetElementSignal::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::writeNetDelta(DataStream& ds, uint64_t fromVersio } template -void NetElementSignal::readNetDelta(DataStream& ds, float interpolationTime) { +void NetElementSignal::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(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(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 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 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::NetElementTop() { } template -pair NetElementTop::writeNetState(uint64_t fromVersion) { +pair NetElementTop::writeNetState(uint64_t fromVersion, NetCompatibilityRules rules) { + DataStreamBuffer ds; + ds.setStreamCompatibilityVersion(rules); if (fromVersion == 0) { - DataStreamBuffer ds; ds.write(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(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 -void NetElementTop::readNetState(ByteArray data, float interpolationTime) { +void NetElementTop::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()) - 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/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()); @@ -89,10 +90,10 @@ void ClientContext::readUpdate(ByteArray data) { if (!shipUpdates.empty()) m_newShipUpdates.merge(DataStreamBuffer::deserialize(std::move(shipUpdates)), true); - m_netGroup.readNetState(ds.read()); + m_netGroup.readNetState(ds.read(), 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(entity)) { - return player->netStore(); + return player->netStore(rules); } else if (auto monster = as(entity)) { - return monster->netStore(); + return monster->netStore(rules); } else if (auto object = as(entity)) { - return object->netStore(); + return object->netStore(rules); } else if (auto plant = as(entity)) { - return plant->netStore(); + return plant->netStore(rules); } else if (auto plantDrop = as(entity)) { - return plantDrop->netStore(); + return plantDrop->netStore(rules); } else if (auto projectile = as(entity)) { - return projectile->netStore(); + return projectile->netStore(rules); } else if (auto itemDrop = as(entity)) { - return itemDrop->netStore(); + return itemDrop->netStore(rules); } else if (auto npc = as(entity)) { - return npc->netStore(); + return npc->netStore(rules); } else if (auto stagehand = as(entity)) { - return stagehand->netStore(); + return stagehand->netStore(rules); } else if (auto vehicle = as(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(netStore); + return make_shared(netStore, rules); } else if (type == EntityType::PlantDrop) { - return make_shared(netStore); + return make_shared(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(netStore); + return make_shared(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(netStore); + return make_shared(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 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(), 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 ItemDrop::writeNetState(uint64_t fromVersion) { - return m_netGroup.writeNetState(fromVersion); +pair 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 writeNetState(uint64_t fromVersion = 0) override; - void readNetState(ByteArray data, float interpolationTime = 0.0f) override; + pair 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 Monster::writeNetState(uint64_t fromVersion) { - return m_netGroup.writeNetState(fromVersion); +pair 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 writeNetState(uint64_t fromVersion = 0) override; - void readNetState(ByteArray data, float interpolationTime = 0.0f) override; + pair 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(); uint64_t seed = ds.read(); @@ -216,8 +218,8 @@ MonsterPtr MonsterDatabase::diskLoadMonster(Json const& diskStore) const { return make_shared(diskStore); } -MonsterPtr MonsterDatabase::netLoadMonster(ByteArray const& netStore) const { - return make_shared(readMonsterVariant(netStore)); +MonsterPtr MonsterDatabase::netLoadMonster(ByteArray const& netStore, NetCompatibilityRules rules) const { + return make_shared(readMonsterVariant(netStore, rules)); } List 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 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 monsterPortrait(MonsterVariant const& variant) const; diff --git a/source/game/StarNetPackets.cpp b/source/game/StarNetPackets.cpp index b8605ed..dd80d76 100644 --- a/source/game/StarNetPackets.cpp +++ b/source/game/StarNetPackets.cpp @@ -5,6 +5,7 @@ namespace Star { VersionNumber const StarProtocolVersion = 747; +VersionNumber const OpenProtocolVersion = 1; EnumMap const PacketTypeNames{ {PacketType::ProtocolRequest, "ProtocolRequest"}, diff --git a/source/game/StarNetPackets.hpp b/source/game/StarNetPackets.hpp index 6181b30..e68a045 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 { @@ -22,6 +23,7 @@ STAR_STRUCT(Packet); STAR_EXCEPTION(StarPacketException, IOException); extern VersionNumber const StarProtocolVersion; +extern VersionNumber const OpenProtocolVersion; // Packet types sent between the client and server over a NetSocket. Does not // correspond to actual packets, simply logical portions of NetSocket data. 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 Npc::writeNetState(uint64_t fromVersion) { +pair 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 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 writeNetState(uint64_t fromVersion = 0) override; - void readNetState(ByteArray data, float interpolationTime = 0.0f) override; + pair 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(npcVariant, diskStore); } -NpcPtr NpcDatabase::netLoadNpc(ByteArray const& netStore) const { - return make_shared(readNpcVariant(netStore)); +NpcPtr NpcDatabase::netLoadNpc(ByteArray const& netStore, NetCompatibilityRules rules) const { + return make_shared(readNpcVariant(netStore, rules)); } List 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 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(m_parameters.baseMap()); return ds.takeData(); @@ -297,13 +298,12 @@ RectF Object::metaBoundBox() const { } } -pair Object::writeNetState(uint64_t fromVersion) { - DataStreamBuffer ds; - return m_netGroup.writeNetState(fromVersion); +pair 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 writeNetState(uint64_t fromVersion = 0) override; - virtual void readNetState(ByteArray data, float interpolationTime = 0.0f) override; + virtual pair 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(); Json parameters = ds.read(); 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()); setupNetStates(); @@ -586,12 +588,12 @@ void Plant::init(World* world, EntityId entityId, EntityMode mode) { m_tilePosition = world->geometry().xwrap(m_tilePosition); } -pair Plant::writeNetState(uint64_t fromVersion) { - return m_netGroup.writeNetState(fromVersion); +pair 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 writeNetState(uint64_t fromVersion = 0) override; - void readNetState(ByteArray data, float interpolationTime = 0.0f) override; + pair 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 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 PlantDrop::writeNetState(uint64_t fromVersion) { - return m_netGroup.writeNetState(fromVersion); +pair 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 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 writeNetState(uint64_t fromVersion = 0) override; - void readNetState(ByteArray data, float interpolationTime = 0.0f) override; + pair 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()); @@ -1618,12 +1619,12 @@ Direction Player::facingDirection() const { return m_movementController->facingDirection(); } -pair Player::writeNetState(uint64_t fromVersion) { - return m_netGroup.writeNetState(fromVersion); +pair 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 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 writeNetState(uint64_t fromStep = 0) override; - void readNetState(ByteArray data, float interpolationStep = 0.0f) override; + pair 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(m_config, diskStore); } -PlayerPtr PlayerFactory::netLoadPlayer(ByteArray const& netStore) const { - return make_shared(m_config, netStore); +PlayerPtr PlayerFactory::netLoadPlayer(ByteArray const& netStore, NetCompatibilityRules rules) const { + return make_shared(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()); } -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 Projectile::writeNetState(uint64_t fromVersion) { - return m_netGroup.writeNetState(fromVersion); +pair 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 writeNetState(uint64_t fromVersion = 0) override; - void readNetState(ByteArray data, float interpolationTime = 0.0f) override; + pair 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(); - return make_shared(m_configs.get(typeName), ds); + return make_shared(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 remoteAddress, Uuid playerUuid, +ServerClientContext::ServerClientContext(ConnectionId clientId, Maybe 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 remoteAddress, Uuid playerUuid, + ServerClientContext(ConnectionId clientId, Maybe 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 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 Sky::writeUpdate(uint64_t fromVersion) { - return m_netGroup.writeNetState(fromVersion); +pair 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 writeUpdate(uint64_t fromVersion = 0); - void readUpdate(ByteArray data); + pair 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(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 Stagehand::writeNetState(uint64_t fromVersion) { - return m_netGroup.writeNetState(fromVersion); +pair 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 writeNetState(uint64_t fromVersion = 0) override; - void readNetState(ByteArray data, float interpolationTime = 0.0f) override; + pair 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..a2a5e9c 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.isLegacy) ds << m_statusProperties.baseMap(); + else m_statusProperties.NetElementHashMap::netStore(ds, rules); + }, + [&](DataStream& ds, NetCompatibilityRules rules) { + if (rules.isLegacy) m_statusProperties.reset(ds.read()); + else m_statusProperties.NetElementHashMap::netLoad(ds, rules); + }, + [&](DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) { + if (rules.isLegacy) { + if (m_statusProperties.shouldWriteNetDelta(fromVersion, rules)) { + ds << m_statusProperties.baseMap(); + return true; + } + return false; + } + return m_statusProperties.NetElementHashMap::writeNetDelta(ds, fromVersion, rules); + }, + [&](DataStream& ds, float interp, NetCompatibilityRules rules) { + if (rules.isLegacy) m_statusProperties.reset(ds.read()); + else m_statusProperties.NetElementHashMap::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 animationConfig; @@ -203,7 +204,7 @@ private: NetElementGroup m_netGroup; StatCollection m_statCollection; - NetElementData m_statusProperties; + NetElementOverride> m_statusProperties; NetElementData 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 SystemObject::writeNetState(uint64_t fromVersion) { - return m_netGroup.writeNetState(fromVersion); +pair 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 SystemClientShip::writeNetState(uint64_t fromVersion) { - return m_netGroup.writeNetState(fromVersion); +pair 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 writeNetState(uint64_t fromVersion); - void readNetState(ByteArray data, float interpolationTime); + pair 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 writeNetState(uint64_t fromVersion); - void readNetState(ByteArray data, float interpolationTime); + pair 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 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 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(this)->updateItemDescriptor(); - m_itemDescriptor.netStore(ds); + m_itemDescriptor.netStore(ds, rules); if (auto netItem = as(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(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(this)->updateItemDescriptor(); m_buffer.clear(); - if (m_itemDescriptor.writeNetDelta(m_buffer, fromVersion)) { + if (m_itemDescriptor.writeNetDelta(m_buffer, fromVersion, rules)) { deltaWritten = true; ds.write(1); ds.writeBytes(m_buffer.data()); if (auto netItem = as(m_item.get())) { ds.write(2); - netItem->netStore(ds); + netItem->netStore(ds, rules); } } if (auto netItem = as(m_item.get())) { m_buffer.clear(); - if (netItem->writeNetDelta(m_buffer, fromVersion)) { + if (netItem->writeNetDelta(m_buffer, fromVersion, rules)) { deltaWritten = true; ds.write(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(); 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(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(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..023140c 100644 --- a/source/game/StarUniverseClient.cpp +++ b/source/game/StarUniverseClient.cpp @@ -94,7 +94,8 @@ Maybe 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)) { + bool legacyServer = protocolResponsePacket->compressionMode() != PacketCompressionMode::Enabled; + if (!legacyServer) { if (auto compressedSocket = as(&connection.packetSocket())) { if (protocolResponsePacket->info) { auto compressionName = protocolResponsePacket->info.getString("compression", "None"); @@ -104,13 +105,13 @@ Maybe UniverseClient::connect(UniverseConnection connection, bool allowA Logger::info("UniverseClient: Using '{}' network stream compression", NetCompressionModeNames.getRight(*compressionMode)); compressedSocket->setCompressionStreamEnabled(compressionMode == NetCompressionMode::Zstd); - } else if (!m_legacyServer) { + } else { Logger::info("UniverseClient: Defaulting to Zstd network stream compression (older server version)"); compressedSocket->setCompressionStreamEnabled(true);// old OpenSB server version always expects it! } } } - connection.packetSocket().setLegacy(m_legacyServer); + connection.packetSocket().setLegacy(legacyServer); auto clientConnect = make_shared(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); @@ -133,14 +134,18 @@ Maybe UniverseClient::connect(UniverseConnection connection, bool allowA packet = connection.pullSingle(); } + NetCompatibilityRules compatibilityRules; + compatibilityRules.isLegacy = legacyServer; + if (auto success = as(packet)) { m_universeClock = make_shared(); m_clientContext = make_shared(success->serverUuid, m_mainPlayer->uuid()); + m_clientContext->setNetCompatibilityRules(compatibilityRules); m_teamClient = make_shared(m_mainPlayer, m_clientContext); m_mainPlayer->setClientContext(m_clientContext); m_mainPlayer->setStatistics(m_statistics); m_worldClient = make_shared(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 +154,7 @@ Maybe UniverseClient::connect(UniverseConnection connection, bool allowA m_celestialDatabase = make_shared(std::move(success->celestialInformation)); m_systemWorldClient = make_shared(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(packet)) { Logger::error("UniverseClient: Join failed: {}", failure->reason); @@ -263,7 +268,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(std::move(contextUpdate))); @@ -650,7 +655,7 @@ void UniverseClient::handlePackets(List const& packets) { for (auto const& packet : packets) { try { if (auto clientContextUpdate = as(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..09cfdc8 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)); @@ -1698,7 +1701,8 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe(clientId, remoteAddress, clientConnect->playerUuid, + NetCompatibilityRules netRules(legacyClient); + auto clientContext = make_shared(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 Vehicle::writeNetState(uint64_t fromVersion) { - return m_netGroup.writeNetState(fromVersion); +pair 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 writeNetState(uint64_t fromVersion) override; - void readNetState(ByteArray data, float interpolationTime = 0) override; + pair 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(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(); auto dynamicConfig = ds.read(); 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 regions) { m_clientVisibleRegions = std::move(regions); } -pair ServerWeather::writeUpdate(uint64_t fromVersion) { +pair 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 regions); - pair writeUpdate(uint64_t fromVersion = 0); + pair 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 9e0478a..aecb77a 100644 --- a/source/game/StarWorldClient.cpp +++ b/source/game/StarWorldClient.cpp @@ -168,7 +168,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(entity->entityId(), std::move(finalNetState), andDie)); } @@ -767,7 +768,7 @@ void WorldClient::handleIncomingPackets(List 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); @@ -788,13 +789,13 @@ void WorldClient::handleIncomingPackets(List 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(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. @@ -909,8 +910,8 @@ void WorldClient::handleIncomingPackets(List const& packets) { m_interpolationTracker.receiveTimeUpdate(stepUpdate->remoteTime); } else if (auto environmentUpdatePacket = as(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(packet)) { m_damageManager->pushRemoteHitRequest(hit->remoteHitRequest); @@ -1229,7 +1230,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(*m_pingTime)); } @@ -1325,7 +1326,8 @@ void WorldClient::addEntity(EntityPtr const& entity, EntityId entityId) { notifyEntityCreate(entity); } else { auto entityFactory = Root::singleton().entityFactory(); - m_outgoingPackets.append(make_shared(entity->entityType(), entityFactory->netStoreEntity(entity), entity->writeNetState().first)); + auto netRules = m_clientState.netCompatibilityRules(); + m_outgoingPackets.append(make_shared(entity->entityType(), entityFactory->netStoreEntity(entity), entity->writeNetState(0, netRules).first)); } } @@ -1456,9 +1458,10 @@ void WorldClient::queueUpdatePackets(bool sendEntityUpdates) { if (sendEntityUpdates) { auto entityUpdateSet = make_shared(); 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; @@ -1789,13 +1792,13 @@ void WorldClient::initWorld(WorldStartPacket const& startPacket) { centerClientWindowOnPlayer(); m_sky = make_shared(); - 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")); @@ -1870,7 +1873,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(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 WorldClientState::monitoringRegions(function(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> m_clientPresenceEntities; - bool m_legacy; + NetCompatibilityRules m_netCompatibilityRules; }; } diff --git a/source/game/StarWorldServer.cpp b/source/game/StarWorldServer.cpp index a78025c..a2df77b 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(clientId, tracker)); clientInfo->local = isLocal; clientInfo->admin = isAdmin; + clientInfo->clientState.setNetCompatibilityRules(netRules); auto worldStartPacket = make_shared(); 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 c } else if (auto sepacket = as(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(packet)) { @@ -434,7 +435,7 @@ void WorldServer::handleIncomingPackets(ConnectionId clientId, List 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 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(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(); @@ -691,6 +692,7 @@ void WorldServer::update(float dt) { queueUpdatePackets(pair.first, sendRemoteUpdates); } m_netStateCache.clear(); + m_legacyNetStateCache.clear(); for (auto& pair : m_clientInfo) pair.second->pendingForward = false; @@ -1789,10 +1791,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(std::move(skyDelta), std::move(weatherDelta))); @@ -1866,12 +1868,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 = netRules.isLegacy ? m_legacyNetStateCache : m_netStateCache; + 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 +1883,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(monitoredEntity->entityType(), entityFactory->netStoreEntity(monitoredEntity), std::move(firstUpdate.first), entityId)); @@ -2075,7 +2079,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(entity->entityId(), std::move(finalDelta), andDie)); } } diff --git a/source/game/StarWorldServer.hpp b/source/game/StarWorldServer.hpp index 9e319e5..a87f72d 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 @@ -375,6 +375,7 @@ private: List m_workingCollisionBlocks; HashMap, pair> m_netStateCache; + HashMap, pair> m_legacyNetStateCache; OrderedHashMap> 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 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 Entity::writeNetState(uint64_t) { +pair 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 writeNetState(uint64_t fromVersion = 0); + virtual pair 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);