diff --git a/source/game/StarNetPacketSocket.cpp b/source/game/StarNetPacketSocket.cpp index f6ebce0..e2bd629 100644 --- a/source/game/StarNetPacketSocket.cpp +++ b/source/game/StarNetPacketSocket.cpp @@ -55,6 +55,9 @@ Maybe PacketSocket::outgoingStats() const { return {}; } +void PacketSocket::setLegacy(bool legacy) { m_legacy = legacy; } +bool PacketSocket::legacy() const { return m_legacy; } + pair LocalPacketSocket::openPair() { auto lhsIncomingPipe = make_shared(); auto rhsIncomingPipe = make_shared(); @@ -138,23 +141,32 @@ void TcpPacketSocket::sendPackets(List packets) { while (it.hasNext()) { PacketType currentType = it.peekNext()->type(); + PacketCompressionMode currentCompressionMode = it.peekNext()->compressionMode(); DataStreamBuffer packetBuffer; - while (it.hasNext() && it.peekNext()->type() == currentType) - it.next()->write(packetBuffer); + while (it.hasNext() + && it.peekNext()->type() == currentType + && it.peekNext()->compressionMode() == currentCompressionMode) { + if (legacy()) + it.next()->writeLegacy(packetBuffer); + else + it.next()->write(packetBuffer); + } // Packets must read and write actual data, because this is used to // determine packet count starAssert(!packetBuffer.empty()); ByteArray compressedPackets; - if (packetBuffer.size() > 64) + bool mustCompress = currentCompressionMode == PacketCompressionMode::Enabled; + bool perhapsCompress = currentCompressionMode == PacketCompressionMode::Automatic && packetBuffer.size() > 64; + if (mustCompress || perhapsCompress) compressedPackets = compressData(packetBuffer.data()); DataStreamBuffer outBuffer; outBuffer.write(currentType); - if (!compressedPackets.empty() && compressedPackets.size() < packetBuffer.size()) { + if (!compressedPackets.empty() && (mustCompress || compressedPackets.size() < packetBuffer.size())) { outBuffer.writeVlqI(-(int)(compressedPackets.size())); outBuffer.writeData(compressedPackets.ptr(), compressedPackets.size()); m_outgoingStats.mix(currentType, compressedPackets.size()); @@ -208,7 +220,11 @@ List TcpPacketSocket::receivePackets() { DataStreamBuffer packetStream(move(packetBytes)); do { PacketPtr packet = createPacket(packetType); - packet->read(packetStream); + packet->setCompressionMode(packetCompressed ? PacketCompressionMode::Enabled : PacketCompressionMode::Disabled); + if (legacy()) + packet->readLegacy(packetStream); + else + packet->read(packetStream); packets.append(move(packet)); } while (!packetStream.atEnd()); @@ -299,23 +315,32 @@ void P2PPacketSocket::sendPackets(List packets) { while (it.hasNext()) { PacketType currentType = it.peekNext()->type(); + PacketCompressionMode currentCompressionMode = it.peekNext()->compressionMode(); DataStreamBuffer packetBuffer; - while (it.hasNext() && it.peekNext()->type() == currentType) - it.next()->write(packetBuffer); + while (it.hasNext() + && it.peekNext()->type() == currentType + && it.peekNext()->compressionMode() == currentCompressionMode) { + if (legacy()) + it.next()->writeLegacy(packetBuffer); + else + it.next()->write(packetBuffer); + } // Packets must read and write actual data, because this is used to // determine packet count starAssert(!packetBuffer.empty()); ByteArray compressedPackets; - if (packetBuffer.size() > 64) + bool mustCompress = currentCompressionMode == PacketCompressionMode::Enabled; + bool perhapsCompress = currentCompressionMode == PacketCompressionMode::Automatic && packetBuffer.size() > 64; + if (mustCompress || perhapsCompress) compressedPackets = compressData(packetBuffer.data()); DataStreamBuffer outBuffer; outBuffer.write(currentType); - if (!compressedPackets.empty() && compressedPackets.size() < packetBuffer.size()) { + if (!compressedPackets.empty() && (mustCompress || compressedPackets.size() < packetBuffer.size())) { outBuffer.write(true); outBuffer.writeData(compressedPackets.ptr(), compressedPackets.size()); m_outgoingStats.mix(currentType, compressedPackets.size()); @@ -347,7 +372,11 @@ List P2PPacketSocket::receivePackets() { DataStreamBuffer packetStream(move(packetBytes)); do { PacketPtr packet = createPacket(packetType); - packet->read(packetStream); + packet->setCompressionMode(packetCompressed ? PacketCompressionMode::Enabled : PacketCompressionMode::Disabled); + if (legacy()) + packet->readLegacy(packetStream); + else + packet->read(packetStream); packets.append(move(packet)); } while (!packetStream.atEnd()); } diff --git a/source/game/StarNetPacketSocket.hpp b/source/game/StarNetPacketSocket.hpp index da90386..bfdd20c 100644 --- a/source/game/StarNetPacketSocket.hpp +++ b/source/game/StarNetPacketSocket.hpp @@ -72,6 +72,11 @@ public: // Default implementations return nothing. virtual Maybe incomingStats() const; virtual Maybe outgoingStats() const; + + void setLegacy(bool legacy); + bool legacy() const; +private: + bool m_legacy = false; }; // PacketSocket for local communication. diff --git a/source/game/StarNetPackets.cpp b/source/game/StarNetPackets.cpp index edd1a14..0ccb66e 100644 --- a/source/game/StarNetPackets.cpp +++ b/source/game/StarNetPackets.cpp @@ -79,6 +79,18 @@ EnumMap const PacketTypeNames{ Packet::~Packet() {} +void Packet::readLegacy(DataStream& ds) { + read(ds); +} +void Packet::writeLegacy(DataStream& ds) const { + write(ds); +} + +PacketCompressionMode Packet::compressionMode() const + { return m_compressionMode; } +void Packet::setCompressionMode(PacketCompressionMode compressionMode) + { m_compressionMode = compressionMode; } + PacketPtr createPacket(PacketType type) { switch (type) { case PacketType::ProtocolRequest: return make_shared(); diff --git a/source/game/StarNetPackets.hpp b/source/game/StarNetPackets.hpp index b97d53e..a02f9bc 100644 --- a/source/game/StarNetPackets.hpp +++ b/source/game/StarNetPackets.hpp @@ -117,6 +117,12 @@ enum class PacketType : uint8_t { }; extern EnumMap const PacketTypeNames; +enum class PacketCompressionMode : uint8_t { + Disabled, + Enabled, + Automatic +}; + struct Packet { virtual ~Packet(); @@ -124,6 +130,14 @@ struct Packet { virtual void read(DataStream& ds) = 0; virtual void write(DataStream& ds) const = 0; + + virtual void readLegacy(DataStream& ds); + virtual void writeLegacy(DataStream& ds) const; + + PacketCompressionMode compressionMode() const; + void setCompressionMode(PacketCompressionMode compressionMode); + + PacketCompressionMode m_compressionMode = PacketCompressionMode::Automatic; }; PacketPtr createPacket(PacketType type); @@ -132,9 +146,7 @@ template struct PacketBase : public Packet { static PacketType const Type = PacketT; - PacketType type() const override { - return Type; - } + PacketType type() const override { return Type; } }; struct ProtocolRequestPacket : PacketBase { diff --git a/source/game/StarUniverseClient.cpp b/source/game/StarUniverseClient.cpp index c17f8bc..79ddd97 100644 --- a/source/game/StarUniverseClient.cpp +++ b/source/game/StarUniverseClient.cpp @@ -77,7 +77,13 @@ Maybe UniverseClient::connect(UniverseConnection connection, bool allowA unsigned timeout = assets->json("/client.config:serverConnectTimeout").toUInt(); - connection.pushSingle(make_shared(StarProtocolVersion)); + { + auto protocolRequest = make_shared(StarProtocolVersion); + protocolRequest->setCompressionMode(PacketCompressionMode::Enabled); + // Signal that we're OpenStarbound. Vanilla Starbound only compresses packets above 64 bytes - by forcing it we can communicate this. + // If you know a less cursed way, please let me know. + connection.pushSingle(protocolRequest); + } connection.sendAll(timeout); connection.receiveAny(timeout); @@ -87,6 +93,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)); + m_legacyServer = protocolResponsePacket->compressionMode() != PacketCompressionMode::Enabled; // True if server is vanilla + connection.setLegacy(m_legacyServer); connection.pushSingle(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)); @@ -121,7 +129,7 @@ Maybe UniverseClient::connect(UniverseConnection connection, bool allowA m_celestialDatabase = make_shared(move(success->celestialInformation)); m_systemWorldClient = make_shared(m_universeClock, m_celestialDatabase, m_mainPlayer->universeMap()); - Logger::info("UniverseClient: Joined server as client {}", success->clientId); + Logger::info("UniverseClient: Joined {} server as client {}", m_legacyServer ? "Starbound" : "OpenStarbound", success->clientId); return {}; } else if (auto failure = as(packet)) { Logger::error("UniverseClient: Join failed: {}", failure->reason); diff --git a/source/game/StarUniverseClient.hpp b/source/game/StarUniverseClient.hpp index 89c9c39..00fe729 100644 --- a/source/game/StarUniverseClient.hpp +++ b/source/game/StarUniverseClient.hpp @@ -127,6 +127,7 @@ private: StatisticsPtr m_statistics; PlayerPtr m_mainPlayer; + bool m_legacyServer; bool m_pause; ClockPtr m_universeClock; WorldClientPtr m_worldClient; diff --git a/source/game/StarUniverseConnection.cpp b/source/game/StarUniverseConnection.cpp index e8230bb..f3c2b28 100644 --- a/source/game/StarUniverseConnection.cpp +++ b/source/game/StarUniverseConnection.cpp @@ -107,6 +107,10 @@ bool UniverseConnection::receiveAny(unsigned timeout) { } } +void UniverseConnection::setLegacy(bool legacy) { + m_packetSocket->setLegacy(legacy); +} + Maybe UniverseConnection::incomingStats() const { MutexLocker locker(m_mutex); return m_packetSocket->incomingStats(); diff --git a/source/game/StarUniverseConnection.hpp b/source/game/StarUniverseConnection.hpp index 679b023..12b3aa5 100644 --- a/source/game/StarUniverseConnection.hpp +++ b/source/game/StarUniverseConnection.hpp @@ -49,6 +49,8 @@ public: // false if the timeout was reached with no packets receivable. bool receiveAny(unsigned timeout); + void setLegacy(bool legacy); + // Packet stats for the most recent one second window of activity incoming // and outgoing. Will only return valid stats if the underlying PacketSocket // implements stat collection. diff --git a/source/game/StarUniverseServer.cpp b/source/game/StarUniverseServer.cpp index 321cbcc..3b04358 100644 --- a/source/game/StarUniverseServer.cpp +++ b/source/game/StarUniverseServer.cpp @@ -1520,20 +1520,30 @@ void UniverseServer::acceptConnection(UniverseConnection connection, MaybecompressionMode() != PacketCompressionMode::Enabled; + connection.setLegacy(legacyClient); + auto protocolResponse = make_shared(); + protocolResponse->setCompressionMode(PacketCompressionMode::Enabled); // Signal that we're OpenStarbound if (protocolRequest->requestProtocolVersion != StarProtocolVersion) { Logger::warn("UniverseServer: client connection aborted, unsupported protocol version {}, supported version {}", protocolRequest->requestProtocolVersion, StarProtocolVersion); - connection.pushSingle(make_shared(false)); + protocolResponse->allowed = false; + connection.pushSingle(protocolResponse); connection.sendAll(clientWaitLimit); mainLocker.lock(); m_deadConnections.append({move(connection), Time::monotonicMilliseconds()}); return; } - connection.pushSingle(make_shared(true)); + protocolResponse->allowed = true; + connection.pushSingle(protocolResponse); connection.sendAll(clientWaitLimit); + String remoteAddressString = remoteAddress ? toString(*remoteAddress) : "local"; + Logger::info("UniverseServer: Awaiting connection info from {}, {} client", remoteAddressString, legacyClient ? "Starbound" : "OpenStarbound"); + connection.receiveAny(clientWaitLimit); auto clientConnect = as(connection.pullSingle()); if (!clientConnect) { @@ -1547,7 +1557,6 @@ void UniverseServer::acceptConnection(UniverseConnection connection, Maybeaccount.empty() ? strf("'{}'", clientConnect->account) : ""; - String remoteAddressString = remoteAddress ? toString(*remoteAddress) : "local"; auto connectionFail = [&](String message) { Logger::warn("UniverseServer: Login attempt failed with account '{}' as player '{}' from address {}, error: {}",