From e96d22e24e38f05e2b74a3bba1f6d42aa8c4aba9 Mon Sep 17 00:00:00 2001 From: Pavel Solodovnikov Date: Wed, 26 Feb 2025 17:24:45 +0300 Subject: [PATCH] netplay: pass compression algorithm to `IClientConnection:s` externally Make `IClientConnection` agnostic on the compression algorithm in use. Introduce a global singleton `WzCompressionProvider`, which currently is only able to create `ZlibCompressionAdapter:s`. Although, in the future we would want to pass extra configuration info to it so that users can globally specify the compression algorithm to use. Signed-off-by: Pavel Solodovnikov --- lib/netplay/client_connection.cpp | 9 ++-- lib/netplay/client_connection.h | 9 ++-- lib/netplay/listen_socket.cpp | 26 ++++++++++++ lib/netplay/listen_socket.h | 7 ++++ lib/netplay/tcp/tcp_client_connection.cpp | 4 +- lib/netplay/tcp/tcp_client_connection.h | 5 ++- lib/netplay/tcp/tcp_connection_provider.cpp | 6 ++- lib/netplay/tcp/tcp_listen_socket.cpp | 7 ++-- lib/netplay/tcp/tcp_listen_socket.h | 7 ++-- lib/netplay/wz_compression_provider.cpp | 39 +++++++++++++++++ lib/netplay/wz_compression_provider.h | 46 +++++++++++++++++++++ 11 files changed, 147 insertions(+), 18 deletions(-) create mode 100644 lib/netplay/listen_socket.cpp create mode 100644 lib/netplay/wz_compression_provider.cpp create mode 100644 lib/netplay/wz_compression_provider.h diff --git a/lib/netplay/client_connection.cpp b/lib/netplay/client_connection.cpp index 3589c52903e..45e09aa767d 100644 --- a/lib/netplay/client_connection.cpp +++ b/lib/netplay/client_connection.cpp @@ -25,11 +25,12 @@ #include "lib/netplay/pending_writes_manager.h" #include "lib/netplay/polling_util.h" #include "lib/netplay/wz_connection_provider.h" -#include "lib/netplay/zlib_compression_adapter.h" +#include "lib/netplay/wz_compression_provider.h" -IClientConnection::IClientConnection(WzConnectionProvider& connProvider) +IClientConnection::IClientConnection(WzConnectionProvider& connProvider, WzCompressionProvider& compressionProvider) : selfConnList_({ this }), connProvider_(&connProvider), + compressionProvider_(&compressionProvider), readAllDescriptorSet_(connProvider_->newDescriptorSet(PollEventType::READABLE)) {} @@ -233,9 +234,11 @@ void IClientConnection::enableCompression() return; // Nothing to do. } + ASSERT_OR_RETURN(, compressionProvider_ != nullptr, "Invalid compression provider"); + PendingWritesManager::instance().executeUnderLock([this] { - compressionAdapter_ = std::make_unique(); + compressionAdapter_ = compressionProvider_->newCompressionAdapter(); const auto initRes = compressionAdapter_->initialize(); if (!initRes.has_value()) { diff --git a/lib/netplay/client_connection.h b/lib/netplay/client_connection.h index 86eeb6c1411..d0ae6faa590 100644 --- a/lib/netplay/client_connection.h +++ b/lib/netplay/client_connection.h @@ -34,6 +34,7 @@ using nonstd::optional; class IDescriptorSet; +class WzCompressionProvider; class WzConnectionProvider; /// @@ -222,7 +223,7 @@ class IClientConnection // disposed connections. friend class PendingWritesManager; - IClientConnection(WzConnectionProvider& connProvider); + IClientConnection(WzConnectionProvider& connProvider, WzCompressionProvider& compressionProvider); // Hide the destructor so that external code cannot accidentally // `delete` the connection directly and has to use `close()` method // to dispose of the connection object. @@ -235,8 +236,10 @@ class IClientConnection // (like `readAll()` and `connectionStatus()`) to avoid extra // memory allocations. const std::vector selfConnList_; - // Connection provider used to create internal descriptor sets - WzConnectionProvider* connProvider_; + // Connection provider used to create internal descriptor sets. + WzConnectionProvider* connProvider_ = nullptr; + // Compression provider which is used to initialize compression algorithm in `enableCompression()`. + WzCompressionProvider* compressionProvider_ = nullptr; private: diff --git a/lib/netplay/listen_socket.cpp b/lib/netplay/listen_socket.cpp new file mode 100644 index 00000000000..a9157cdb248 --- /dev/null +++ b/lib/netplay/listen_socket.cpp @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + This file is part of Warzone 2100. + Copyright (C) 2025 Warzone 2100 Project + + Warzone 2100 is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Warzone 2100 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Warzone 2100; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "listen_socket.h" + +IListenSocket::IListenSocket(WzCompressionProvider& compressionProvider) + : compressionProvider_(&compressionProvider) +{} diff --git a/lib/netplay/listen_socket.h b/lib/netplay/listen_socket.h index 8a8e0b6f5f6..67230d2a307 100644 --- a/lib/netplay/listen_socket.h +++ b/lib/netplay/listen_socket.h @@ -23,6 +23,7 @@ #include class IClientConnection; +class WzCompressionProvider; /// /// Server-side listen socket abstraction. @@ -45,4 +46,10 @@ class IListenSocket /// virtual IClientConnection* accept() = 0; virtual IPVersionsMask supportedIpVersions() const = 0; + +protected: + + IListenSocket(WzCompressionProvider& compressionProvider); + + WzCompressionProvider* compressionProvider_ = nullptr; }; diff --git a/lib/netplay/tcp/tcp_client_connection.cpp b/lib/netplay/tcp/tcp_client_connection.cpp index 2aaf20f22b1..bef51408509 100644 --- a/lib/netplay/tcp/tcp_client_connection.cpp +++ b/lib/netplay/tcp/tcp_client_connection.cpp @@ -30,8 +30,8 @@ namespace tcp { -TCPClientConnection::TCPClientConnection(WzConnectionProvider& connProvider, Socket* rawSocket) - : IClientConnection(connProvider), +TCPClientConnection::TCPClientConnection(WzConnectionProvider& connProvider, WzCompressionProvider& compressionProvider, Socket* rawSocket) + : IClientConnection(connProvider, compressionProvider), socket_(rawSocket), connStatusDescriptorSet_(connProvider_->newDescriptorSet(PollEventType::READABLE)) { diff --git a/lib/netplay/tcp/tcp_client_connection.h b/lib/netplay/tcp/tcp_client_connection.h index 9bba5632f7a..7103534acd0 100644 --- a/lib/netplay/tcp/tcp_client_connection.h +++ b/lib/netplay/tcp/tcp_client_connection.h @@ -23,6 +23,7 @@ #include "lib/netplay/descriptor_set.h" #include "lib/netplay/tcp/netsocket.h" // for SOCKET +class WzCompressionProvider; class WzConnectionProvider; namespace tcp @@ -34,7 +35,7 @@ class TCPClientConnection : public IClientConnection { public: - explicit TCPClientConnection(WzConnectionProvider& connProvider, Socket* rawSocket); + explicit TCPClientConnection(WzConnectionProvider& connProvider, WzCompressionProvider& compressionProvider, Socket* rawSocket); virtual ~TCPClientConnection() override; virtual net::result sendImpl(const std::vector& data) override; @@ -55,7 +56,7 @@ class TCPClientConnection : public IClientConnection friend class TCPConnectionPollGroup; - Socket* socket_; + Socket* socket_ = nullptr; std::unique_ptr connStatusDescriptorSet_; }; diff --git a/lib/netplay/tcp/tcp_connection_provider.cpp b/lib/netplay/tcp/tcp_connection_provider.cpp index 66d7bfdf903..077d850dfb8 100644 --- a/lib/netplay/tcp/tcp_connection_provider.cpp +++ b/lib/netplay/tcp/tcp_connection_provider.cpp @@ -26,6 +26,8 @@ #include "lib/netplay/tcp/tcp_listen_socket.h" #include "lib/netplay/open_connection_result.h" +#include "lib/netplay/wz_compression_provider.h" + #include "lib/framework/wzapp.h" #ifdef WZ_OS_WIN @@ -64,7 +66,7 @@ net::result TCPConnectionProvider::openListenSocket(uint16_t por { return tl::make_unexpected(res.error()); } - return new TCPListenSocket(*this, res.value()); + return new TCPListenSocket(*this, WzCompressionProvider::Instance(), res.value()); } net::result TCPConnectionProvider::openClientConnectionAny(const IConnectionAddress& addr, unsigned timeout) @@ -81,7 +83,7 @@ net::result TCPConnectionProvider::openClientConnectionAny(c { return tl::make_unexpected(res.error()); } - return new TCPClientConnection(*this, res.value()); + return new TCPClientConnection(*this, WzCompressionProvider::Instance(), res.value()); } IConnectionPollGroup* TCPConnectionProvider::newConnectionPollGroup() diff --git a/lib/netplay/tcp/tcp_listen_socket.cpp b/lib/netplay/tcp/tcp_listen_socket.cpp index 180e605c62e..a2598d1959c 100644 --- a/lib/netplay/tcp/tcp_listen_socket.cpp +++ b/lib/netplay/tcp/tcp_listen_socket.cpp @@ -26,8 +26,9 @@ namespace tcp { -TCPListenSocket::TCPListenSocket(WzConnectionProvider& connProvider, Socket* rawSocket) - : listenSocket_(rawSocket), +TCPListenSocket::TCPListenSocket(WzConnectionProvider& connProvider, WzCompressionProvider& compressionProvider, Socket* rawSocket) + : IListenSocket(compressionProvider), + listenSocket_(rawSocket), connProvider_(&connProvider) {} @@ -51,7 +52,7 @@ IClientConnection* TCPListenSocket::accept() { return nullptr; } - return new TCPClientConnection(*connProvider_, s); + return new TCPClientConnection(*connProvider_, *compressionProvider_, s); } IListenSocket::IPVersionsMask TCPListenSocket::supportedIpVersions() const diff --git a/lib/netplay/tcp/tcp_listen_socket.h b/lib/netplay/tcp/tcp_listen_socket.h index d1329c11e29..f48e0cf7fd4 100644 --- a/lib/netplay/tcp/tcp_listen_socket.h +++ b/lib/netplay/tcp/tcp_listen_socket.h @@ -23,6 +23,7 @@ #include "lib/netplay/listen_socket.h" +class WzCompressionProvider; class WzConnectionProvider; namespace tcp @@ -34,7 +35,7 @@ class TCPListenSocket : public IListenSocket { public: - explicit TCPListenSocket(WzConnectionProvider& connProvider, tcp::Socket* rawSocket); + explicit TCPListenSocket(WzConnectionProvider& connProvider, WzCompressionProvider& compressionProvider, tcp::Socket* rawSocket); virtual ~TCPListenSocket() override; virtual IClientConnection* accept() override; @@ -42,8 +43,8 @@ class TCPListenSocket : public IListenSocket private: - tcp::Socket* listenSocket_; - WzConnectionProvider* connProvider_; + tcp::Socket* listenSocket_ = nullptr; + WzConnectionProvider* connProvider_ = nullptr; }; } // namespace tcp diff --git a/lib/netplay/wz_compression_provider.cpp b/lib/netplay/wz_compression_provider.cpp new file mode 100644 index 00000000000..ffae1a8aa08 --- /dev/null +++ b/lib/netplay/wz_compression_provider.cpp @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + This file is part of Warzone 2100. + Copyright (C) 2025 Warzone 2100 Project + + Warzone 2100 is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Warzone 2100 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Warzone 2100; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "wz_compression_provider.h" + +#include "lib/netplay/zlib_compression_adapter.h" + +WzCompressionProvider& WzCompressionProvider::Instance() +{ + static WzCompressionProvider instance; + return instance; +} + +std::unique_ptr WzCompressionProvider::newCompressionAdapter() +{ + // Only support Zlib for the time being. + // TODO: in the future, we might want to support more compression algorithms, e.g. Zstd. + // In this case, we would need to somehow configure `WzCompressionAdapter` from + // the global WZ config to specify compression algorithm to use. + return std::make_unique(); +} diff --git a/lib/netplay/wz_compression_provider.h b/lib/netplay/wz_compression_provider.h new file mode 100644 index 00000000000..e8e1c84bd42 --- /dev/null +++ b/lib/netplay/wz_compression_provider.h @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + This file is part of Warzone 2100. + Copyright (C) 2025 Warzone 2100 Project + + Warzone 2100 is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + Warzone 2100 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Warzone 2100; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include + +class ICompressionAdapter; + +/// +/// This class provides is responsible for creating `ICompressionAdapter:s`, +/// which are thin wrappers over some compression algorithm, intended for +/// use in `IClientConnection` to provide compression over raw net messages. +/// +class WzCompressionProvider +{ +public: + + static WzCompressionProvider& Instance(); + + std::unique_ptr newCompressionAdapter(); + +private: + + WzCompressionProvider() = default; + WzCompressionProvider(const WzCompressionProvider&) = delete; + WzCompressionProvider(WzCompressionProvider&&) = delete; +};