use a version number rather than a bool

more flexible, allows for backwards compat with older OpenSB versions & not just vanilla Starbound
This commit is contained in:
Kae 2024-09-11 18:22:44 +10:00
parent 5a75473e16
commit 7408981e13
16 changed files with 85 additions and 58 deletions

View File

@ -18,7 +18,6 @@ SET (star_base_HEADERS
StarMixer.hpp StarMixer.hpp
StarPackedAssetSource.hpp StarPackedAssetSource.hpp
StarRootBase.hpp StarRootBase.hpp
StarVersion.hpp
StarVersionOptionParser.hpp StarVersionOptionParser.hpp
StarWorldGeometry.hpp StarWorldGeometry.hpp
scripting/StarImageLuaBindings.hpp scripting/StarImageLuaBindings.hpp
@ -40,8 +39,7 @@ SET (star_base_SOURCES
scripting/StarImageLuaBindings.cpp scripting/StarImageLuaBindings.cpp
) )
CONFIGURE_FILE (StarVersion.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/StarVersion.cpp) ADD_LIBRARY (star_base OBJECT ${star_base_SOURCES} ${star_base_HEADERS})
ADD_LIBRARY (star_base OBJECT ${star_base_SOURCES} ${star_base_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/StarVersion.cpp)
IF(STAR_PRECOMPILED_HEADERS) IF(STAR_PRECOMPILED_HEADERS)
TARGET_PRECOMPILE_HEADERS (star_base REUSE_FROM star_core) TARGET_PRECOMPILE_HEADERS (star_base REUSE_FROM star_core)

View File

@ -124,6 +124,7 @@ SET (star_core_HEADERS
StarUnicode.hpp StarUnicode.hpp
StarUuid.hpp StarUuid.hpp
StarVector.hpp StarVector.hpp
StarVersion.hpp
StarVlqEncoding.hpp StarVlqEncoding.hpp
StarWeightedPool.hpp StarWeightedPool.hpp
StarWorkerPool.hpp StarWorkerPool.hpp
@ -164,6 +165,7 @@ SET (star_core_SOURCES
StarLua.cpp StarLua.cpp
StarLuaConverters.cpp StarLuaConverters.cpp
StarMemory.cpp StarMemory.cpp
StarNetCompatibility.cpp
StarNetElement.cpp StarNetElement.cpp
StarNetElementBasicFields.cpp StarNetElementBasicFields.cpp
StarNetElementGroup.cpp StarNetElementGroup.cpp
@ -219,7 +221,8 @@ ELSEIF (STAR_SYSTEM_FAMILY_WINDOWS)
ENDIF () ENDIF ()
ADD_LIBRARY (star_core OBJECT ${star_core_SOURCES} ${star_core_HEADERS}) CONFIGURE_FILE (StarVersion.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/StarVersion.cpp)
ADD_LIBRARY (star_core OBJECT ${star_core_SOURCES} ${star_core_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/StarVersion.cpp)
IF(STAR_PRECOMPILED_HEADERS) IF(STAR_PRECOMPILED_HEADERS)
TARGET_PRECOMPILE_HEADERS (star_core PUBLIC StarPch.hpp) TARGET_PRECOMPILE_HEADERS (star_core PUBLIC StarPch.hpp)

View File

@ -36,10 +36,7 @@ void DataStream::setStreamCompatibilityVersion(unsigned streamCompatibilityVersi
} }
void DataStream::setStreamCompatibilityVersion(NetCompatibilityRules const& rules) { void DataStream::setStreamCompatibilityVersion(NetCompatibilityRules const& rules) {
if (rules.isLegacy) m_streamCompatibilityVersion = rules.version();
m_streamCompatibilityVersion = 1;
else
m_streamCompatibilityVersion = CurrentStreamVersion;
} }
ByteArray DataStream::readBytes(size_t len) { ByteArray DataStream::readBytes(size_t len) {

View File

@ -0,0 +1,7 @@
#include "StarNetCompatibility.hpp"
namespace Star {
VersionNumber const OpenProtocolVersion = 2;
}

View File

@ -1,40 +1,54 @@
#pragma once #pragma once
#include "StarDataStream.hpp" #include "StarVersion.hpp"
#include "StarHash.hpp"
namespace Star { namespace Star {
extern VersionNumber const OpenProtocolVersion;
enum class NetCompatibilityFilter { constexpr VersionNumber AnyVersion = 0xFFFFFFFF;
None = 0, constexpr VersionNumber LegacyVersion = 0;
Old = 1,
New = 2
};
struct NetCompatibilityRules { class NetCompatibilityRules {
NetCompatibilityRules() = default; public:
NetCompatibilityRules();
NetCompatibilityRules(uint64_t) = delete; NetCompatibilityRules(uint64_t) = delete;
NetCompatibilityRules(bool legacy); NetCompatibilityRules(VersionNumber version);
bool checkFilter(NetCompatibilityFilter const& filter) const; VersionNumber version() const;
void setVersion(VersionNumber version);
bool isLegacy() const;
bool isLegacy = false; bool operator==(NetCompatibilityRules const& a) const;
private:
VersionNumber m_version = OpenProtocolVersion;
}; };
inline NetCompatibilityRules::NetCompatibilityRules(bool legacy) : isLegacy(legacy) {} inline NetCompatibilityRules::NetCompatibilityRules() : m_version(OpenProtocolVersion) {}
inline bool NetCompatibilityRules::checkFilter(NetCompatibilityFilter const& filter) const { inline NetCompatibilityRules::NetCompatibilityRules(VersionNumber v) : m_version(v) {}
if (filter == NetCompatibilityFilter::None)
return true; inline VersionNumber NetCompatibilityRules::version() const {
else if (isLegacy) return m_version;
return filter == NetCompatibilityFilter::Old; }
else
return filter == NetCompatibilityFilter::New; inline void NetCompatibilityRules::setVersion(VersionNumber version) {
m_version = version;
}
inline bool NetCompatibilityRules::isLegacy() const {
return m_version == LegacyVersion;
}
inline bool NetCompatibilityRules::operator==(NetCompatibilityRules const& a) const {
return m_version == a.m_version;
} }
template <> template <>
struct hash<NetCompatibilityRules> { struct hash<NetCompatibilityRules> {
size_t operator()(NetCompatibilityRules const& s) const { size_t operator()(NetCompatibilityRules const& s) const {
return s.isLegacy; return s.version();
} }
}; };

View File

@ -55,23 +55,25 @@ public:
// received even if no deltas are produced, so no extrapolation takes place. // received even if no deltas are produced, so no extrapolation takes place.
virtual void blankNetDelta(float interpolationTime); virtual void blankNetDelta(float interpolationTime);
NetCompatibilityFilter netCompatibilityFilter() const; VersionNumber compatibilityVersion() const;
void setNetCompatibilityFilter(NetCompatibilityFilter netFilter); void setCompatibilityVersion(VersionNumber version);
bool checkWithRules(NetCompatibilityRules const& rules) const; bool checkWithRules(NetCompatibilityRules const& rules) const;
private: private:
NetCompatibilityFilter m_netCompatibilityFilter = NetCompatibilityFilter::None; VersionNumber m_netCompatibilityVersion = AnyVersion;
}; };
inline NetCompatibilityFilter NetElement::netCompatibilityFilter() const { inline VersionNumber NetElement::compatibilityVersion() const {
return m_netCompatibilityFilter; return m_netCompatibilityVersion;
} }
inline void NetElement::setNetCompatibilityFilter(NetCompatibilityFilter netFilter) { inline void NetElement::setCompatibilityVersion(VersionNumber version) {
m_netCompatibilityFilter = netFilter; m_netCompatibilityVersion = version;
} }
inline bool NetElement::checkWithRules(NetCompatibilityRules const& rules) const { inline bool NetElement::checkWithRules(NetCompatibilityRules const& rules) const {
return rules.checkFilter(m_netCompatibilityFilter); if (m_netCompatibilityVersion != AnyVersion)
return rules.version() >= m_netCompatibilityVersion;
return true;
} }
} }

View File

@ -5,7 +5,6 @@
namespace Star { namespace Star {
VersionNumber const StarProtocolVersion = 747; VersionNumber const StarProtocolVersion = 747;
VersionNumber const OpenProtocolVersion = 1;
EnumMap<PacketType> const PacketTypeNames{ EnumMap<PacketType> const PacketTypeNames{
{PacketType::ProtocolRequest, "ProtocolRequest"}, {PacketType::ProtocolRequest, "ProtocolRequest"},

View File

@ -23,7 +23,6 @@ STAR_STRUCT(Packet);
STAR_EXCEPTION(StarPacketException, IOException); STAR_EXCEPTION(StarPacketException, IOException);
extern VersionNumber const StarProtocolVersion; extern VersionNumber const StarProtocolVersion;
extern VersionNumber const OpenProtocolVersion;
// Packet types sent between the client and server over a NetSocket. Does not // Packet types sent between the client and server over a NetSocket. Does not
// correspond to actual packets, simply logical portions of NetSocket data. // correspond to actual packets, simply logical portions of NetSocket data.

View File

@ -21,15 +21,15 @@ StatusController::StatusController(Json const& config) : m_statCollection(config
m_statusProperties.reset(config.getObject("statusProperties", {})); m_statusProperties.reset(config.getObject("statusProperties", {}));
m_statusProperties.setOverrides( m_statusProperties.setOverrides(
[&](DataStream& ds, NetCompatibilityRules rules) { [&](DataStream& ds, NetCompatibilityRules rules) {
if (rules.isLegacy) ds << m_statusProperties.baseMap(); if (rules.version() <= 1) ds << m_statusProperties.baseMap();
else m_statusProperties.NetElementHashMap<String, Json>::netStore(ds, rules); else m_statusProperties.NetElementHashMap<String, Json>::netStore(ds, rules);
}, },
[&](DataStream& ds, NetCompatibilityRules rules) { [&](DataStream& ds, NetCompatibilityRules rules) {
if (rules.isLegacy) m_statusProperties.reset(ds.read<JsonObject>()); if (rules.version() <= 1) m_statusProperties.reset(ds.read<JsonObject>());
else m_statusProperties.NetElementHashMap<String, Json>::netLoad(ds, rules); else m_statusProperties.NetElementHashMap<String, Json>::netLoad(ds, rules);
}, },
[&](DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) { [&](DataStream& ds, uint64_t fromVersion, NetCompatibilityRules rules) {
if (rules.isLegacy) { if (rules.version() <= 1) {
if (m_statusProperties.shouldWriteNetDelta(fromVersion, rules)) { if (m_statusProperties.shouldWriteNetDelta(fromVersion, rules)) {
ds << m_statusProperties.baseMap(); ds << m_statusProperties.baseMap();
return true; return true;
@ -39,7 +39,7 @@ StatusController::StatusController(Json const& config) : m_statCollection(config
return m_statusProperties.NetElementHashMap<String, Json>::writeNetDelta(ds, fromVersion, rules); return m_statusProperties.NetElementHashMap<String, Json>::writeNetDelta(ds, fromVersion, rules);
}, },
[&](DataStream& ds, float interp, NetCompatibilityRules rules) { [&](DataStream& ds, float interp, NetCompatibilityRules rules) {
if (rules.isLegacy) m_statusProperties.reset(ds.read<JsonObject>()); if (rules.version() <= 1) m_statusProperties.reset(ds.read<JsonObject>());
else m_statusProperties.NetElementHashMap<String, Json>::readNetDelta(ds, interp, rules); else m_statusProperties.NetElementHashMap<String, Json>::readNetDelta(ds, interp, rules);
} }
); );

View File

@ -94,20 +94,27 @@ Maybe<String> UniverseClient::connect(UniverseConnection connection, bool allowA
else if (!protocolResponsePacket->allowed) else if (!protocolResponsePacket->allowed)
return String(strf("Join failed! Server does not support connections with protocol version {}", StarProtocolVersion)); return String(strf("Join failed! Server does not support connections with protocol version {}", StarProtocolVersion));
NetCompatibilityRules compatibilityRules;
compatibilityRules.setVersion(LegacyVersion);
bool legacyServer = protocolResponsePacket->compressionMode() != PacketCompressionMode::Enabled; bool legacyServer = protocolResponsePacket->compressionMode() != PacketCompressionMode::Enabled;
if (!legacyServer) { if (!legacyServer) {
if (auto compressedSocket = as<CompressedPacketSocket>(&connection.packetSocket())) { auto compressedSocket = as<CompressedPacketSocket>(&connection.packetSocket());
if (protocolResponsePacket->info) { if (protocolResponsePacket->info) {
compatibilityRules.setVersion(protocolResponsePacket->info.getUInt("openProtocolVersion", 1));
auto compressionName = protocolResponsePacket->info.getString("compression", "None"); auto compressionName = protocolResponsePacket->info.getString("compression", "None");
if (compressedSocket) {
auto compressionMode = NetCompressionModeNames.maybeLeft(compressionName); auto compressionMode = NetCompressionModeNames.maybeLeft(compressionName);
if (!compressionMode) if (!compressionMode)
return String(strf("Join failed! Unknown net stream connection type '{}'", compressionName)); return String(strf("Join failed! Unknown net stream connection type '{}'", compressionName));
Logger::info("UniverseClient: Using '{}' network stream compression", NetCompressionModeNames.getRight(*compressionMode)); Logger::info("UniverseClient: Using '{}' network stream compression", NetCompressionModeNames.getRight(*compressionMode));
compressedSocket->setCompressionStreamEnabled(compressionMode == NetCompressionMode::Zstd); compressedSocket->setCompressionStreamEnabled(compressionMode == NetCompressionMode::Zstd);
}
} else { } else {
compatibilityRules.setVersion(1); // A version of 1 is OpenStarbound prior to the NetElement compatibility stuff
if (compressedSocket) {
Logger::info("UniverseClient: Defaulting to Zstd network stream compression (older server version)"); Logger::info("UniverseClient: Defaulting to Zstd network stream compression (older server version)");
compressedSocket->setCompressionStreamEnabled(true);// old OpenSB server version always expects it! compressedSocket->setCompressionStreamEnabled(true);
} }
} }
} }
@ -115,7 +122,10 @@ Maybe<String> UniverseClient::connect(UniverseConnection connection, bool allowA
auto clientConnect = make_shared<ClientConnectPacket>(Root::singleton().assets()->digest(), allowAssetsMismatch, m_mainPlayer->uuid(), m_mainPlayer->name(), auto clientConnect = make_shared<ClientConnectPacket>(Root::singleton().assets()->digest(), allowAssetsMismatch, m_mainPlayer->uuid(), m_mainPlayer->name(),
m_mainPlayer->species(), m_playerStorage->loadShipData(m_mainPlayer->uuid()), m_mainPlayer->shipUpgrades(), m_mainPlayer->species(), m_playerStorage->loadShipData(m_mainPlayer->uuid()), m_mainPlayer->shipUpgrades(),
m_mainPlayer->log()->introComplete(), account); m_mainPlayer->log()->introComplete(), account);
clientConnect->info = JsonObject{ {"brand", "OpenStarbound"} }; clientConnect->info = JsonObject{
{"brand", "OpenStarbound"},
{"openProtocolVersion", OpenProtocolVersion }
};
connection.pushSingle(std::move(clientConnect)); connection.pushSingle(std::move(clientConnect));
connection.sendAll(timeout); connection.sendAll(timeout);
@ -134,9 +144,6 @@ Maybe<String> UniverseClient::connect(UniverseConnection connection, bool allowA
packet = connection.pullSingle(); packet = connection.pullSingle();
} }
NetCompatibilityRules compatibilityRules;
compatibilityRules.isLegacy = legacyServer;
if (auto success = as<ConnectSuccessPacket>(packet)) { if (auto success = as<ConnectSuccessPacket>(packet)) {
m_universeClock = make_shared<Clock>(); m_universeClock = make_shared<Clock>();
m_clientContext = make_shared<ClientContext>(success->serverUuid, m_mainPlayer->uuid()); m_clientContext = make_shared<ClientContext>(success->serverUuid, m_mainPlayer->uuid());

View File

@ -1577,7 +1577,8 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe<HostA
auto compressionMode = NetCompressionModeNames.maybeLeft(compressionName).value(NetCompressionMode::None); auto compressionMode = NetCompressionModeNames.maybeLeft(compressionName).value(NetCompressionMode::None);
useCompressionStream = compressionMode == NetCompressionMode::Zstd; useCompressionStream = compressionMode == NetCompressionMode::Zstd;
protocolResponse->info = JsonObject{ protocolResponse->info = JsonObject{
{"compression", NetCompressionModeNames.getRight(compressionMode)} {"compression", NetCompressionModeNames.getRight(compressionMode)},
{"openProtocolVersion", OpenProtocolVersion}
}; };
} }
connection.pushSingle(protocolResponse); connection.pushSingle(protocolResponse);
@ -1674,7 +1675,10 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe<HostA
String connectionLog = strf("UniverseServer: Logged in account '{}' as player '{}' from address {}", String connectionLog = strf("UniverseServer: Logged in account '{}' as player '{}' from address {}",
accountString, clientConnect->playerName, remoteAddressString); accountString, clientConnect->playerName, remoteAddressString);
NetCompatibilityRules netRules(legacyClient ? LegacyVersion : 1);
if (Json& info = clientConnect->info) { if (Json& info = clientConnect->info) {
if (auto openProtocolVersion = info.optUInt("openProtocolVersion"))
netRules.setVersion(*openProtocolVersion);
if (Json brand = info.get("brand", "custom")) if (Json brand = info.get("brand", "custom"))
connectionLog += strf(" ({} client)", brand.toString()); connectionLog += strf(" ({} client)", brand.toString());
if (info.getBool("legacy", false)) if (info.getBool("legacy", false))
@ -1701,7 +1705,6 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybe<HostA
} }
ConnectionId clientId = m_clients.nextId(); ConnectionId clientId = m_clients.nextId();
NetCompatibilityRules netRules(legacyClient);
auto clientContext = make_shared<ServerClientContext>(clientId, remoteAddress, netRules, clientConnect->playerUuid, auto clientContext = make_shared<ServerClientContext>(clientId, remoteAddress, netRules, clientConnect->playerUuid,
clientConnect->playerName, clientConnect->playerSpecies, administrator, clientConnect->shipChunks); clientConnect->playerName, clientConnect->playerSpecies, administrator, clientConnect->shipChunks);
m_clients.add(clientId, clientContext); m_clients.add(clientId, clientContext);

View File

@ -1230,7 +1230,7 @@ void WorldClient::update(float dt) {
queueUpdatePackets(m_entityUpdateTimer.wrapTick(dt)); queueUpdatePackets(m_entityUpdateTimer.wrapTick(dt));
if ((!m_clientState.netCompatibilityRules().isLegacy && m_currentStep % 3 == 0) || m_pingTime.isNothing()) { if ((!m_clientState.netCompatibilityRules().isLegacy() && m_currentStep % 3 == 0) || m_pingTime.isNothing()) {
m_pingTime = Time::monotonicMilliseconds(); m_pingTime = Time::monotonicMilliseconds();
m_outgoingPackets.append(make_shared<PingPacket>(*m_pingTime)); m_outgoingPackets.append(make_shared<PingPacket>(*m_pingTime));
} }

View File

@ -692,7 +692,6 @@ void WorldServer::update(float dt) {
queueUpdatePackets(pair.first, sendRemoteUpdates); queueUpdatePackets(pair.first, sendRemoteUpdates);
} }
m_netStateCache.clear(); m_netStateCache.clear();
m_legacyNetStateCache.clear();
for (auto& pair : m_clientInfo) for (auto& pair : m_clientInfo)
pair.second->pendingForward = false; pair.second->pendingForward = false;
@ -1872,7 +1871,7 @@ void WorldServer::queueUpdatePackets(ConnectionId clientId, bool sendRemoteUpdat
if (auto version = clientInfo->clientSlavesNetVersion.ptr(entityId)) { if (auto version = clientInfo->clientSlavesNetVersion.ptr(entityId)) {
if (auto updateSetPacket = updateSetPackets.value(connectionId)) { if (auto updateSetPacket = updateSetPackets.value(connectionId)) {
auto pair = make_pair(entityId, *version); auto pair = make_pair(entityId, *version);
auto& cache = netRules.isLegacy ? m_legacyNetStateCache : m_netStateCache; auto& cache = m_netStateCache[netRules];
auto i = cache.find(pair); auto i = cache.find(pair);
if (i == cache.end()) if (i == cache.end())
i = cache.insert(pair, monitoredEntity->writeNetState(*version, netRules)).first; i = cache.insert(pair, monitoredEntity->writeNetState(*version, netRules)).first;

View File

@ -374,8 +374,7 @@ private:
CollisionGenerator m_collisionGenerator; CollisionGenerator m_collisionGenerator;
List<CollisionBlock> m_workingCollisionBlocks; List<CollisionBlock> m_workingCollisionBlocks;
HashMap<pair<EntityId, uint64_t>, pair<ByteArray, uint64_t>> m_netStateCache; HashMap<NetCompatibilityRules, HashMap<pair<EntityId, uint64_t>, pair<ByteArray, uint64_t>>> m_netStateCache;
HashMap<pair<EntityId, uint64_t>, pair<ByteArray, uint64_t>> m_legacyNetStateCache;
OrderedHashMap<ConnectionId, shared_ptr<ClientInfo>> m_clientInfo; OrderedHashMap<ConnectionId, shared_ptr<ClientInfo>> m_clientInfo;
GameTimer m_entityUpdateTimer; GameTimer m_entityUpdateTimer;