From 19f7b3d77e7fa87bd3354eae975fb7cd4a49d0ea Mon Sep 17 00:00:00 2001 From: Totto16 Date: Wed, 5 Apr 2023 17:46:22 +0200 Subject: [PATCH 01/45] WIP: starting online multiplier support --- CMakeLists.txt | 8 +- meson.build | 12 +++ src/application.hpp | 4 +- src/input.cpp | 7 ++ src/input.hpp | 17 ++++- src/main.cpp | 4 +- src/meson.build | 52 +++++++------ src/network/connection_manager.cpp | 2 + src/network/connection_manager.hpp | 16 ++++ src/network/meson.build | 4 + src/network/network_manager.cpp | 119 +++++++++++++++++++++++++++++ src/network/network_manager.hpp | 25 ++++++ src/sdl_context.cpp | 16 +++- src/tetris_application.hpp | 84 ++++++++++++++++++-- subprojects/.gitignore | 3 + subprojects/sdl2_net.wrap | 12 +++ subprojects/tl-expected.wrap | 12 +++ vcpkg.json | 6 ++ 18 files changed, 365 insertions(+), 38 deletions(-) create mode 100644 src/network/connection_manager.cpp create mode 100644 src/network/connection_manager.hpp create mode 100644 src/network/meson.build create mode 100644 src/network/network_manager.cpp create mode 100644 src/network/network_manager.hpp create mode 100644 subprojects/.gitignore create mode 100644 subprojects/sdl2_net.wrap create mode 100644 subprojects/tl-expected.wrap diff --git a/CMakeLists.txt b/CMakeLists.txt index 49ec8f41..ab442291 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 23) find_package(SDL2 CONFIG REQUIRED) find_package(SDL2_ttf CONFIG REQUIRED) +find_package(SDL2_net CONFIG REQUIRED) add_executable(oopetris src/main.cpp @@ -40,7 +41,10 @@ add_executable(oopetris src/event_dispatcher.cpp src/event_dispatcher.hpp src/event_listener.hpp - src/input.cpp) + src/input.cpp + src/network/network_manager.cpp + src/network/connection_manager.cpp +) if(MSVC) target_compile_options(oopetris PRIVATE /W4 /WX) @@ -55,3 +59,5 @@ target_link_libraries(oopetris ) target_link_libraries(oopetris PRIVATE $,SDL2_ttf::SDL2_ttf,SDL2_ttf::SDL2_ttf-static>) + +target_link_libraries(oopetris PRIVATE $,SDL2_net::SDL2_net,SDL2_net::SDL2_net-static>) diff --git a/meson.build b/meson.build index c4950518..6a1e4fe8 100644 --- a/meson.build +++ b/meson.build @@ -39,7 +39,19 @@ else deps += sdl2_ttf_dep endif +sdl2_ttf_dep = dependency('sdl2_net', required: false) +if not sdl2_ttf_dep.found() + deps += dependency( + 'sdl2_net', + required: true, + fallback: 'sdl2_net', + ) +else + deps += sdl2_ttf_dep +endif + deps += dependency('tl-optional', required: true, fallback: ['tl-optional']) +deps += dependency('tl-expected', required: true, fallback: ['tl-expected']) inc_dirs = [] src_files = [] diff --git a/src/application.hpp b/src/application.hpp index 6be6b39b..40ec2f8c 100644 --- a/src/application.hpp +++ b/src/application.hpp @@ -1,10 +1,10 @@ #pragma once +#include "event_dispatcher.hpp" +#include "event_listener.hpp" #include "renderer.hpp" #include "sdl_context.hpp" #include "window.hpp" -#include "event_dispatcher.hpp" -#include "event_listener.hpp" struct Application : public EventListener { private: diff --git a/src/input.cpp b/src/input.cpp index 625f331c..7c04c0b6 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -60,3 +60,10 @@ void KeyboardInput::handle_event(const SDL_Event& event) { } } } + + +void OnlineInput::update() { + Input::update(); + + //TODO +} diff --git a/src/input.hpp b/src/input.hpp index add57806..500f60fb 100644 --- a/src/input.hpp +++ b/src/input.hpp @@ -1,7 +1,9 @@ #pragma once #include "event_listener.hpp" +#include "network/connection_manager.hpp" #include +#include #include #include @@ -24,7 +26,7 @@ struct Input { explicit Input(GameManager* target_game_manager) : m_target_game_manager{ target_game_manager } { } public: - virtual void update() {} + virtual void update() { } virtual ~Input() = default; }; @@ -47,3 +49,16 @@ struct KeyboardInput : public Input, public EventListener { void handle_event(const SDL_Event& event) override; }; + +struct OnlineInput : public Input { +private: + Connection m_connection; + //TODO + +public: + explicit OnlineInput(GameManager* target_game_manager, Connection connection) + : Input{ target_game_manager }, + m_connection{ connection } { } + + void update() override; +}; diff --git a/src/main.cpp b/src/main.cpp index 56847b97..4365c9fb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,7 +4,9 @@ int main(int, char**) { static constexpr int target_fps = 60; - TetrisApplication tetris_app; + // TODO show buttons to select from one of the RunTypes + + TetrisApplication tetris_app = TetrisApplication{ RunType::OnlineMultiplayer, 2 }; tetris_app.run(target_fps); return 0; diff --git a/src/meson.build b/src/meson.build index 027cdc74..8deabcea 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,38 +1,40 @@ src_files += files( - 'main.cpp', - 'application.hpp', 'application.cpp', + 'application.hpp', + 'bag.cpp', + 'bag.hpp', + 'color.hpp', + 'event_dispatcher.cpp', + 'event_dispatcher.hpp', + 'event_listener.hpp', + 'font.cpp', + 'font.hpp', + 'game_manager.cpp', + 'game_manager.hpp', + 'grid.cpp', + 'grid.hpp', + 'input.cpp', + 'input.hpp', + 'main.cpp', + 'mino.cpp', + 'mino.hpp', + 'point.hpp', + 'rect.hpp', 'renderer.cpp', 'renderer.hpp', 'sdl_context.cpp', 'sdl_context.hpp', - 'window.cpp', - 'window.hpp', - 'color.hpp', - 'rect.hpp', - 'point.hpp', - 'tetromino.hpp', - 'mino.hpp', - 'grid.hpp', - 'tetromino_type.hpp', - 'tetromino_type.cpp', - 'mino.cpp', - 'game_manager.cpp', - 'game_manager.hpp', 'tetris_application.cpp', 'tetris_application.hpp', - 'bag.cpp', - 'bag.hpp', - 'font.cpp', - 'font.hpp', + 'tetromino.hpp', + 'tetromino_type.cpp', + 'tetromino_type.hpp', 'text.cpp', 'text.hpp', - 'grid.cpp', - 'input.hpp', - 'event_dispatcher.cpp', - 'event_dispatcher.hpp', - 'event_listener.hpp', - 'input.cpp', + 'window.cpp', + 'window.hpp', ) inc_dirs += include_directories('.') + +subdir('network') diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp new file mode 100644 index 00000000..75fd2f88 --- /dev/null +++ b/src/network/connection_manager.cpp @@ -0,0 +1,2 @@ + +#include "connection_manager.hpp" \ No newline at end of file diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp new file mode 100644 index 00000000..f86b6b3b --- /dev/null +++ b/src/network/connection_manager.hpp @@ -0,0 +1,16 @@ + + + +#pragma once + + +struct SocketHandle { + // +}; + + + +struct Connection +{ + /* data */ +}; diff --git a/src/network/meson.build b/src/network/meson.build new file mode 100644 index 00000000..fe297a08 --- /dev/null +++ b/src/network/meson.build @@ -0,0 +1,4 @@ +src_files += files('connection_manager.cpp', 'network_manager.cpp') + +inc_dirs += include_directories('.') + diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp new file mode 100644 index 00000000..ade8a2d1 --- /dev/null +++ b/src/network/network_manager.cpp @@ -0,0 +1,119 @@ + +#include "network_manager.hpp" +#include +#include +#include + + +NetworkManager::NetworkManager() : m_connections{ std::vector{} } {}; + +// started from example from: https://gist.github.com/psqq/b92243f2149fcf4dd46370d4c0b5fef9 +MaybeConnection NetworkManager::add_connection(NetworkType type) { + + if (type == NetworkType::Server) { + + bool done = false; + TCPsocket server, client; + IPaddress ip; + if (SDLNet_ResolveHost(&ip, NULL, NetworkManager::Port) == -1) { + std::string error = "SDLNet_ResolveHost: " + std::string{ SDLNet_GetError() }; + return tl::make_unexpected(error); + } + server = SDLNet_TCP_Open(&ip); + if (!server) { + std::string error = "SDLNet_TCP_Open: " + std::string{ SDLNet_GetError() }; + return tl::make_unexpected(error); + } + while (!done) { + /* try to accept a connection */ + client = SDLNet_TCP_Accept(server); + if (!client) { /* no connection accepted */ + /*printf("SDLNet_TCP_Accept: %s\n",SDLNet_GetError()); */ + SDL_Delay(100); /*sleep 1/10th of a second */ + continue; + } + + /* get the clients IP and port number */ + IPaddress* remoteip; + remoteip = SDLNet_TCP_GetPeerAddress(client); + if (!remoteip) { + std::string error = "SDLNet_TCP_GetPeerAddress: " + std::string{ SDLNet_GetError() }; + return tl::make_unexpected(error); + } + + /* print out the clients IP and port number */ + Uint32 ipaddr; + ipaddr = SDL_SwapBE32(remoteip->host); + printf("Accepted a connection from %d.%d.%d.%d port %hu\n", ipaddr >> 24, (ipaddr >> 16) & 0xff, + (ipaddr >> 8) & 0xff, ipaddr & 0xff, remoteip->port); + + while (1) { + /* read the buffer from client */ + char message[1024]; + int len = SDLNet_TCP_Recv(client, message, 1024); + if (!len) { + std::string error = "SDLNet_TCP_Recv: " + std::string{ SDLNet_GetError() }; + return tl::make_unexpected(error); + } + /* print out the message */ + printf("Received: %.*s\n", len, message); + if (message[0] == 'q') { + printf("Disconecting on a q\n"); + break; + } + if (message[0] == 'Q') { + printf("Closing server on a Q.\n"); + done = true; + break; + } + } + SDLNet_TCP_Close(client); + } + } else if (type == NetworkType::Client) { + printf("Starting client...\n"); + IPaddress ip; + TCPsocket tcpsock; + + if (SDLNet_ResolveHost(&ip, NetworkManager::ServerHost, NetworkManager::Port) == -1) { + std::string error = "SDLNet_ResolveHost: " + std::string{ SDLNet_GetError() }; + return tl::make_unexpected(error); + } + + tcpsock = SDLNet_TCP_Open(&ip); + if (!tcpsock) { + std::string error = "SDLNet_TCP_Open: " + std::string{ SDLNet_GetError() }; + return tl::make_unexpected(error); + } + + while (1) { + printf("message: "); + char message[1024]; + [[maybe_unused]] auto result = fgets(message, 1024, stdin); + int len = strlen(message); + + /* strip the newline */ + message[len - 1] = '\0'; + + if (len) { + int result; + + /* print out the message */ + printf("Sending: %.*s\n", len, message); + + result = SDLNet_TCP_Send(tcpsock, message, len); /* add 1 for the NULL */ + if (result < len) { + std::string error = "SDLNet_TCP_Send: " + std::string{ SDLNet_GetError() }; + return tl::make_unexpected(error); + } + } + + if (len == 2 && tolower(message[0]) == 'q') { + break; + } + } + + SDLNet_TCP_Close(tcpsock); + } + + return {}; +} \ No newline at end of file diff --git a/src/network/network_manager.hpp b/src/network/network_manager.hpp new file mode 100644 index 00000000..5282543f --- /dev/null +++ b/src/network/network_manager.hpp @@ -0,0 +1,25 @@ + + + +#pragma once + +#include "connection_manager.hpp" +#include +#include +#include +#include + +enum class NetworkType { Server, Client }; + +using MaybeConnection = tl::expected; + +struct NetworkManager { +private: + std::vector m_connections; + static constexpr const char* ServerHost = "oopetris.totto.tk"; + static constexpr const int Port = 789; + +public: + explicit NetworkManager(); + MaybeConnection add_connection(NetworkType type); +}; \ No newline at end of file diff --git a/src/sdl_context.cpp b/src/sdl_context.cpp index ace5ef54..cebb175f 100644 --- a/src/sdl_context.cpp +++ b/src/sdl_context.cpp @@ -1,13 +1,25 @@ #include "sdl_context.hpp" #include +#include #include SdlContext::SdlContext() { - SDL_Init(SDL_INIT_VIDEO); - TTF_Init(); + if (SDL_Init(SDL_INIT_VIDEO) != 0) { + printf("SDL_Init: %s\n", SDL_GetError()); + exit(2); + } + if (TTF_Init() != 0) { + printf("TTF_Init: %s\n", SDL_GetError()); + exit(2); + } + if (SDLNet_Init() == -1) { + printf("SDLNet_Init: %s\n", SDLNet_GetError()); + exit(2); + } } SdlContext::~SdlContext() { + SDLNet_Quit(); TTF_Quit(); SDL_Quit(); } diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index 797098ba..46b28f7e 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -2,28 +2,82 @@ #include "application.hpp" #include "game_manager.hpp" +#include "network/network_manager.hpp" #include "tetromino_type.hpp" #include +#include #include +#include + + +enum class RunType { OnlineMultiplayer, LocalMultiplayer, SinglePlayer, VersusKI }; struct TetrisApplication : public Application { private: - enum class InputMethod { - Keyboard, - }; + enum class InputMethod { Keyboard, OnlineNetwork, LocalNetwork, KI }; std::vector> m_game_managers; std::vector> m_inputs; + NetworkManager m_network_manager; + RunType m_run_type; + int m_num_players; public: static constexpr int width = 800; static constexpr int height = 600; - TetrisApplication() : Application{ "TetrisApplication", WindowPosition::Centered, width, height } { - static constexpr auto num_players = 1; + TetrisApplication(RunType run_type, int num_players) + : Application{ "TetrisApplication", WindowPosition::Centered, width, height }, + m_network_manager{ NetworkManager{} }, + m_run_type{ run_type }, + m_num_players{ num_players } { + + auto input_methods = std::vector{}; + switch (run_type) { + case RunType::OnlineMultiplayer: { + + if (num_players < 2 || num_players > 4) { + throw std::range_error{ "The mode OnlineMultiplayer only allows between 2 and 4 players" }; + } + input_methods.push_back(InputMethod::Keyboard); + for (int i = 0; i < num_players - 1; ++i) { + input_methods.push_back(InputMethod::OnlineNetwork); + } + break; + } + case RunType::LocalMultiplayer: { + if (num_players < 2 || num_players > 4) { + throw std::range_error{ "The mode OnlineMultiplayer only allows between 2 and 4 players" }; + } + input_methods.push_back(InputMethod::Keyboard); + for (int i = 0; i < num_players - 1; ++i) { + input_methods.push_back(InputMethod::LocalNetwork); + } + break; + } + case RunType::SinglePlayer: { + if (num_players != 1) { + throw std::range_error{ "The mode SinglePlayer only allows 1 player" }; + } + input_methods.push_back(InputMethod::Keyboard); + break; + } + case RunType::VersusKI: { + if (num_players < 2 || num_players > 4) { + throw std::range_error{ "The mode VersusKI only allows between 2 and 4 players" }; + } + input_methods.push_back(InputMethod::Keyboard); + for (int i = 0; i < num_players - 1; ++i) { + input_methods.push_back(InputMethod::KI); + } + break; + } + } + for (int i = 0; i < num_players; ++i) { m_game_managers.push_back(std::make_unique()); - m_inputs.push_back(create_input(InputMethod::Keyboard, m_game_managers.back().get())); + // TODO, create input has to connect the players for online mode, that process has to be made better and clearer (with waiting screen / lobby) + m_inputs.push_back(create_input(input_methods.at(i), m_game_managers.back().get())); } for (const auto& game_manager : m_game_managers) { game_manager->spawn_next_tetromino(); @@ -56,6 +110,24 @@ struct TetrisApplication : public Application { m_event_dispatcher.register_listener(keyboard_input.get()); return keyboard_input; } + case InputMethod::OnlineNetwork: { + MaybeConnection connection = m_network_manager.add_connection(NetworkType::Client); + if (connection.has_value()) { + auto online_input = std::make_unique(associated_game_manager, connection.value()); + return online_input; + } + std::cout << "Error in getting a connection for InputMethod::OnlineNetwork: " << connection.error() + << "\n"; + return {}; + } + case InputMethod::LocalNetwork: { + assert(false and "unreachable"); + return {}; + } + case InputMethod::KI: { + assert(false and "unreachable"); + return {}; + } } assert(false and "unreachable"); return {}; diff --git a/subprojects/.gitignore b/subprojects/.gitignore new file mode 100644 index 00000000..e869c2ff --- /dev/null +++ b/subprojects/.gitignore @@ -0,0 +1,3 @@ +* +!.gitignore +!*.wrap \ No newline at end of file diff --git a/subprojects/sdl2_net.wrap b/subprojects/sdl2_net.wrap new file mode 100644 index 00000000..732be1c6 --- /dev/null +++ b/subprojects/sdl2_net.wrap @@ -0,0 +1,12 @@ +[wrap-file] +directory = SDL2_net-2.2.0 +source_url = https://www.libsdl.org/projects/SDL_net/release/SDL2_net-2.2.0.tar.gz +source_filename = SDL2_net-2.2.0.tar.gz +source_hash = 4e4a891988316271974ff4e9585ed1ef729a123d22c08bd473129179dc857feb +patch_filename = sdl2_net_2.2.0-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/sdl2_net_2.2.0-1/get_patch +patch_hash = f63aea40e83c2b4d97d63d97d94c76e73fdaf5923b71a319938cbebea21d7834 +wrapdb_version = 2.2.0-1 + +[provide] +sdl2_net = sdl2_net_dep diff --git a/subprojects/tl-expected.wrap b/subprojects/tl-expected.wrap new file mode 100644 index 00000000..cf5e663e --- /dev/null +++ b/subprojects/tl-expected.wrap @@ -0,0 +1,12 @@ +[wrap-file] +directory = expected-1.0.0 +source_url = https://github.com/TartanLlama/expected/archive/v1.0.0.tar.gz +source_filename = expected-1.0.0.tar.gz +source_hash = 8f5124085a124113e75e3890b4e923e3a4de5b26a973b891b3deb40e19c03cee +patch_filename = tl-expected_1.0.0-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/tl-expected_1.0.0-1/get_patch +patch_hash = 8b421837c67357a22bf105213c459786df0fe35a3bc8dc28b7fe9c44c7da71e3 + +[provide] +tl-expected = tl_expected_dep + diff --git a/vcpkg.json b/vcpkg.json index c8668583..cd365534 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -11,5 +11,11 @@ }, { "name" : "tl-optional", "version>=" : "2021-05-02" + }, { + "name" : "tl-expected", + "version>=" : "2022-11-24" + }, { + "name" : "sdl2-net", + "version>=" : "2.2.0" } ] } From 19631be083ce9a093cf8ba08a22b154bea25b4d0 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Wed, 5 Apr 2023 19:45:08 +0200 Subject: [PATCH 02/45] added SDL2 Wrapper classes --- CMakeLists.txt | 1 + src/network/connection_manager.cpp | 72 +++++++++++++- src/network/connection_manager.hpp | 29 +++++- src/network/meson.build | 6 +- src/network/network_address_old.cpp | 40 ++++++++ src/network/network_address_old.hpp | 24 +++++ src/network/network_manager.cpp | 130 ++++++-------------------- src/network/network_manager.hpp | 13 ++- src/network/network_transportable.cpp | 11 +++ src/network/network_transportable.hpp | 21 +++++ src/tetris_application.hpp | 2 +- 11 files changed, 236 insertions(+), 113 deletions(-) create mode 100644 src/network/network_address_old.cpp create mode 100644 src/network/network_address_old.hpp create mode 100644 src/network/network_transportable.cpp create mode 100644 src/network/network_transportable.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ab442291..72ae43af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ add_executable(oopetris src/input.cpp src/network/network_manager.cpp src/network/connection_manager.cpp + src/network/network_transportable.cpp ) if(MSVC) diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index 75fd2f88..f9408f84 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -1,2 +1,72 @@ -#include "connection_manager.hpp" \ No newline at end of file +#include "connection_manager.hpp" +#include "network_manager.hpp" +#include "network_transportable.hpp" +#include +#include + + +Connection::Connection(TCPsocket socket) + : m_socket{ socket } { + + }; + +Connection::~Connection() { + SDLNet_TCP_Close(m_socket); +} + +tl::optional Connection::send_data(const Transportable transportable) { + + + auto [message, length] = Transportable::serialize(transportable); + + const auto result = SDLNet_TCP_Send(m_socket, message, length); + if (result == -1) { + return tl::make_optional("SDLNet_TCP_Send: invalid socket"); + } + + if ((std::size_t) result != length) { + std::string error = "SDLNet_TCP_Send: " + std::string{ SDLNet_GetError() }; + return tl::make_optional(error); + } + + return {}; +}; + + +Server::Server(TCPsocket socket) : m_socket{ socket } {}; + +Server::~Server() { + SDLNet_TCP_Close(m_socket); +} + + +tl::optional Server::try_get_client() { + + TCPsocket client; + /* try to accept a connection */ + client = SDLNet_TCP_Accept(m_socket); + if (client) { /* no connection accepted */ + return Connection{ client }; + } + return {}; +} + +tl::optional Server::get_client(std::size_t ms_delay, std::size_t abort_after) { + auto start_time = SDL_GetTicks64(); + while (true) { + /* try to accept a connection */ + auto client = this->try_get_client(); + if (client.has_value()) { + return client; + } + + auto elapsed_time = SDL_GetTicks64() - start_time; + if (elapsed_time >= abort_after) { + return {}; + } + + SDL_Delay(ms_delay); + continue; + } +} \ No newline at end of file diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index f86b6b3b..b8966f70 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -4,13 +4,32 @@ #pragma once -struct SocketHandle { - // +#include "network_transportable.hpp" +#include +#include +#include + + +struct Connection { + +private: + TCPsocket m_socket; + +public: + explicit Connection(TCPsocket socket); + ~Connection(); + tl::optional send_data(const Transportable transportable); }; +struct Server { + +private: + TCPsocket m_socket; -struct Connection -{ - /* data */ +public: + explicit Server(TCPsocket socket); + ~Server(); + tl::optional try_get_client(); + tl::optional get_client(std::size_t ms_delay = 100, std::size_t abort_after = 60 * 1000); }; diff --git a/src/network/meson.build b/src/network/meson.build index fe297a08..c2512469 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -1,4 +1,8 @@ -src_files += files('connection_manager.cpp', 'network_manager.cpp') +src_files += files( + 'connection_manager.cpp', + 'network_manager.cpp', + 'network_transportable.cpp', +) inc_dirs += include_directories('.') diff --git a/src/network/network_address_old.cpp b/src/network/network_address_old.cpp new file mode 100644 index 00000000..7b71075d --- /dev/null +++ b/src/network/network_address_old.cpp @@ -0,0 +1,40 @@ + +#include "network_address.hpp" +#include "network_transportable.hpp" +#include +#include +#include +#include + +NetworkAddress::NetworkAddress(TCPsocket socket) : m_socket{ std::move(socket) } { } + +NetworkAddress::~NetworkAddress() { + SDLNet_TCP_Close(m_socket); +} + +tl::optional NetworkAddress::send_data(RawBytes bytes) { + auto [message, length] = bytes; + const auto result = SDLNet_TCP_Send(m_socket, message, length); + if (result == -1) { + return tl::make_optional("SDLNet_TCP_Send: invalid socket"); + } + + if (result != length) { + std::string error = "SDLNet_TCP_Send: " + std::string{ SDLNet_GetError() }; + return tl::make_optional(error); + } + + return {}; +} + + +tl::optional NetworkAddress::try_accept() { + + TCPsocket client; + /* try to accept a connection */ + client = SDLNet_TCP_Accept(m_socket); + if (client) { /* no connection accepted */ + return Connection{ client }; + } + return {}; +} diff --git a/src/network/network_address_old.hpp b/src/network/network_address_old.hpp new file mode 100644 index 00000000..24c53e7d --- /dev/null +++ b/src/network/network_address_old.hpp @@ -0,0 +1,24 @@ + + + +#pragma once + +#include "connection_manager.hpp" +#include "network_transportable.hpp" +#include +#include +#include + +struct NetworkAddress { +private: + TCPsocket m_socket; + +public: + NetworkAddress(TCPsocket socket); + ~NetworkAddress(); + // client (Connection) socket only method! + tl::optional send_data(RawBytes bytes); + + // server (Server) socket only method + tl::optional try_accept(); +}; diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index ade8a2d1..1380b320 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -1,119 +1,47 @@ #include "network_manager.hpp" +#include "network_transportable.hpp" #include #include #include +#include NetworkManager::NetworkManager() : m_connections{ std::vector{} } {}; -// started from example from: https://gist.github.com/psqq/b92243f2149fcf4dd46370d4c0b5fef9 -MaybeConnection NetworkManager::add_connection(NetworkType type) { - if (type == NetworkType::Server) { +MaybeConnection NetworkManager::try_connect(const char* host, std::size_t port) { - bool done = false; - TCPsocket server, client; - IPaddress ip; - if (SDLNet_ResolveHost(&ip, NULL, NetworkManager::Port) == -1) { - std::string error = "SDLNet_ResolveHost: " + std::string{ SDLNet_GetError() }; - return tl::make_unexpected(error); - } - server = SDLNet_TCP_Open(&ip); - if (!server) { - std::string error = "SDLNet_TCP_Open: " + std::string{ SDLNet_GetError() }; - return tl::make_unexpected(error); - } - while (!done) { - /* try to accept a connection */ - client = SDLNet_TCP_Accept(server); - if (!client) { /* no connection accepted */ - /*printf("SDLNet_TCP_Accept: %s\n",SDLNet_GetError()); */ - SDL_Delay(100); /*sleep 1/10th of a second */ - continue; - } + IPaddress ip; + TCPsocket tcpsock; - /* get the clients IP and port number */ - IPaddress* remoteip; - remoteip = SDLNet_TCP_GetPeerAddress(client); - if (!remoteip) { - std::string error = "SDLNet_TCP_GetPeerAddress: " + std::string{ SDLNet_GetError() }; - return tl::make_unexpected(error); - } - - /* print out the clients IP and port number */ - Uint32 ipaddr; - ipaddr = SDL_SwapBE32(remoteip->host); - printf("Accepted a connection from %d.%d.%d.%d port %hu\n", ipaddr >> 24, (ipaddr >> 16) & 0xff, - (ipaddr >> 8) & 0xff, ipaddr & 0xff, remoteip->port); - - while (1) { - /* read the buffer from client */ - char message[1024]; - int len = SDLNet_TCP_Recv(client, message, 1024); - if (!len) { - std::string error = "SDLNet_TCP_Recv: " + std::string{ SDLNet_GetError() }; - return tl::make_unexpected(error); - } - /* print out the message */ - printf("Received: %.*s\n", len, message); - if (message[0] == 'q') { - printf("Disconecting on a q\n"); - break; - } - if (message[0] == 'Q') { - printf("Closing server on a Q.\n"); - done = true; - break; - } - } - SDLNet_TCP_Close(client); - } - } else if (type == NetworkType::Client) { - printf("Starting client...\n"); - IPaddress ip; - TCPsocket tcpsock; - - if (SDLNet_ResolveHost(&ip, NetworkManager::ServerHost, NetworkManager::Port) == -1) { - std::string error = "SDLNet_ResolveHost: " + std::string{ SDLNet_GetError() }; - return tl::make_unexpected(error); - } - - tcpsock = SDLNet_TCP_Open(&ip); - if (!tcpsock) { - std::string error = "SDLNet_TCP_Open: " + std::string{ SDLNet_GetError() }; - return tl::make_unexpected(error); - } - - while (1) { - printf("message: "); - char message[1024]; - [[maybe_unused]] auto result = fgets(message, 1024, stdin); - int len = strlen(message); - - /* strip the newline */ - message[len - 1] = '\0'; - - if (len) { - int result; + if (SDLNet_ResolveHost(&ip, host, port) == -1) { + std::string error = "SDLNet_ResolveHost: " + std::string{ SDLNet_GetError() }; + return tl::make_unexpected(error); + } - /* print out the message */ - printf("Sending: %.*s\n", len, message); + tcpsock = SDLNet_TCP_Open(&ip); + if (!tcpsock) { + std::string error = "SDLNet_TCP_Open: " + std::string{ SDLNet_GetError() }; + return tl::make_unexpected(error); + } - result = SDLNet_TCP_Send(tcpsock, message, len); /* add 1 for the NULL */ - if (result < len) { - std::string error = "SDLNet_TCP_Send: " + std::string{ SDLNet_GetError() }; - return tl::make_unexpected(error); - } - } + return Connection{ tcpsock }; +} - if (len == 2 && tolower(message[0]) == 'q') { - break; - } - } +MaybeServer NetworkManager::spawn_server(std::size_t port) { - SDLNet_TCP_Close(tcpsock); + TCPsocket server; + IPaddress ip; + if (SDLNet_ResolveHost(&ip, NULL, port) == -1) { + std::string error = "SDLNet_ResolveHost: " + std::string{ SDLNet_GetError() }; + return tl::make_unexpected(error); + } + server = SDLNet_TCP_Open(&ip); + if (!server) { + std::string error = "SDLNet_TCP_Open: " + std::string{ SDLNet_GetError() }; + return tl::make_unexpected(error); } - return {}; -} \ No newline at end of file + return Server{ server }; +} diff --git a/src/network/network_manager.hpp b/src/network/network_manager.hpp index 5282543f..0314dddf 100644 --- a/src/network/network_manager.hpp +++ b/src/network/network_manager.hpp @@ -4,22 +4,27 @@ #pragma once #include "connection_manager.hpp" +#include "network_transportable.hpp" +#include +#include +#include #include #include #include #include -enum class NetworkType { Server, Client }; using MaybeConnection = tl::expected; +using MaybeServer = tl::expected; struct NetworkManager { private: std::vector m_connections; - static constexpr const char* ServerHost = "oopetris.totto.tk"; + static constexpr const char* ServerHost = "localhost"; static constexpr const int Port = 789; public: explicit NetworkManager(); - MaybeConnection add_connection(NetworkType type); -}; \ No newline at end of file + MaybeConnection try_connect(const char* host = NetworkManager::ServerHost, std::size_t port = NetworkManager::Port); + MaybeServer spawn_server(std::size_t port = NetworkManager::Port); +}; diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp new file mode 100644 index 00000000..3bf1a618 --- /dev/null +++ b/src/network/network_transportable.cpp @@ -0,0 +1,11 @@ + + +#include "network_transportable.hpp" + + +RawBytes Transportable::serialize(const Transportable transportable) { + + (void) transportable; + //TODO implement + return RawBytes{ nullptr, 0 }; +} diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp new file mode 100644 index 00000000..19445953 --- /dev/null +++ b/src/network/network_transportable.hpp @@ -0,0 +1,21 @@ + + +#pragma once + +#include +#include +#include + +using RawBytes = std::pair; + +/* abstract*/ class Transportable { +public: + // every class has to have such a serialUUID in some way + //TODO enforce this in some compile time way, that they are unique! + size_t serialUUID = 0; + static RawBytes serialize(const Transportable transportable); + +protected: + explicit Transportable() { } + static uint32_t checksum(RawBytes bytes); +}; \ No newline at end of file diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index 46b28f7e..15ef9c86 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -111,7 +111,7 @@ struct TetrisApplication : public Application { return keyboard_input; } case InputMethod::OnlineNetwork: { - MaybeConnection connection = m_network_manager.add_connection(NetworkType::Client); + MaybeConnection connection = m_network_manager.try_connect(); if (connection.has_value()) { auto online_input = std::make_unique(associated_game_manager, connection.value()); return online_input; From 579204d7c21932135ab3f1b2ba5653140ae31aa7 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Wed, 5 Apr 2023 21:51:50 +0200 Subject: [PATCH 03/45] implemented PlayerManager --- CMakeLists.txt | 58 +++++++++---------- src/input.cpp | 2 +- src/local_multiplayer.cpp | 77 ++++++++++++++++++++++++++ src/local_multiplayer.hpp | 25 +++++++++ src/main.cpp | 26 ++++++++- src/meson.build | 2 + src/network/network_manager.hpp | 2 +- src/play_manager.cpp | 30 ++++++++++ src/play_manager.hpp | 30 ++++++++++ src/sdl_context.cpp | 1 + src/tetris_application.hpp | 98 ++++++--------------------------- 11 files changed, 238 insertions(+), 113 deletions(-) create mode 100644 src/local_multiplayer.cpp create mode 100644 src/local_multiplayer.hpp create mode 100644 src/play_manager.cpp create mode 100644 src/play_manager.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 72ae43af..ea872666 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,43 +8,45 @@ find_package(SDL2_ttf CONFIG REQUIRED) find_package(SDL2_net CONFIG REQUIRED) add_executable(oopetris - src/main.cpp - src/application.hpp src/application.cpp - src/renderer.cpp - src/renderer.hpp - src/sdl_context.cpp - src/sdl_context.hpp - src/window.cpp - src/window.hpp - src/color.hpp - src/rect.hpp - src/point.hpp - src/tetromino.hpp - src/mino.hpp - src/grid.hpp - src/tetromino_type.hpp - src/tetromino_type.cpp - src/mino.cpp - src/game_manager.cpp - src/game_manager.hpp - src/tetris_application.cpp - src/tetris_application.hpp + src/application.hpp src/bag.cpp src/bag.hpp - src/font.cpp - src/font.hpp - src/text.cpp - src/text.hpp - src/grid.cpp - src/input.hpp + src/color.hpp src/event_dispatcher.cpp src/event_dispatcher.hpp src/event_listener.hpp + src/font.cpp + src/font.hpp + src/game_manager.cpp + src/game_manager.hpp + src/grid.cpp + src/grid.hpp src/input.cpp - src/network/network_manager.cpp + src/input.hpp + src/local_multiplayer.cpp + src/main.cpp + src/mino.cpp + src/mino.hpp src/network/connection_manager.cpp + src/network/network_manager.cpp src/network/network_transportable.cpp + src/play_manager.cpp + src/point.hpp + src/rect.hpp + src/renderer.cpp + src/renderer.hpp + src/sdl_context.cpp + src/sdl_context.hpp + src/tetris_application.cpp + src/tetris_application.hpp + src/tetromino_type.cpp + src/tetromino_type.hpp + src/tetromino.hpp + src/text.cpp + src/text.hpp + src/window.cpp + src/window.hpp ) if(MSVC) diff --git a/src/input.cpp b/src/input.cpp index 7c04c0b6..68a69df2 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -65,5 +65,5 @@ void KeyboardInput::handle_event(const SDL_Event& event) { void OnlineInput::update() { Input::update(); - //TODO + //TODO implement } diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp new file mode 100644 index 00000000..2fc4e329 --- /dev/null +++ b/src/local_multiplayer.cpp @@ -0,0 +1,77 @@ + +#include "local_multiplayer.hpp" +#include "network/network_manager.hpp" +#include "play_manager.hpp" +#include +#include +#include + + +LocalMultiplayer::LocalMultiplayer(std::size_t num_players, bool is_server) + : PlayManager{}, + m_num_players{ num_players }, + m_is_server{ is_server }, + m_network_manager{ NetworkManager{} }, + m_server{ nullptr } {}; + +std::size_t LocalMultiplayer::get_num_players() { + return m_num_players; +}; + +tl::optional LocalMultiplayer::init() { + if (m_num_players > 4 || m_num_players < 2) { + return tl::make_optional( + "The LocalMultiplayer mode only allows between 2 and 4 players, but got: " + + std::to_string(m_num_players) + ); + } + + if (m_is_server) { + auto server = m_network_manager.spawn_server(); + if (!server.has_value()) { + return tl::make_optional("Error in initializing the server: " + server.error()); + } + + m_server = std::make_unique(server.value()); + } + + return {}; +} + + +std::unique_ptr +LocalMultiplayer::get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher event_dispatcher) { + if (index >= m_num_players) { + throw std::range_error{ "LocalMultiplayer mode: error in index of get_input" }; + } + + //TODO: the Keyboard input should broadcast it (if a server to all clients, otherwise to the server, which sends it to all clients!) + + if (index == 0) { + auto keyboard_input = std::make_unique(associated_game_manager); + event_dispatcher.register_listener(keyboard_input.get()); + return keyboard_input; + } else { + + if (m_is_server) { + auto client = m_server->get_client(); + if (!client.has_value()) { + throw std::runtime_error{ "Waited to long for new client" }; + } + auto online_input = std::make_unique(associated_game_manager, client.value()); + return online_input; + } else { + const constexpr std::size_t connection_attempts = 10; + for (std::size_t i = 0; i < connection_attempts; ++i) { + MaybeConnection connection = m_network_manager.try_connect(); + if (connection.has_value()) { + auto online_input = std::make_unique(associated_game_manager, connection.value()); + return online_input; + } + } + + throw std::runtime_error{ "Error in getting a connection for InputMethod::OnlineNetwork: failed after " + + std::to_string(connection_attempts) + " attempts" }; + } + } +} \ No newline at end of file diff --git a/src/local_multiplayer.hpp b/src/local_multiplayer.hpp new file mode 100644 index 00000000..e22b1715 --- /dev/null +++ b/src/local_multiplayer.hpp @@ -0,0 +1,25 @@ + + +#pragma once + +#include "network/network_manager.hpp" +#include "play_manager.hpp" +#include +#include +#include +#include + +struct LocalMultiplayer : public PlayManager { +private: + std::size_t m_num_players; + bool m_is_server; + NetworkManager m_network_manager; + std::unique_ptr m_server; + +public: + explicit LocalMultiplayer(std::size_t num_players, bool is_server); + std::size_t get_num_players() override; + tl::optional init() override; + std::unique_ptr + get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher event_dispatcher) override; +}; diff --git a/src/main.cpp b/src/main.cpp index 4365c9fb..33819605 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,12 +1,34 @@ +#include "local_multiplayer.hpp" #include "tetris_application.hpp" #include +#include +#include +#include +#include -int main(int, char**) { +int main(int argc, char** argv) { static constexpr int target_fps = 60; // TODO show buttons to select from one of the RunTypes - TetrisApplication tetris_app = TetrisApplication{ RunType::OnlineMultiplayer, 2 }; + if (argc != 2) { + std::cerr << "no argument provided, please provide an argument\n"; + std::exit(2); + } + + std::string argument = std ::string{ argv[1] }; + + if (argument != "client" && argument != "server") { + std::cerr << "you have to be either server or client, but you provided: " << argument << "\n"; + std::exit(2); + } + + bool is_server = argument == "server"; + + auto play_manager = std::make_unique(2, is_server); + + TetrisApplication tetris_app = TetrisApplication{ std::move(play_manager) }; + tetris_app.run(target_fps); return 0; diff --git a/src/meson.build b/src/meson.build index 8deabcea..d270c180 100644 --- a/src/meson.build +++ b/src/meson.build @@ -15,9 +15,11 @@ src_files += files( 'grid.hpp', 'input.cpp', 'input.hpp', + 'local_multiplayer.cpp', 'main.cpp', 'mino.cpp', 'mino.hpp', + 'play_manager.cpp', 'point.hpp', 'rect.hpp', 'renderer.cpp', diff --git a/src/network/network_manager.hpp b/src/network/network_manager.hpp index 0314dddf..11583337 100644 --- a/src/network/network_manager.hpp +++ b/src/network/network_manager.hpp @@ -21,7 +21,7 @@ struct NetworkManager { private: std::vector m_connections; static constexpr const char* ServerHost = "localhost"; - static constexpr const int Port = 789; + static constexpr const int Port = 1212; public: explicit NetworkManager(); diff --git a/src/play_manager.cpp b/src/play_manager.cpp new file mode 100644 index 00000000..cce2458b --- /dev/null +++ b/src/play_manager.cpp @@ -0,0 +1,30 @@ + + +#include "play_manager.hpp" +#include "game_manager.hpp" +#include +#include + +PlayManager::PlayManager(){}; + +SinglePlayer::SinglePlayer() : PlayManager{} {}; + +std::size_t SinglePlayer::get_num_players() { + return 1; +}; + +tl::optional SinglePlayer::init() { + return {}; +} + +std::unique_ptr +SinglePlayer::get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher event_dispatcher) { + + if (index != 0) { + throw std::range_error{ "SinglePlayer mode: error in index of get_input" }; + } + + auto keyboard_input = std::make_unique(associated_game_manager); + event_dispatcher.register_listener(keyboard_input.get()); + return keyboard_input; +} diff --git a/src/play_manager.hpp b/src/play_manager.hpp new file mode 100644 index 00000000..2fafb22c --- /dev/null +++ b/src/play_manager.hpp @@ -0,0 +1,30 @@ + + +#pragma once + +#include "game_manager.hpp" +#include +#include +#include +#include + + +/* abstract */ struct PlayManager { +public: + explicit PlayManager(); + virtual ~PlayManager() = default; + virtual std::size_t get_num_players() = 0; + virtual tl::optional init() = 0; + virtual std::unique_ptr + get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher event_dispatcher) = 0; +}; + + +struct SinglePlayer : public PlayManager { +public: + explicit SinglePlayer(); + std::size_t get_num_players() override; + tl::optional init() override; + std::unique_ptr + get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher event_dispatcher) override; +}; diff --git a/src/sdl_context.cpp b/src/sdl_context.cpp index cebb175f..d0891531 100644 --- a/src/sdl_context.cpp +++ b/src/sdl_context.cpp @@ -12,6 +12,7 @@ SdlContext::SdlContext() { printf("TTF_Init: %s\n", SDL_GetError()); exit(2); } + //TODO: if we don't need the network, this should be disabled if (SDLNet_Init() == -1) { printf("SDLNet_Init: %s\n", SDLNet_GetError()); exit(2); diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index 15ef9c86..73eae910 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -3,81 +3,45 @@ #include "application.hpp" #include "game_manager.hpp" #include "network/network_manager.hpp" +#include "play_manager.hpp" #include "tetromino_type.hpp" #include +#include #include #include +#include #include -enum class RunType { OnlineMultiplayer, LocalMultiplayer, SinglePlayer, VersusKI }; - struct TetrisApplication : public Application { private: enum class InputMethod { Keyboard, OnlineNetwork, LocalNetwork, KI }; std::vector> m_game_managers; std::vector> m_inputs; - NetworkManager m_network_manager; - RunType m_run_type; - int m_num_players; + + std::unique_ptr m_manager; public: static constexpr int width = 800; static constexpr int height = 600; - TetrisApplication(RunType run_type, int num_players) + TetrisApplication(std::unique_ptr manager) : Application{ "TetrisApplication", WindowPosition::Centered, width, height }, - m_network_manager{ NetworkManager{} }, - m_run_type{ run_type }, - m_num_players{ num_players } { - - auto input_methods = std::vector{}; - switch (run_type) { - case RunType::OnlineMultiplayer: { + m_manager{ std::move(manager) } { - if (num_players < 2 || num_players > 4) { - throw std::range_error{ "The mode OnlineMultiplayer only allows between 2 and 4 players" }; - } - input_methods.push_back(InputMethod::Keyboard); - for (int i = 0; i < num_players - 1; ++i) { - input_methods.push_back(InputMethod::OnlineNetwork); - } - break; - } - case RunType::LocalMultiplayer: { - if (num_players < 2 || num_players > 4) { - throw std::range_error{ "The mode OnlineMultiplayer only allows between 2 and 4 players" }; - } - input_methods.push_back(InputMethod::Keyboard); - for (int i = 0; i < num_players - 1; ++i) { - input_methods.push_back(InputMethod::LocalNetwork); - } - break; - } - case RunType::SinglePlayer: { - if (num_players != 1) { - throw std::range_error{ "The mode SinglePlayer only allows 1 player" }; - } - input_methods.push_back(InputMethod::Keyboard); - break; - } - case RunType::VersusKI: { - if (num_players < 2 || num_players > 4) { - throw std::range_error{ "The mode VersusKI only allows between 2 and 4 players" }; - } - input_methods.push_back(InputMethod::Keyboard); - for (int i = 0; i < num_players - 1; ++i) { - input_methods.push_back(InputMethod::KI); - } - break; - } + auto is_valid = m_manager->init(); + if (is_valid.has_value()) { + std::cerr << "Error in initializing PlayManager: " << is_valid.value() << "\n"; + std::exit(2); } - for (int i = 0; i < num_players; ++i) { + auto num_players = m_manager->get_num_players(); + + for (std::size_t i = 0; i < num_players; ++i) { m_game_managers.push_back(std::make_unique()); - // TODO, create input has to connect the players for online mode, that process has to be made better and clearer (with waiting screen / lobby) - m_inputs.push_back(create_input(input_methods.at(i), m_game_managers.back().get())); + std::cout << "initializing manager input at" << i << " (online atm)\n"; + m_inputs.push_back(m_manager->get_input(i, m_game_managers.back().get(), m_event_dispatcher)); } for (const auto& game_manager : m_game_managers) { game_manager->spawn_next_tetromino(); @@ -103,33 +67,5 @@ struct TetrisApplication : public Application { } private: - std::unique_ptr create_input(InputMethod input_method, GameManager* associated_game_manager) { - switch (input_method) { - case InputMethod::Keyboard: { - auto keyboard_input = std::make_unique(associated_game_manager); - m_event_dispatcher.register_listener(keyboard_input.get()); - return keyboard_input; - } - case InputMethod::OnlineNetwork: { - MaybeConnection connection = m_network_manager.try_connect(); - if (connection.has_value()) { - auto online_input = std::make_unique(associated_game_manager, connection.value()); - return online_input; - } - std::cout << "Error in getting a connection for InputMethod::OnlineNetwork: " << connection.error() - << "\n"; - return {}; - } - case InputMethod::LocalNetwork: { - assert(false and "unreachable"); - return {}; - } - case InputMethod::KI: { - assert(false and "unreachable"); - return {}; - } - } - assert(false and "unreachable"); - return {}; - } + // }; From 18595a82755da136e9e9530573a13d80f60abe1c Mon Sep 17 00:00:00 2001 From: Totto16 Date: Wed, 5 Apr 2023 23:57:57 +0200 Subject: [PATCH 04/45] implemented serialization of Transportable --- src/game_manager.hpp | 6 ++- src/local_multiplayer.cpp | 17 ++++-- src/local_multiplayer.hpp | 2 +- src/network/connection_manager.cpp | 4 +- src/network/connection_manager.hpp | 3 +- src/network/crc32.h | 39 ++++++++++++++ src/network/network_transportable.cpp | 78 +++++++++++++++++++++++++-- src/network/network_transportable.hpp | 30 +++++++++-- subprojects/.gitignore | 2 +- 9 files changed, 160 insertions(+), 21 deletions(-) create mode 100644 src/network/crc32.h diff --git a/src/game_manager.hpp b/src/game_manager.hpp index 0a0e4c88..99b0cc51 100644 --- a/src/game_manager.hpp +++ b/src/game_manager.hpp @@ -71,10 +71,12 @@ struct GameManager final { [[nodiscard]] double get_gravity_delay() const { const double accelerated_gravity_delay_multiplier = (m_is_accelerated_down_movement ? 1.0 / 20.0 : 1.0); - const int frames = (m_level >= static_cast(frames_per_tile.size()) ? frames_per_tile.back() : frames_per_tile[m_level]); + const int frames = + (m_level >= static_cast(frames_per_tile.size()) ? frames_per_tile.back() : frames_per_tile[m_level] + ); return 1.0 / 60.0 * static_cast(frames) * accelerated_gravity_delay_multiplier; } static constexpr auto frames_per_tile = std::array{ 48, 43, 38, 33, 28, 23, 18, 13, 8, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1 }; -}; +}; \ No newline at end of file diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index 2fc4e329..98c53bb5 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -3,8 +3,8 @@ #include "network/network_manager.hpp" #include "play_manager.hpp" #include -#include #include +#include LocalMultiplayer::LocalMultiplayer(std::size_t num_players, bool is_server) @@ -32,7 +32,7 @@ tl::optional LocalMultiplayer::init() { return tl::make_optional("Error in initializing the server: " + server.error()); } - m_server = std::make_unique(server.value()); + m_server = std::make_shared(server.value()); } return {}; @@ -48,9 +48,15 @@ LocalMultiplayer::get_input(std::size_t index, GameManager* associated_game_mana //TODO: the Keyboard input should broadcast it (if a server to all clients, otherwise to the server, which sends it to all clients!) if (index == 0) { - auto keyboard_input = std::make_unique(associated_game_manager); - event_dispatcher.register_listener(keyboard_input.get()); - return keyboard_input; + if (m_is_server) { + auto keyboard_input = std::make_unique(associated_game_manager); + event_dispatcher.register_listener(keyboard_input.get()); + return keyboard_input; + } else { + auto keyboard_input = std::make_unique(associated_game_manager); + event_dispatcher.register_listener(keyboard_input.get()); + return keyboard_input; + } } else { if (m_is_server) { @@ -68,6 +74,7 @@ LocalMultiplayer::get_input(std::size_t index, GameManager* associated_game_mana auto online_input = std::make_unique(associated_game_manager, connection.value()); return online_input; } + SDL_Delay(200); } throw std::runtime_error{ "Error in getting a connection for InputMethod::OnlineNetwork: failed after " diff --git a/src/local_multiplayer.hpp b/src/local_multiplayer.hpp index e22b1715..65aa40ba 100644 --- a/src/local_multiplayer.hpp +++ b/src/local_multiplayer.hpp @@ -14,7 +14,7 @@ struct LocalMultiplayer : public PlayManager { std::size_t m_num_players; bool m_is_server; NetworkManager m_network_manager; - std::unique_ptr m_server; + std::shared_ptr m_server; public: explicit LocalMultiplayer(std::size_t num_players, bool is_server); diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index f9408f84..6656800e 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -15,10 +15,10 @@ Connection::~Connection() { SDLNet_TCP_Close(m_socket); } -tl::optional Connection::send_data(const Transportable transportable) { +tl::optional Connection::send_data(const Transportable* transportable) { - auto [message, length] = Transportable::serialize(transportable); + auto [message, length] = serialize_transportable(transportable); const auto result = SDLNet_TCP_Send(m_socket, message, length); if (result == -1) { diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index b8966f70..910524f5 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -6,6 +6,7 @@ #include "network_transportable.hpp" #include +#include #include #include @@ -18,7 +19,7 @@ struct Connection { public: explicit Connection(TCPsocket socket); ~Connection(); - tl::optional send_data(const Transportable transportable); + tl::optional send_data(const Transportable* transportable); }; diff --git a/src/network/crc32.h b/src/network/crc32.h new file mode 100644 index 00000000..251e0fc4 --- /dev/null +++ b/src/network/crc32.h @@ -0,0 +1,39 @@ +// from:https://gist.github.com/timepp/1f678e200d9e0f2a043a9ec6b3690635 + +#pragma once + +#include + +struct crc32 { + static void generate_table(uint32_t (&table)[256]) { + uint32_t polynomial = 0xEDB88320; + for (uint32_t i = 0; i < 256; i++) { + uint32_t c = i; + for (size_t j = 0; j < 8; j++) { + if (c & 1) { + c = polynomial ^ (c >> 1); + } else { + c >>= 1; + } + } + table[i] = c; + } + } + + static uint32_t update(uint32_t (&table)[256], uint32_t initial, const void* buf, size_t len) { + uint32_t c = initial ^ 0xFFFFFFFF; + const uint8_t* u = static_cast(buf); + for (size_t i = 0; i < len; ++i) { + c = table[(c ^ u[i]) & 0xFF] ^ (c >> 8); + } + return c ^ 0xFFFFFFFF; + } +}; + + +// usage: the following code generates crc for 2 pieces of data +// uint32_t table[256]; +// crc32::generate_table(table); +// uint32_t crc = crc32::update(table, 0, data_piece1, len1); +// crc = crc32::update(table, crc, data_piece2, len2); +// output(crc); diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index 3bf1a618..ee744265 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -1,11 +1,81 @@ #include "network_transportable.hpp" +#include "crc32.h" +#include +#include +#include +#include +#include -RawBytes Transportable::serialize(const Transportable transportable) { +RawBytes Transportable::serialize(const Transportable* transportable, uint32_t data_size) { - (void) transportable; - //TODO implement - return RawBytes{ nullptr, 0 }; + const uint32_t send_size = Transportable::header_size + data_size + Transportable::checksum_size; + + uint8_t* memory = (uint8_t*) std::malloc(send_size); + if (!memory) { + throw std::runtime_error{ "error in malloc for sending a message" }; + } + + + Transportable::write_header(RawBytes{ memory, Transportable::header_size }, transportable->serialUUID(), data_size); + + Transportable::write_data(RawBytes{ memory + Transportable::header_size, data_size }, transportable); + Transportable::write_checksum(RawBytes{ memory + Transportable::header_size, + data_size + Transportable::checksum_size }); + + return RawBytes{ memory, send_size }; } + + +uint32_t Transportable::checksum(RawBytes bytes) { + + auto [start, length] = bytes; + + uint32_t table[256]; + crc32::generate_table(table); + uint32_t CRC = 0; + for (std::uint32_t i = 0; i < length; ++i) { + CRC = crc32::update(table, CRC, start, 1); + start++; + } + + return CRC; +} + + +void Transportable::write_header(RawBytes bytes, uint32_t serialUUID, uint32_t data_size) { + auto [start, length] = bytes; + + assert(length == Transportable::header_size); + + uint32_t* data_ptr = (uint32_t*) start; + + data_ptr[0] = Transportable::protocol_version; + + data_ptr[1] = serialUUID; + data_ptr[2] = data_size; +} + +void Transportable::write_data(RawBytes bytes, const Transportable* transportable) { + + auto [start, length] = bytes; + + uint8_t* data_ptr = (uint8_t*) transportable; + std::memcpy(start, data_ptr, length); +} + + +void Transportable::write_checksum(RawBytes bytes) { + + auto [start, length] = bytes; + + uint32_t data_size = length - Transportable::checksum_size; + + uint32_t checksum = Transportable::checksum(RawBytes{ start, data_size }); + + uint32_t* data_ptr = (uint32_t*) ((uint8_t*) start + data_size); + + data_ptr[0] = checksum; +} \ No newline at end of file diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp index 19445953..88f7e2e3 100644 --- a/src/network/network_transportable.hpp +++ b/src/network/network_transportable.hpp @@ -6,16 +6,36 @@ #include #include -using RawBytes = std::pair; +using RawBytes = std::pair; -/* abstract*/ class Transportable { +/* abstract*/ struct Transportable { public: + virtual ~Transportable() = default; + // if you change the protocol behaviour in some way, change this number + static constexpr uint32_t protocol_version = 1; + static constexpr uint32_t checksum_size = sizeof(uint32_t); + static constexpr uint32_t header_size = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t); + + //TODO de-serialize + // static std::map deserialize_map = std::map; + // every class has to have such a serialUUID in some way //TODO enforce this in some compile time way, that they are unique! - size_t serialUUID = 0; - static RawBytes serialize(const Transportable transportable); + virtual uint32_t serialUUID() const = 0; + static RawBytes serialize(const Transportable* transportable, std::uint32_t data_size); protected: explicit Transportable() { } static uint32_t checksum(RawBytes bytes); -}; \ No newline at end of file + +private: + static void write_header(RawBytes bytes, uint32_t serialUUID, uint32_t data_size); + static void write_data(RawBytes bytes, const Transportable* transportable); + static void write_checksum(RawBytes bytes); +}; + +template +RawBytes serialize_transportable(const T* transportable) { + + return Transportable::serialize(transportable, sizeof(T)); +} \ No newline at end of file diff --git a/subprojects/.gitignore b/subprojects/.gitignore index e869c2ff..9a1d1e46 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -1,3 +1,3 @@ * !.gitignore -!*.wrap \ No newline at end of file +!*.wrap From eb391fec9f2d2866e94b923e952aa58e418552e1 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 6 Apr 2023 01:11:41 +0200 Subject: [PATCH 05/45] more features added, client and server can connect (but still not communicate correctly) --- src/game_manager.cpp | 9 +++++++ src/game_manager.hpp | 4 +++ src/input.hpp | 4 +-- src/local_multiplayer.cpp | 24 +++++++++++++++--- src/network/connection_manager.cpp | 36 +++++++++++++++++++++------ src/network/connection_manager.hpp | 32 +++++++++++++++++++++--- src/network/meson.build | 2 ++ src/network/network_data.cpp | 18 ++++++++++++++ src/network/network_data.hpp | 28 +++++++++++++++++++++ src/network/network_manager.cpp | 5 ++-- src/network/network_manager.hpp | 4 +-- src/network/network_transportable.hpp | 8 +----- src/network/online_handler.cpp | 33 ++++++++++++++++++++++++ src/network/online_handler.hpp | 17 +++++++++++++ src/tetris_application.hpp | 2 +- 15 files changed, 196 insertions(+), 30 deletions(-) create mode 100644 src/network/network_data.cpp create mode 100644 src/network/network_data.hpp create mode 100644 src/network/online_handler.cpp create mode 100644 src/network/online_handler.hpp diff --git a/src/game_manager.cpp b/src/game_manager.cpp index dadf6752..2e9c4f1a 100644 --- a/src/game_manager.cpp +++ b/src/game_manager.cpp @@ -58,6 +58,10 @@ void GameManager::render(const Application& app) const { } bool GameManager::handle_input_event(Input::Event event) { + if (m_online_handler) { + m_online_handler->handle_event(event); + } + switch (event) { case Input::Event::RotateLeft: return rotate_tetromino_left(); @@ -185,6 +189,11 @@ bool GameManager::drop_tetromino() { return num_movements > 0; } + +void GameManager::set_online_handler(std::unique_ptr online_handler) { + m_online_handler = std::move(online_handler); +} + void GameManager::refresh_texts() { std::stringstream stream; stream << "score: " << m_score; diff --git a/src/game_manager.hpp b/src/game_manager.hpp index 99b0cc51..72543ee7 100644 --- a/src/game_manager.hpp +++ b/src/game_manager.hpp @@ -3,9 +3,11 @@ #include "bag.hpp" #include "grid.hpp" #include "input.hpp" +#include "network/online_handler.hpp" #include "tetromino.hpp" #include "text.hpp" #include +#include #include struct Application; @@ -43,6 +45,7 @@ struct GameManager final { Text m_cleared_lines_text; bool m_down_key_pressed = false; bool m_is_accelerated_down_movement = false; + std::unique_ptr m_online_handler = nullptr; public: GameManager(); @@ -58,6 +61,7 @@ struct GameManager final { bool move_tetromino_left(); bool move_tetromino_right(); bool drop_tetromino(); + void set_online_handler(std::unique_ptr online_handler); private: void refresh_texts(); diff --git a/src/input.hpp b/src/input.hpp index 500f60fb..a89595dc 100644 --- a/src/input.hpp +++ b/src/input.hpp @@ -52,11 +52,11 @@ struct KeyboardInput : public Input, public EventListener { struct OnlineInput : public Input { private: - Connection m_connection; + std::shared_ptr m_connection; //TODO public: - explicit OnlineInput(GameManager* target_game_manager, Connection connection) + explicit OnlineInput(GameManager* target_game_manager, std::shared_ptr connection) : Input{ target_game_manager }, m_connection{ connection } { } diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index 98c53bb5..6dd159a9 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -1,5 +1,6 @@ #include "local_multiplayer.hpp" +#include "network/network_data.hpp" #include "network/network_manager.hpp" #include "play_manager.hpp" #include @@ -32,7 +33,7 @@ tl::optional LocalMultiplayer::init() { return tl::make_optional("Error in initializing the server: " + server.error()); } - m_server = std::make_shared(server.value()); + m_server = server.value(); } return {}; @@ -49,10 +50,16 @@ LocalMultiplayer::get_input(std::size_t index, GameManager* associated_game_mana if (index == 0) { if (m_is_server) { + + associated_game_manager->set_online_handler(std::make_unique(m_server, nullptr, 0)); + auto keyboard_input = std::make_unique(associated_game_manager); event_dispatcher.register_listener(keyboard_input.get()); return keyboard_input; } else { + //TODO nullptr 2 has to be a connection! + associated_game_manager->set_online_handler(std::make_unique(nullptr, nullptr, 0)); + auto keyboard_input = std::make_unique(associated_game_manager); event_dispatcher.register_listener(keyboard_input.get()); return keyboard_input; @@ -67,11 +74,20 @@ LocalMultiplayer::get_input(std::size_t index, GameManager* associated_game_mana auto online_input = std::make_unique(associated_game_manager, client.value()); return online_input; } else { - const constexpr std::size_t connection_attempts = 10; - for (std::size_t i = 0; i < connection_attempts; ++i) { + const constexpr std::uint32_t connection_attempts = 10; + for (std::uint32_t i = 0; i < connection_attempts; ++i) { MaybeConnection connection = m_network_manager.try_connect(); if (connection.has_value()) { - auto online_input = std::make_unique(associated_game_manager, connection.value()); + auto connection_value = connection.value(); + auto online_input = std::make_unique(associated_game_manager, connection_value); + auto send_data = InitializationData{ InitializationDataType::Client, i }; + const auto send_result = ptr_connection_send_data(connection_value, &send_data); + if (send_result.has_value()) { + throw std::runtime_error{ + "Error in sending the InitializationData to the server for InputMethod::OnlineNetwork: " + + send_result.value() + }; + } return online_input; } SDL_Delay(200); diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index 6656800e..02f27339 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -3,8 +3,11 @@ #include "network_manager.hpp" #include "network_transportable.hpp" #include +#include +#include +#include #include - +#include Connection::Connection(TCPsocket socket) : m_socket{ socket } { @@ -15,44 +18,49 @@ Connection::~Connection() { SDLNet_TCP_Close(m_socket); } -tl::optional Connection::send_data(const Transportable* transportable) { - +tl::optional Connection::send_data(const Transportable* transportable, uint32_t data_size) { - auto [message, length] = serialize_transportable(transportable); + auto [message, length] = Transportable::serialize(transportable, data_size); const auto result = SDLNet_TCP_Send(m_socket, message, length); if (result == -1) { + std::free(message); return tl::make_optional("SDLNet_TCP_Send: invalid socket"); } if ((std::size_t) result != length) { + std::free(message); std::string error = "SDLNet_TCP_Send: " + std::string{ SDLNet_GetError() }; return tl::make_optional(error); } + std::free(message); + return {}; }; -Server::Server(TCPsocket socket) : m_socket{ socket } {}; +Server::Server(TCPsocket socket) : m_socket{ socket }, m_connections{ std::vector>{} } {}; Server::~Server() { SDLNet_TCP_Close(m_socket); } -tl::optional Server::try_get_client() { +tl::optional> Server::try_get_client() { TCPsocket client; /* try to accept a connection */ client = SDLNet_TCP_Accept(m_socket); if (client) { /* no connection accepted */ - return Connection{ client }; + auto connection = std::make_shared(client); + m_connections.push_back(connection); + return connection; } return {}; } -tl::optional Server::get_client(std::size_t ms_delay, std::size_t abort_after) { +tl::optional> Server::get_client(std::size_t ms_delay, std::size_t abort_after) { auto start_time = SDL_GetTicks64(); while (true) { /* try to accept a connection */ @@ -69,4 +77,16 @@ tl::optional Server::get_client(std::size_t ms_delay, std::size_t ab SDL_Delay(ms_delay); continue; } +} + +tl::optional Server::send_all(const Transportable* transportable, uint32_t data_size) { + + for (size_t i = 0; i < m_connections.size(); ++i) { + auto result = m_connections.at(i)->send_data(transportable, data_size); + if (result.has_value()) { + return tl::make_optional("Error while sending to client: " + std::to_string(i) + " : " + result.value()); + } + } + + return {}; } \ No newline at end of file diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index 910524f5..6533cca3 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -9,7 +9,7 @@ #include #include #include - +#include struct Connection { @@ -19,7 +19,17 @@ struct Connection { public: explicit Connection(TCPsocket socket); ~Connection(); - tl::optional send_data(const Transportable* transportable); + tl::optional send_data(const Transportable* transportable, uint32_t data_size); +}; + +template +tl::optional connection_send_data(Connection& connection, const T* transportable) { + return connection.send_data(transportable, sizeof(T)); +}; + +template +tl::optional ptr_connection_send_data(C connection, const T* transportable) { + return connection->send_data(transportable, sizeof(T)); }; @@ -27,10 +37,24 @@ struct Server { private: TCPsocket m_socket; + std::vector> m_connections; public: explicit Server(TCPsocket socket); ~Server(); - tl::optional try_get_client(); - tl::optional get_client(std::size_t ms_delay = 100, std::size_t abort_after = 60 * 1000); + tl::optional> try_get_client(); + tl::optional> + get_client(std::size_t ms_delay = 100, std::size_t abort_after = 60 * 1000); + tl::optional send_all(const Transportable* transportable, uint32_t data_size); +}; + + +template +tl::optional server_send_all(Server& server, const T* transportable) { + return server.send_all(transportable, sizeof(T)); +}; + +template +tl::optional ptr_server_send_all(S server, const T* transportable) { + return server->send_all(transportable, sizeof(T)); }; diff --git a/src/network/meson.build b/src/network/meson.build index c2512469..c75ce44b 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -1,7 +1,9 @@ src_files += files( 'connection_manager.cpp', + 'network_data.cpp', 'network_manager.cpp', 'network_transportable.cpp', + 'online_handler.cpp', ) inc_dirs += include_directories('.') diff --git a/src/network/network_data.cpp b/src/network/network_data.cpp new file mode 100644 index 00000000..66a92a86 --- /dev/null +++ b/src/network/network_data.cpp @@ -0,0 +1,18 @@ + + +#include "network_data.hpp" +#include "network_transportable.hpp" +#include + +std::uint32_t InitializationData::serialUUID() const { + return 1; +} + +InitializationData::InitializationData(InitializationDataType type, uint32_t uuid) : m_type{ type }, m_uuid{ uuid } {}; + + +std::uint32_t EventData::serialUUID() const { + return 2; +} + +EventData::EventData(Input::Event event) : m_event{ event } {}; diff --git a/src/network/network_data.hpp b/src/network/network_data.hpp new file mode 100644 index 00000000..c5a332bc --- /dev/null +++ b/src/network/network_data.hpp @@ -0,0 +1,28 @@ + + +#pragma once + +#include "../input.hpp" +#include "network_transportable.hpp" +#include + +enum class InitializationDataType : uint8_t { Client, Server }; + +struct InitializationData : public Transportable { +private: + InitializationDataType m_type; + uint32_t m_uuid; + +public: + uint32_t serialUUID() const override; + explicit InitializationData(InitializationDataType type, uint32_t uuid); +}; + +struct EventData : public Transportable { +private: + Input::Event m_event; + +public: + uint32_t serialUUID() const override; + explicit EventData(Input::Event event); +}; \ No newline at end of file diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index 1380b320..09d46961 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -2,6 +2,7 @@ #include "network_manager.hpp" #include "network_transportable.hpp" #include +#include #include #include #include @@ -26,7 +27,7 @@ MaybeConnection NetworkManager::try_connect(const char* host, std::size_t port) return tl::make_unexpected(error); } - return Connection{ tcpsock }; + return std::make_shared(tcpsock); } MaybeServer NetworkManager::spawn_server(std::size_t port) { @@ -43,5 +44,5 @@ MaybeServer NetworkManager::spawn_server(std::size_t port) { return tl::make_unexpected(error); } - return Server{ server }; + return std::make_shared(server); } diff --git a/src/network/network_manager.hpp b/src/network/network_manager.hpp index 11583337..b7af02f9 100644 --- a/src/network/network_manager.hpp +++ b/src/network/network_manager.hpp @@ -14,8 +14,8 @@ #include -using MaybeConnection = tl::expected; -using MaybeServer = tl::expected; +using MaybeConnection = tl::expected, std::string>; +using MaybeServer = tl::expected, std::string>; struct NetworkManager { private: diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp index 88f7e2e3..1b58a6cb 100644 --- a/src/network/network_transportable.hpp +++ b/src/network/network_transportable.hpp @@ -32,10 +32,4 @@ using RawBytes = std::pair; static void write_header(RawBytes bytes, uint32_t serialUUID, uint32_t data_size); static void write_data(RawBytes bytes, const Transportable* transportable); static void write_checksum(RawBytes bytes); -}; - -template -RawBytes serialize_transportable(const T* transportable) { - - return Transportable::serialize(transportable, sizeof(T)); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/network/online_handler.cpp b/src/network/online_handler.cpp new file mode 100644 index 00000000..bf9770d1 --- /dev/null +++ b/src/network/online_handler.cpp @@ -0,0 +1,33 @@ + + +#include "online_handler.hpp" +#include "../input.hpp" +#include "connection_manager.hpp" +#include "network_data.hpp" +#include +#include + +OnlineHandler::OnlineHandler(std::shared_ptr server, std::shared_ptr connection, uint32_t uuid) + : m_server{ server }, + m_connection{ connection }, + m_uuid{ uuid } {}; + + +void OnlineHandler::handle_event(Input::Event event) { + if (m_server) { + auto event_data = EventData{ event }; + //TODO handle error + ptr_server_send_all(m_server, &event_data); + + /* m_server.send_all_if(EventData{ event }, []() { + + + }); */ + } else if (m_connection) { + auto event_data = EventData{ event }; + //TODO handle error + ptr_connection_send_data(m_connection, &event_data); + } else { + throw std::runtime_error{ "OnlineHandler needs either a connection (client mode) or server!" }; + } +} \ No newline at end of file diff --git a/src/network/online_handler.hpp b/src/network/online_handler.hpp new file mode 100644 index 00000000..ddf26414 --- /dev/null +++ b/src/network/online_handler.hpp @@ -0,0 +1,17 @@ + +#pragma once + +#include "../input.hpp" +#include "connection_manager.hpp" +#include + +struct OnlineHandler { +private: + std::shared_ptr m_server; + std::shared_ptr m_connection; + std::uint32_t m_uuid; + +public: + OnlineHandler(std::shared_ptr server, std::shared_ptr connection, uint32_t uuid); + void handle_event(Input::Event event); +}; \ No newline at end of file diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index 73eae910..bb1d8cbf 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -40,7 +40,7 @@ struct TetrisApplication : public Application { for (std::size_t i = 0; i < num_players; ++i) { m_game_managers.push_back(std::make_unique()); - std::cout << "initializing manager input at" << i << " (online atm)\n"; + std::cout << "initializing manager input at " << i << " (online atm)\n"; m_inputs.push_back(m_manager->get_input(i, m_game_managers.back().get(), m_event_dispatcher)); } for (const auto& game_manager : m_game_managers) { From 26f93e5391e7bf0552ffdbcec0a7f598725cd560 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 6 Apr 2023 01:13:31 +0200 Subject: [PATCH 06/45] deleted unused code --- src/network/network_address_old.cpp | 40 ----------------------------- src/network/network_address_old.hpp | 24 ----------------- 2 files changed, 64 deletions(-) delete mode 100644 src/network/network_address_old.cpp delete mode 100644 src/network/network_address_old.hpp diff --git a/src/network/network_address_old.cpp b/src/network/network_address_old.cpp deleted file mode 100644 index 7b71075d..00000000 --- a/src/network/network_address_old.cpp +++ /dev/null @@ -1,40 +0,0 @@ - -#include "network_address.hpp" -#include "network_transportable.hpp" -#include -#include -#include -#include - -NetworkAddress::NetworkAddress(TCPsocket socket) : m_socket{ std::move(socket) } { } - -NetworkAddress::~NetworkAddress() { - SDLNet_TCP_Close(m_socket); -} - -tl::optional NetworkAddress::send_data(RawBytes bytes) { - auto [message, length] = bytes; - const auto result = SDLNet_TCP_Send(m_socket, message, length); - if (result == -1) { - return tl::make_optional("SDLNet_TCP_Send: invalid socket"); - } - - if (result != length) { - std::string error = "SDLNet_TCP_Send: " + std::string{ SDLNet_GetError() }; - return tl::make_optional(error); - } - - return {}; -} - - -tl::optional NetworkAddress::try_accept() { - - TCPsocket client; - /* try to accept a connection */ - client = SDLNet_TCP_Accept(m_socket); - if (client) { /* no connection accepted */ - return Connection{ client }; - } - return {}; -} diff --git a/src/network/network_address_old.hpp b/src/network/network_address_old.hpp deleted file mode 100644 index 24c53e7d..00000000 --- a/src/network/network_address_old.hpp +++ /dev/null @@ -1,24 +0,0 @@ - - - -#pragma once - -#include "connection_manager.hpp" -#include "network_transportable.hpp" -#include -#include -#include - -struct NetworkAddress { -private: - TCPsocket m_socket; - -public: - NetworkAddress(TCPsocket socket); - ~NetworkAddress(); - // client (Connection) socket only method! - tl::optional send_data(RawBytes bytes); - - // server (Server) socket only method - tl::optional try_accept(); -}; From b2271e936a3710c8d5e8d73b96eb47e9e767aa99 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 6 Apr 2023 02:16:12 +0200 Subject: [PATCH 07/45] fixed issues in offsets and in event_dispatching, now it works visually (input receiveing isn't implemented yet) --- src/game_manager.cpp | 9 +++- src/game_manager.hpp | 13 ++++-- src/grid.cpp | 16 ++++--- src/grid.hpp | 1 + src/local_multiplayer.cpp | 79 ++++++++++++++++++++++------------ src/local_multiplayer.hpp | 6 ++- src/main.cpp | 9 +++- src/network/online_handler.hpp | 2 +- src/play_manager.cpp | 4 +- src/play_manager.hpp | 4 +- src/rect.hpp | 9 ++++ src/tetris_application.hpp | 14 ++++-- 12 files changed, 115 insertions(+), 51 deletions(-) diff --git a/src/game_manager.cpp b/src/game_manager.cpp index 2e9c4f1a..3ffa05d2 100644 --- a/src/game_manager.cpp +++ b/src/game_manager.cpp @@ -4,9 +4,14 @@ #include #include -GameManager::GameManager() - : m_grid{ Point::zero(), tile_size }, +GameManager::GameManager(std::size_t uuid) + : m_uuid{ uuid }, + m_grid{ Point{ + (int) ((uuid * GameManager::size_per_field) + (uuid * GameManager::space_between)), 0 + }, tile_size }, m_next_gravity_step_time{ Application::elapsed_time() + get_gravity_delay() } { + + m_fonts.push_back(std::make_shared("assets/fonts/PressStart2P.ttf", 18)); m_score_text = Text{ Point{ m_grid.to_screen_coords(Grid::preview_tetromino_position + Point{ 0, Grid::preview_extends.y }) }, diff --git a/src/game_manager.hpp b/src/game_manager.hpp index 72543ee7..44208565 100644 --- a/src/game_manager.hpp +++ b/src/game_manager.hpp @@ -7,6 +7,7 @@ #include "tetromino.hpp" #include "text.hpp" #include +#include #include #include @@ -23,12 +24,16 @@ enum class MovementType { }; struct GameManager final { -private: - static constexpr int tile_size = 30; +public: + static constexpr std::size_t tile_size = 30; + static constexpr std::size_t legend_size = Grid::preview_extends.x; + static constexpr std::size_t size_per_field = tile_size * (Grid::width + legend_size); + static constexpr std::size_t space_between = 125; +private: // while holding down, this level is assumed for gravity calculation static constexpr int accelerated_drop_movement_level = 10; - + std::size_t m_uuid; Grid m_grid; tl::optional m_active_tetromino; tl::optional m_preview_tetromino; @@ -48,7 +53,7 @@ struct GameManager final { std::unique_ptr m_online_handler = nullptr; public: - GameManager(); + GameManager(std::size_t uuid); void update(); void render(const Application& app) const; diff --git a/src/grid.cpp b/src/grid.cpp index 4bfeb19c..34800844 100644 --- a/src/grid.cpp +++ b/src/grid.cpp @@ -1,6 +1,7 @@ #include "grid.hpp" +#include "rect.hpp" -Grid::Grid(Point offset, int tile_size) : m_offset{ offset - Point{ 0, invisible_rows * tile_size }}, m_tile_size{ tile_size } { +Grid::Grid(Point offset, int tile_size) : m_start_point{ offset }, m_offset{ offset - Point{ 0, invisible_rows * tile_size }},m_tile_size{ tile_size } { m_minos.reserve(num_tiles); } @@ -9,7 +10,7 @@ Point Grid::tile_size() const { } Point Grid::to_screen_coords(Point grid_coords) const { - return m_offset + grid_coords * m_tile_size; + return m_offset + (grid_coords * m_tile_size); } void Grid::render(const Application& app) const { @@ -62,12 +63,12 @@ void Grid::draw_preview_background(const Application& app) const { const Point preview_top_left = to_screen_coords(preview_background_position); const Point preview_bottom_right = preview_top_left + Point{ tile_size().x * preview_extends.x - 1, tile_size().y * preview_extends.y - 1 }; - app.renderer().draw_rect_filled(Rect{ preview_top_left, preview_bottom_right }, background_color); + app.renderer().draw_rect_filled(m_start_point + Rect{ preview_top_left, preview_bottom_right }, background_color); const Point outline_top_left = preview_top_left - Point{ 1, 1 }; const Point outline_bottom_right = preview_bottom_right + Point{ 1, 1 }; const Rect outline_rect = Rect{ outline_top_left, outline_bottom_right }; - app.renderer().draw_rect_outline(outline_rect, border_color); + app.renderer().draw_rect_outline(m_start_point + outline_rect, border_color); } void Grid::draw_playing_field_background(const Application& app) const { @@ -75,8 +76,11 @@ void Grid::draw_playing_field_background(const Application& app) const { width * m_tile_size - 1, (height - invisible_rows) * m_tile_size, }; - app.renderer().draw_rect_filled(Rect{ Point::zero(), bottom_right }, background_color); + app.renderer().draw_rect_filled(m_start_point + Rect{ Point::zero(), bottom_right }, background_color); app.renderer().draw_line( - Point{ bottom_right.x + 1, 0 }, Point{ bottom_right.x + 1, app.window().size().y - 1 }, border_color + m_start_point + Point{ bottom_right.x + 1, 0 }, + m_start_point + Point{ bottom_right.x + 1, app.window().size().y - 1 }, border_color ); + printf("m_offset: y %d , y: %d, size %d\n", m_start_point.y, + (m_start_point + Point{ bottom_right.x + 1, app.window().size().y - 1 }).y, app.window().size().y); } diff --git a/src/grid.hpp b/src/grid.hpp index ea48450f..1a7997f2 100644 --- a/src/grid.hpp +++ b/src/grid.hpp @@ -19,6 +19,7 @@ struct Grid final { static constexpr Color border_color{ 42, 42, 42 }; private: + Point m_start_point; Point m_offset; int m_tile_size; std::vector m_minos; diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index 6dd159a9..44fdd8fe 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -6,7 +6,9 @@ #include #include #include - +#include +#include +#include LocalMultiplayer::LocalMultiplayer(std::size_t num_players, bool is_server) : PlayManager{}, @@ -40,28 +42,38 @@ tl::optional LocalMultiplayer::init() { } -std::unique_ptr -LocalMultiplayer::get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher event_dispatcher) { +std::unique_ptr LocalMultiplayer::get_input( + std::size_t index, + GameManager* associated_game_manager, + EventDispatcher* event_dispatcher +) { if (index >= m_num_players) { throw std::range_error{ "LocalMultiplayer mode: error in index of get_input" }; } - //TODO: the Keyboard input should broadcast it (if a server to all clients, otherwise to the server, which sends it to all clients!) - if (index == 0) { if (m_is_server) { associated_game_manager->set_online_handler(std::make_unique(m_server, nullptr, 0)); auto keyboard_input = std::make_unique(associated_game_manager); - event_dispatcher.register_listener(keyboard_input.get()); + event_dispatcher->register_listener(keyboard_input.get()); return keyboard_input; } else { - //TODO nullptr 2 has to be a connection! - associated_game_manager->set_online_handler(std::make_unique(nullptr, nullptr, 0)); + + + auto connection_result = get_connection_to_server(); + + if (!connection_result.has_value()) { + throw std::runtime_error{ connection_result.error() }; + } + + auto connection = connection_result.value(); + + associated_game_manager->set_online_handler(std::make_unique(nullptr, connection, 0)); auto keyboard_input = std::make_unique(associated_game_manager); - event_dispatcher.register_listener(keyboard_input.get()); + event_dispatcher->register_listener(keyboard_input.get()); return keyboard_input; } } else { @@ -74,27 +86,38 @@ LocalMultiplayer::get_input(std::size_t index, GameManager* associated_game_mana auto online_input = std::make_unique(associated_game_manager, client.value()); return online_input; } else { - const constexpr std::uint32_t connection_attempts = 10; - for (std::uint32_t i = 0; i < connection_attempts; ++i) { - MaybeConnection connection = m_network_manager.try_connect(); - if (connection.has_value()) { - auto connection_value = connection.value(); - auto online_input = std::make_unique(associated_game_manager, connection_value); - auto send_data = InitializationData{ InitializationDataType::Client, i }; - const auto send_result = ptr_connection_send_data(connection_value, &send_data); - if (send_result.has_value()) { - throw std::runtime_error{ - "Error in sending the InitializationData to the server for InputMethod::OnlineNetwork: " - + send_result.value() - }; - } - return online_input; - } - SDL_Delay(200); + + auto connection_result = get_connection_to_server(); + + if (!connection_result.has_value()) { + throw std::runtime_error{ connection_result.error() }; } - throw std::runtime_error{ "Error in getting a connection for InputMethod::OnlineNetwork: failed after " - + std::to_string(connection_attempts) + " attempts" }; + auto connection = connection_result.value(); + auto online_input = std::make_unique(associated_game_manager, connection); + auto send_data = InitializationData{ InitializationDataType::Client, (uint32_t) index }; + const auto send_result = ptr_connection_send_data(connection, &send_data); + if (send_result.has_value()) { } + return online_input; } } +} + + +tl::expected, std::string> +LocalMultiplayer::get_connection_to_server(std::uint32_t delay_between_attempts, std::uint32_t connection_attempts) { + + + for (std::uint32_t i = 0; i < connection_attempts; ++i) { + MaybeConnection connection = m_network_manager.try_connect(); + if (connection.has_value()) { + return connection.value(); + } + SDL_Delay(delay_between_attempts); + } + + return tl::make_unexpected( + "Error in getting a connection for LocalMultiplayer: failed after " + std::to_string(connection_attempts) + + " attempts" + ); } \ No newline at end of file diff --git a/src/local_multiplayer.hpp b/src/local_multiplayer.hpp index 65aa40ba..f8e85415 100644 --- a/src/local_multiplayer.hpp +++ b/src/local_multiplayer.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include struct LocalMultiplayer : public PlayManager { @@ -16,10 +17,13 @@ struct LocalMultiplayer : public PlayManager { NetworkManager m_network_manager; std::shared_ptr m_server; + tl::expected, std::string> + get_connection_to_server(std::uint32_t delay_between_attempts = 200, std::uint32_t connection_attempts = 10); + public: explicit LocalMultiplayer(std::size_t num_players, bool is_server); std::size_t get_num_players() override; tl::optional init() override; std::unique_ptr - get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher event_dispatcher) override; + get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher) override; }; diff --git a/src/main.cpp b/src/main.cpp index 33819605..83ad143d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,8 +12,13 @@ int main(int argc, char** argv) { // TODO show buttons to select from one of the RunTypes if (argc != 2) { - std::cerr << "no argument provided, please provide an argument\n"; - std::exit(2); + std::cerr << "no argument provided, playing single-player\n"; + auto play_manager = std::make_unique(); + TetrisApplication tetris_app = TetrisApplication{ std::move(play_manager) }; + + tetris_app.run(target_fps); + + return 0; } std::string argument = std ::string{ argv[1] }; diff --git a/src/network/online_handler.hpp b/src/network/online_handler.hpp index ddf26414..1afec893 100644 --- a/src/network/online_handler.hpp +++ b/src/network/online_handler.hpp @@ -12,6 +12,6 @@ struct OnlineHandler { std::uint32_t m_uuid; public: - OnlineHandler(std::shared_ptr server, std::shared_ptr connection, uint32_t uuid); + OnlineHandler(std::shared_ptr server, std::shared_ptr connection, uint32_t uuid); void handle_event(Input::Event event); }; \ No newline at end of file diff --git a/src/play_manager.cpp b/src/play_manager.cpp index cce2458b..d1b2ae3a 100644 --- a/src/play_manager.cpp +++ b/src/play_manager.cpp @@ -18,13 +18,13 @@ tl::optional SinglePlayer::init() { } std::unique_ptr -SinglePlayer::get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher event_dispatcher) { +SinglePlayer::get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher) { if (index != 0) { throw std::range_error{ "SinglePlayer mode: error in index of get_input" }; } auto keyboard_input = std::make_unique(associated_game_manager); - event_dispatcher.register_listener(keyboard_input.get()); + event_dispatcher->register_listener(keyboard_input.get()); return keyboard_input; } diff --git a/src/play_manager.hpp b/src/play_manager.hpp index 2fafb22c..741d0075 100644 --- a/src/play_manager.hpp +++ b/src/play_manager.hpp @@ -16,7 +16,7 @@ virtual std::size_t get_num_players() = 0; virtual tl::optional init() = 0; virtual std::unique_ptr - get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher event_dispatcher) = 0; + get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher) = 0; }; @@ -26,5 +26,5 @@ struct SinglePlayer : public PlayManager { std::size_t get_num_players() override; tl::optional init() override; std::unique_ptr - get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher event_dispatcher) override; + get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher) override; }; diff --git a/src/rect.hpp b/src/rect.hpp index dc890fd9..54036d49 100644 --- a/src/rect.hpp +++ b/src/rect.hpp @@ -12,3 +12,12 @@ struct Rect final { : top_left{ x, y }, bottom_right{ x + width - 1, y + height - 1 } { } }; + + +inline constexpr Rect operator+(Point lhs, Rect rhs) { + return Rect{ lhs + rhs.top_left, lhs + rhs.bottom_right }; +} + +inline constexpr Rect operator+(Rect lhs, Point rhs) { + return Rect{ lhs.top_left + rhs, lhs.bottom_right + rhs }; +} \ No newline at end of file diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index bb1d8cbf..4a924591 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -23,7 +23,7 @@ struct TetrisApplication : public Application { std::unique_ptr m_manager; public: - static constexpr int width = 800; + static constexpr int width = 1200; static constexpr int height = 600; TetrisApplication(std::unique_ptr manager) @@ -39,15 +39,23 @@ struct TetrisApplication : public Application { auto num_players = m_manager->get_num_players(); for (std::size_t i = 0; i < num_players; ++i) { - m_game_managers.push_back(std::make_unique()); + m_game_managers.push_back(std::make_unique(i)); std::cout << "initializing manager input at " << i << " (online atm)\n"; - m_inputs.push_back(m_manager->get_input(i, m_game_managers.back().get(), m_event_dispatcher)); + m_inputs.push_back(m_manager->get_input(i, m_game_managers.back().get(), &m_event_dispatcher)); } for (const auto& game_manager : m_game_managers) { game_manager->spawn_next_tetromino(); } + + //TODO if this is to big to handle num_players, we have to resize in some way + [[maybe_unused]] const size_t game_field_size = + (GameManager::size_per_field * num_players) + ((num_players - 1) * GameManager::space_between); + + + //TODO: resize(), but then game_managers have to updated as well, to repaint or not? } + protected: void update(double) override { for (const auto& input : m_inputs) { From 60783f954278803dd3c9c9c391fe2fbc9c52ef1b Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 6 Apr 2023 04:39:58 +0200 Subject: [PATCH 08/45] added ability to deserialize (WIP) --- src/grid.cpp | 2 - src/network/connection_manager.cpp | 91 ++++++++++++++++++++-- src/network/connection_manager.hpp | 10 +++ src/network/network_transportable.cpp | 104 ++++++++++++++++++++++++++ src/network/network_transportable.hpp | 58 +++++++++++++- 5 files changed, 255 insertions(+), 10 deletions(-) diff --git a/src/grid.cpp b/src/grid.cpp index 34800844..c7ba3469 100644 --- a/src/grid.cpp +++ b/src/grid.cpp @@ -81,6 +81,4 @@ void Grid::draw_playing_field_background(const Application& app) const { m_start_point + Point{ bottom_right.x + 1, 0 }, m_start_point + Point{ bottom_right.x + 1, app.window().size().y - 1 }, border_color ); - printf("m_offset: y %d , y: %d, size %d\n", m_start_point.y, - (m_start_point + Point{ bottom_right.x + 1, app.window().size().y - 1 }).y, app.window().size().y); } diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index 02f27339..147f76be 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -9,10 +9,7 @@ #include #include -Connection::Connection(TCPsocket socket) - : m_socket{ socket } { - - }; +Connection::Connection(TCPsocket socket) : m_socket{ socket } {}; Connection::~Connection() { SDLNet_TCP_Close(m_socket); @@ -89,4 +86,88 @@ tl::optional Server::send_all(const Transportable* transportable, u } return {}; -} \ No newline at end of file +} + +tl::expected Server::is_data_available(Uint32 timeout_ms) { + + SDLNet_SocketSet set = SDLNet_AllocSocketSet(1); + if (!set) { + return tl::make_unexpected("no more memory for creating a SDLNet_SocketSet"); + } + + + auto num_sockets = SDLNet_TCP_AddSocket(set, m_socket); + if (num_sockets != 1) { + return tl::make_unexpected("SDLNet_AddSocket failed, this is an implementation error"); + } + + auto result = SDLNet_CheckSockets(set, timeout_ms); + if (result == -1) { + return tl::make_unexpected("SDLNet_CheckSockets error (select() system call error)"); + } + + + SDLNet_FreeSocketSet(set); + + return result == 1; +} + + +tl::expected Server::get_all_data_blocking() { + void* memory = std::malloc(Server::chunk_size); + if (!memory) { + return tl::make_unexpected("error in malloc for receiving a socket message"); + } + std::uint32_t data_size = 0; + while (true) { + int len = SDLNet_TCP_Recv(m_socket, memory, Server::chunk_size); + if (len <= 0) { + free(memory); + return tl::make_unexpected("SDLNet_TCP_Recv: " + std::string{ SDLNet_GetError() }); + } + + if (len != Server::chunk_size) { + + return RawBytes{ (uint8_t*) memory, data_size + len }; + } + + data_size += Server::chunk_size; + void* new_memory = std::realloc(memory, data_size + Server::chunk_size); + if (!new_memory) { + free(memory); + return tl::make_unexpected("error in realloc for receiving a socket message"); + } + memory = new_memory; + } + + return tl::make_unexpected("error in SDLNet_TCP_Recv: somehow exited the while loop"); +} + +MaybeData Server::get_data() { + + auto data_available = is_data_available(); + if (!data_available.has_value()) { + return tl::make_unexpected("in is_data_available: " + data_available.error()); + } + + if (!data_available.value()) { + return {}; + } + + auto data = get_all_data_blocking(); + if (!data.has_value()) { + return tl::make_unexpected("in get_all_data_blocking: " + data_available.error()); + } + + + RawBytes raw_bytes = data.value(); + + auto result = RawTransportData::from_raw_bytes(raw_bytes); + if (!result.has_value()) { + free(raw_bytes.first); + return tl::make_unexpected("in RawTransportData::from_raw_bytes: " + result.error()); + } + + free(raw_bytes.first); + return tl::make_optional(std::move(result.value())); +} diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index 6533cca3..bee81e15 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,8 @@ tl::optional ptr_connection_send_data(C connection, const T* transp return connection->send_data(transportable, sizeof(T)); }; +using MaybeData = tl::expected>>, std::string>; + struct Server { @@ -39,6 +42,11 @@ struct Server { TCPsocket m_socket; std::vector> m_connections; + tl::expected is_data_available(Uint32 timeout_ms = 3); + tl::expected get_all_data_blocking(); + + static constexpr std::size_t chunk_size = 1024; + public: explicit Server(TCPsocket socket); ~Server(); @@ -46,6 +54,8 @@ struct Server { tl::optional> get_client(std::size_t ms_delay = 100, std::size_t abort_after = 60 * 1000); tl::optional send_all(const Transportable* transportable, uint32_t data_size); + + MaybeData get_data(); }; diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index ee744265..f4716372 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -6,9 +6,13 @@ #include #include #include +#include #include +#include +#include +//TODO fix inconsistency and don't raise exceptions, rather return tl:expected RawBytes Transportable::serialize(const Transportable* transportable, uint32_t data_size) { const uint32_t send_size = Transportable::header_size + data_size + Transportable::checksum_size; @@ -48,6 +52,7 @@ uint32_t Transportable::checksum(RawBytes bytes) { void Transportable::write_header(RawBytes bytes, uint32_t serialUUID, uint32_t data_size) { auto [start, length] = bytes; + //TODO remove assert assert(length == Transportable::header_size); uint32_t* data_ptr = (uint32_t*) start; @@ -78,4 +83,103 @@ void Transportable::write_checksum(RawBytes bytes) { uint32_t* data_ptr = (uint32_t*) ((uint8_t*) start + data_size); data_ptr[0] = checksum; +} + +RawTransportData::RawTransportData(uint32_t serialUUID, RawBytes data) : m_serialUUID{ serialUUID }, m_data{ data } {}; + + +MaybeRawTransportData RawTransportData::from_raw_bytes(RawBytes raw_bytes) { + + auto result = std::vector>{}; + + auto [start, length] = raw_bytes; + long remaining_length = length; + auto advance = [&](uint32_t size) { + remaining_length -= size; + start += size; + }; + while (remaining_length > 0) { + + auto header = RawTransportData::read_header(RawBytes{ start, remaining_length }); + if (!header.has_value()) { + return tl::make_unexpected("in RawTransportData::from_raw_bytes: " + header.error()); + } + + advance(Transportable::header_size); + + auto [protocol_version, serialUUID, data_size] = header.value(); + + if (remaining_length < data_size) { + return tl::make_unexpected( + "in RawTransportData::from_raw_bytes: couldn't read data, since the raw data is to small" + ); + } + + //TODO check if implemented correctly + // this malloc get'S freed in the unique ptr destructor later + void* memory = std::malloc(data_size); + if (!memory) { + return tl::make_unexpected("in RawTransportData::from_raw_bytes: error in malloc for RawTransportData"); + } + auto data = RawBytes{ (uint8_t*) std::memcpy(memory, start, data_size), data_size }; + + auto checksum = RawTransportData::read_checksum(RawBytes{ start, remaining_length }, data_size); + + advance(data_size + Transportable::checksum_size); + result.push_back(std::make_unique(serialUUID, data)); + } + + return result; +} + +tl::expected, std::string> RawTransportData::read_header( + RawBytes bytes +) { + auto [start, length] = bytes; + if (length < Transportable::header_size) { + return tl::make_unexpected("couldn't read header, since the raw data is to small"); + } + + uint32_t* data_ptr = (uint32_t*) start; + + uint32_t protocol_version = data_ptr[0]; + if (RawTransportData::protocol_version != protocol_version) { + return tl::make_unexpected( + "couldn't parse header, since the protocol version mismatches: parser can parse: " + + std::to_string(RawTransportData::protocol_version) + + "but received: " + std::to_string(protocol_version) + ); + } + + uint32_t serialUUID = data_ptr[1]; + uint32_t data_size = data_ptr[2]; + return std::tuple{ protocol_version, serialUUID, data_size }; +} + + +tl::expected RawTransportData::read_checksum(RawBytes bytes, uint32_t data_size) { + auto [start, length] = bytes; + if (length < data_size + Transportable::checksum_size) { + return tl::make_unexpected("couldn't read checksum, since the raw data is to small"); + } + + uint32_t calc_checksum = Transportable::checksum(RawBytes{ start, data_size }); + + uint32_t* data_ptr = (uint32_t*) ((uint8_t*) start + data_size); + + uint32_t read_checksum = data_ptr[0]; + + if (read_checksum != calc_checksum) { + return tl::make_unexpected( + "couldn't read data, since the checksum mismatches: read checksum: " + to_hex_str(read_checksum) + + "but calculated checksum: " + to_hex_str(calc_checksum) + ); + } + + return read_checksum; +} + +//TODO this shouldn't need an Transportable*, but just the type +bool RawTransportData::is_of_type(Transportable* transportable) { + return m_serialUUID == transportable->serialUUID(); } \ No newline at end of file diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp index 1b58a6cb..853402e1 100644 --- a/src/network/network_transportable.hpp +++ b/src/network/network_transportable.hpp @@ -4,14 +4,20 @@ #include #include +#include +#include +#include +#include +#include #include +#include using RawBytes = std::pair; /* abstract*/ struct Transportable { public: virtual ~Transportable() = default; - // if you change the protocol behaviour in some way, change this number + // if you change the protocol behaviour in some way, change this number and the number in RawTransportData static constexpr uint32_t protocol_version = 1; static constexpr uint32_t checksum_size = sizeof(uint32_t); static constexpr uint32_t header_size = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t); @@ -24,12 +30,58 @@ using RawBytes = std::pair; virtual uint32_t serialUUID() const = 0; static RawBytes serialize(const Transportable* transportable, std::uint32_t data_size); + static uint32_t checksum(RawBytes bytes); + protected: explicit Transportable() { } - static uint32_t checksum(RawBytes bytes); private: static void write_header(RawBytes bytes, uint32_t serialUUID, uint32_t data_size); static void write_data(RawBytes bytes, const Transportable* transportable); static void write_checksum(RawBytes bytes); -}; \ No newline at end of file +}; + +//TODO fix inconsistencies in std::size_t and std::uint32_t usage + + +struct RawTransportData { +private: + uint32_t m_serialUUID; + RawBytes m_data; + +public: + RawTransportData(uint32_t serialUUID, RawBytes data); + + // if you change the protocol behaviour in some way, change this number and the number in Transportable (this parses only packets with the same protocol_version) + static constexpr uint32_t protocol_version = 1; + + static tl::expected>, std::string> from_raw_bytes(RawBytes raw_bytes); + static tl::expected, std::string> read_header(RawBytes bytes + ); + static tl::expected read_checksum(RawBytes bytes, uint32_t data_size); + + bool is_of_type(Transportable* transportable); +}; + +using MaybeRawTransportData = tl::expected>, std::string>; + + +template +std::string to_hex_str(T number) { + std::ostringstream ss{}; + ss << std::hex << number; + return ss.str(); +} + +template +using RawUniqueTransportData = std::unique_ptr>; + +// TODO: RawTransportData data is invalid after this, that has to be enforced in some way (at compile time) +template +RawUniqueTransportData raw_transport_data_to(RawTransportData& data) { + //TODO pre-check this, after making serialUUID() static! + //if(RawTransportData::is_of_type(nullptr)) + // TODO: check if this is correct (with free. etc)! + RawUniqueTransportData raw_unique_ptr{ std::move(data.m_data) /*.first*/, [](void* ptr) { free(ptr); } }; + return raw_unique_ptr; +} From 851c79f1f472e7dffd07e8f701ca6bf7b3c295e0 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 6 Apr 2023 04:42:06 +0200 Subject: [PATCH 09/45] added source files to cmake --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ea872666..72e27c66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,12 +25,16 @@ add_executable(oopetris src/input.cpp src/input.hpp src/local_multiplayer.cpp + src/local_multiplayer.cpp src/main.cpp src/mino.cpp src/mino.hpp src/network/connection_manager.cpp + src/network/network_data.cpp src/network/network_manager.cpp src/network/network_transportable.cpp + src/network/online_handler.cpp + src/play_manager.cpp src/play_manager.cpp src/point.hpp src/rect.hpp From 6e2699f83ef42e62ff96a111133ed549eae661b2 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 6 Apr 2023 04:43:18 +0200 Subject: [PATCH 10/45] added correct header so that msvc doesn't complain --- src/play_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/play_manager.cpp b/src/play_manager.cpp index d1b2ae3a..b4c3ea3b 100644 --- a/src/play_manager.cpp +++ b/src/play_manager.cpp @@ -3,7 +3,7 @@ #include "play_manager.hpp" #include "game_manager.hpp" #include -#include +#include PlayManager::PlayManager(){}; From 8711790d1f16cec7d3e8f0f7dd1abfc04b8188b6 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 6 Apr 2023 04:47:40 +0200 Subject: [PATCH 11/45] fixed meson CI (linux) to use gcc-12 --- .github/workflows/meson.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/meson.yml b/.github/workflows/meson.yml index 82bbb380..005b55b0 100644 --- a/.github/workflows/meson.yml +++ b/.github/workflows/meson.yml @@ -45,13 +45,12 @@ jobs: sudo apt-get update sudo apt-get install ninja-build build-essential libx11-dev gcc-12 g++-12 libwxgtk3.0-gtk3-dev libgtk-3-dev libsdl2-2.0-0 libsdl2-dev -y - - name: Set Environment - Linux + - name: Configure (Linux) if: matrix.config.os == 'ubuntu-22.04' - run: | - export CXX=g++-12 - export CC=gcc-12 + run: CXX=g++-12 CC=gcc-12 meson setup build -Dbuildtype=${{ matrix.config.buildtype }} - - name: Configure + - name: Configure (Windows) + if: matrix.config.os == 'windows-latest' run: meson setup build -Dbuildtype=${{ matrix.config.buildtype }} - name: Build From 95ed3a3c50f570dca1e2229403d4f6d5afaa1770 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 6 Apr 2023 04:50:00 +0200 Subject: [PATCH 12/45] fixed msvc warning (error) --- src/network/network_transportable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index f4716372..2dc0062f 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -107,7 +107,7 @@ MaybeRawTransportData RawTransportData::from_raw_bytes(RawBytes raw_bytes) { advance(Transportable::header_size); - auto [protocol_version, serialUUID, data_size] = header.value(); + auto [_protocol_version, serialUUID, data_size] = header.value(); if (remaining_length < data_size) { return tl::make_unexpected( From 4ea8f0be8d843ba8f817b5b61c3c8fcde3436031 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 6 Apr 2023 04:53:34 +0200 Subject: [PATCH 13/45] fix more msvc errors --- src/network/connection_manager.cpp | 2 +- src/network/connection_manager.hpp | 2 +- src/network/network_manager.cpp | 4 ++-- src/network/network_manager.hpp | 6 +++--- src/network/network_transportable.cpp | 10 +++++----- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index 147f76be..2aee9d7b 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -57,7 +57,7 @@ tl::optional> Server::try_get_client() { return {}; } -tl::optional> Server::get_client(std::size_t ms_delay, std::size_t abort_after) { +tl::optional> Server::get_client(Uint32 ms_delay, std::size_t abort_after) { auto start_time = SDL_GetTicks64(); while (true) { /* try to accept a connection */ diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index bee81e15..84eb9eb2 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -52,7 +52,7 @@ struct Server { ~Server(); tl::optional> try_get_client(); tl::optional> - get_client(std::size_t ms_delay = 100, std::size_t abort_after = 60 * 1000); + get_client(Uint32 ms_delay = 100, std::size_t abort_after = 60 * 1000); tl::optional send_all(const Transportable* transportable, uint32_t data_size); MaybeData get_data(); diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index 09d46961..48c1cf01 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -11,7 +11,7 @@ NetworkManager::NetworkManager() : m_connections{ std::vector{} } {}; -MaybeConnection NetworkManager::try_connect(const char* host, std::size_t port) { +MaybeConnection NetworkManager::try_connect(const char* host, Uint16 port) { IPaddress ip; TCPsocket tcpsock; @@ -30,7 +30,7 @@ MaybeConnection NetworkManager::try_connect(const char* host, std::size_t port) return std::make_shared(tcpsock); } -MaybeServer NetworkManager::spawn_server(std::size_t port) { +MaybeServer NetworkManager::spawn_server(Uint16 port) { TCPsocket server; IPaddress ip; diff --git a/src/network/network_manager.hpp b/src/network/network_manager.hpp index b7af02f9..e243040d 100644 --- a/src/network/network_manager.hpp +++ b/src/network/network_manager.hpp @@ -21,10 +21,10 @@ struct NetworkManager { private: std::vector m_connections; static constexpr const char* ServerHost = "localhost"; - static constexpr const int Port = 1212; + static constexpr const Uint16 Port = 1212; public: explicit NetworkManager(); - MaybeConnection try_connect(const char* host = NetworkManager::ServerHost, std::size_t port = NetworkManager::Port); - MaybeServer spawn_server(std::size_t port = NetworkManager::Port); + MaybeConnection try_connect(const char* host = NetworkManager::ServerHost, Uint16 port = NetworkManager::Port); + MaybeServer spawn_server(Uint16 port = NetworkManager::Port); }; diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index 2dc0062f..421580ac 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -109,7 +109,7 @@ MaybeRawTransportData RawTransportData::from_raw_bytes(RawBytes raw_bytes) { auto [_protocol_version, serialUUID, data_size] = header.value(); - if (remaining_length < data_size) { + if (remaining_length < (long) data_size) { return tl::make_unexpected( "in RawTransportData::from_raw_bytes: couldn't read data, since the raw data is to small" ); @@ -142,18 +142,18 @@ tl::expected, std::strin uint32_t* data_ptr = (uint32_t*) start; - uint32_t protocol_version = data_ptr[0]; - if (RawTransportData::protocol_version != protocol_version) { + uint32_t protocol_version_number = data_ptr[0]; + if (RawTransportData::protocol_version != protocol_version_number) { return tl::make_unexpected( "couldn't parse header, since the protocol version mismatches: parser can parse: " + std::to_string(RawTransportData::protocol_version) - + "but received: " + std::to_string(protocol_version) + + "but received: " + std::to_string(protocol_version_number) ); } uint32_t serialUUID = data_ptr[1]; uint32_t data_size = data_ptr[2]; - return std::tuple{ protocol_version, serialUUID, data_size }; + return std::tuple{ protocol_version_number, serialUUID, data_size }; } From cf8f219e96e1d8762dbf3d41e1f4a44d686ea246 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Thu, 6 Apr 2023 22:43:00 +0200 Subject: [PATCH 14/45] - fixed consistency with std::uint32_t and uint32_t - fixed a few templates - remove free floating templates / functions --- src/local_multiplayer.cpp | 2 +- src/network/connection_manager.cpp | 22 ++++---- src/network/connection_manager.hpp | 40 ++++++-------- src/network/network_data.cpp | 9 +-- src/network/network_data.hpp | 9 +-- src/network/network_transportable.cpp | 67 +++++++--------------- src/network/network_transportable.hpp | 80 +++++++++++++++++++-------- src/network/online_handler.cpp | 2 +- src/network/online_handler.hpp | 2 +- src/tetris_application.hpp | 2 +- src/util.hpp | 17 ++++++ 11 files changed, 132 insertions(+), 120 deletions(-) create mode 100644 src/util.hpp diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index 44fdd8fe..ca5e35c2 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -95,7 +95,7 @@ std::unique_ptr LocalMultiplayer::get_input( auto connection = connection_result.value(); auto online_input = std::make_unique(associated_game_manager, connection); - auto send_data = InitializationData{ InitializationDataType::Client, (uint32_t) index }; + auto send_data = InitializationData{ InitializationDataType::Client, (std::uint32_t) index }; const auto send_result = ptr_connection_send_data(connection, &send_data); if (send_result.has_value()) { } return online_input; diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index 2aee9d7b..b486b87a 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -15,7 +15,7 @@ Connection::~Connection() { SDLNet_TCP_Close(m_socket); } -tl::optional Connection::send_data(const Transportable* transportable, uint32_t data_size) { +tl::optional Connection::send_data(const Transportable* transportable, std::uint32_t data_size) { auto [message, length] = Transportable::serialize(transportable, data_size); @@ -76,9 +76,9 @@ tl::optional> Server::get_client(Uint32 ms_delay, st } } -tl::optional Server::send_all(const Transportable* transportable, uint32_t data_size) { +tl::optional Server::send_all(const Transportable* transportable, std::uint32_t data_size) { - for (size_t i = 0; i < m_connections.size(); ++i) { + for (std::size_t i = 0; i < m_connections.size(); ++i) { auto result = m_connections.at(i)->send_data(transportable, data_size); if (result.has_value()) { return tl::make_optional("Error while sending to client: " + std::to_string(i) + " : " + result.value()); @@ -88,7 +88,7 @@ tl::optional Server::send_all(const Transportable* transportable, u return {}; } -tl::expected Server::is_data_available(Uint32 timeout_ms) { +tl::expected Connection::is_data_available(Uint32 timeout_ms) { SDLNet_SocketSet set = SDLNet_AllocSocketSet(1); if (!set) { @@ -113,26 +113,26 @@ tl::expected Server::is_data_available(Uint32 timeout_ms) { } -tl::expected Server::get_all_data_blocking() { - void* memory = std::malloc(Server::chunk_size); +tl::expected Connection::get_all_data_blocking() { + void* memory = std::malloc(Connection::chunk_size); if (!memory) { return tl::make_unexpected("error in malloc for receiving a socket message"); } std::uint32_t data_size = 0; while (true) { - int len = SDLNet_TCP_Recv(m_socket, memory, Server::chunk_size); + int len = SDLNet_TCP_Recv(m_socket, memory, Connection::chunk_size); if (len <= 0) { free(memory); return tl::make_unexpected("SDLNet_TCP_Recv: " + std::string{ SDLNet_GetError() }); } - if (len != Server::chunk_size) { + if (len != Connection::chunk_size) { return RawBytes{ (uint8_t*) memory, data_size + len }; } - data_size += Server::chunk_size; - void* new_memory = std::realloc(memory, data_size + Server::chunk_size); + data_size += Connection::chunk_size; + void* new_memory = std::realloc(memory, data_size + Connection::chunk_size); if (!new_memory) { free(memory); return tl::make_unexpected("error in realloc for receiving a socket message"); @@ -143,7 +143,7 @@ tl::expected Server::get_all_data_blocking() { return tl::make_unexpected("error in SDLNet_TCP_Recv: somehow exited the while loop"); } -MaybeData Server::get_data() { +MaybeData Connection::get_data() { auto data_available = is_data_available(); if (!data_available.has_value()) { diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index 84eb9eb2..750c760e 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -12,15 +12,25 @@ #include #include + +using MaybeData = tl::expected>>, std::string>; + + struct Connection { private: TCPsocket m_socket; + static constexpr std::size_t chunk_size = 1024; + public: explicit Connection(TCPsocket socket); ~Connection(); - tl::optional send_data(const Transportable* transportable, uint32_t data_size); + tl::optional send_data(const Transportable* transportable, std::uint32_t data_size); + + tl::expected is_data_available(Uint32 timeout_ms = 3); + tl::expected get_all_data_blocking(); + MaybeData get_data(); }; template @@ -33,8 +43,6 @@ tl::optional ptr_connection_send_data(C connection, const T* transp return connection->send_data(transportable, sizeof(T)); }; -using MaybeData = tl::expected>>, std::string>; - struct Server { @@ -42,29 +50,15 @@ struct Server { TCPsocket m_socket; std::vector> m_connections; - tl::expected is_data_available(Uint32 timeout_ms = 3); - tl::expected get_all_data_blocking(); - - static constexpr std::size_t chunk_size = 1024; - public: explicit Server(TCPsocket socket); ~Server(); tl::optional> try_get_client(); - tl::optional> - get_client(Uint32 ms_delay = 100, std::size_t abort_after = 60 * 1000); - tl::optional send_all(const Transportable* transportable, uint32_t data_size); - - MaybeData get_data(); -}; - - -template -tl::optional server_send_all(Server& server, const T* transportable) { - return server.send_all(transportable, sizeof(T)); -}; + tl::optional> get_client(Uint32 ms_delay = 100, std::size_t abort_after = 60 * 1000); + tl::optional send_all(const Transportable* transportable, std::uint32_t data_size); -template -tl::optional ptr_server_send_all(S server, const T* transportable) { - return server->send_all(transportable, sizeof(T)); + template + tl::optional send_all(const T* transportable) { + return server->send_all(transportable, sizeof(T)); + }; }; diff --git a/src/network/network_data.cpp b/src/network/network_data.cpp index 66a92a86..e6d66f7d 100644 --- a/src/network/network_data.cpp +++ b/src/network/network_data.cpp @@ -4,15 +4,8 @@ #include "network_transportable.hpp" #include -std::uint32_t InitializationData::serialUUID() const { - return 1; -} -InitializationData::InitializationData(InitializationDataType type, uint32_t uuid) : m_type{ type }, m_uuid{ uuid } {}; +InitializationData::InitializationData(InitializationDataType type, std::uint32_t uuid) : m_type{ type }, m_uuid{ uuid } {}; -std::uint32_t EventData::serialUUID() const { - return 2; -} - EventData::EventData(Input::Event event) : m_event{ event } {}; diff --git a/src/network/network_data.hpp b/src/network/network_data.hpp index c5a332bc..d8f73bfb 100644 --- a/src/network/network_data.hpp +++ b/src/network/network_data.hpp @@ -9,13 +9,14 @@ enum class InitializationDataType : uint8_t { Client, Server }; struct InitializationData : public Transportable { + static constexpr std::uint32_t serialUUID = 1; + private: InitializationDataType m_type; - uint32_t m_uuid; + std::uint32_t m_uuid; public: - uint32_t serialUUID() const override; - explicit InitializationData(InitializationDataType type, uint32_t uuid); + explicit InitializationData(InitializationDataType type, std::uint32_t uuid); }; struct EventData : public Transportable { @@ -23,6 +24,6 @@ struct EventData : public Transportable { Input::Event m_event; public: - uint32_t serialUUID() const override; + static constexpr std::uint32_t serialUUID = 2; explicit EventData(Input::Event event); }; \ No newline at end of file diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index 421580ac..1a2c3699 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -1,6 +1,7 @@ #include "network_transportable.hpp" +#include "../util.hpp" #include "crc32.h" #include #include @@ -12,34 +13,13 @@ #include -//TODO fix inconsistency and don't raise exceptions, rather return tl:expected -RawBytes Transportable::serialize(const Transportable* transportable, uint32_t data_size) { - - const uint32_t send_size = Transportable::header_size + data_size + Transportable::checksum_size; - - uint8_t* memory = (uint8_t*) std::malloc(send_size); - if (!memory) { - throw std::runtime_error{ "error in malloc for sending a message" }; - } - - - Transportable::write_header(RawBytes{ memory, Transportable::header_size }, transportable->serialUUID(), data_size); - - Transportable::write_data(RawBytes{ memory + Transportable::header_size, data_size }, transportable); - Transportable::write_checksum(RawBytes{ memory + Transportable::header_size, - data_size + Transportable::checksum_size }); - - return RawBytes{ memory, send_size }; -} - - -uint32_t Transportable::checksum(RawBytes bytes) { +std::uint32_t Transportable::checksum(RawBytes bytes) { auto [start, length] = bytes; - uint32_t table[256]; + std::uint32_t table[256]; crc32::generate_table(table); - uint32_t CRC = 0; + std::uint32_t CRC = 0; for (std::uint32_t i = 0; i < length; ++i) { CRC = crc32::update(table, CRC, start, 1); start++; @@ -49,13 +29,13 @@ uint32_t Transportable::checksum(RawBytes bytes) { } -void Transportable::write_header(RawBytes bytes, uint32_t serialUUID, uint32_t data_size) { +void Transportable::write_header(RawBytes bytes, std::uint32_t serialUUID, std::uint32_t data_size) { auto [start, length] = bytes; //TODO remove assert assert(length == Transportable::header_size); - uint32_t* data_ptr = (uint32_t*) start; + std::uint32_t* data_ptr = (std::uint32_t*) start; data_ptr[0] = Transportable::protocol_version; @@ -76,16 +56,16 @@ void Transportable::write_checksum(RawBytes bytes) { auto [start, length] = bytes; - uint32_t data_size = length - Transportable::checksum_size; + std::uint32_t data_size = length - Transportable::checksum_size; - uint32_t checksum = Transportable::checksum(RawBytes{ start, data_size }); + std::uint32_t checksum = Transportable::checksum(RawBytes{ start, data_size }); - uint32_t* data_ptr = (uint32_t*) ((uint8_t*) start + data_size); + std::uint32_t* data_ptr = (std::uint32_t*) ((uint8_t*) start + data_size); data_ptr[0] = checksum; } -RawTransportData::RawTransportData(uint32_t serialUUID, RawBytes data) : m_serialUUID{ serialUUID }, m_data{ data } {}; +RawTransportData::RawTransportData(std::uint32_t serialUUID, RawBytes data) : m_serialUUID{ serialUUID }, m_data{ data } {}; MaybeRawTransportData RawTransportData::from_raw_bytes(RawBytes raw_bytes) { @@ -94,7 +74,7 @@ MaybeRawTransportData RawTransportData::from_raw_bytes(RawBytes raw_bytes) { auto [start, length] = raw_bytes; long remaining_length = length; - auto advance = [&](uint32_t size) { + auto advance = [&](std::uint32_t size) { remaining_length -= size; start += size; }; @@ -140,9 +120,9 @@ tl::expected, std::strin return tl::make_unexpected("couldn't read header, since the raw data is to small"); } - uint32_t* data_ptr = (uint32_t*) start; + std::uint32_t* data_ptr = (std::uint32_t*) start; - uint32_t protocol_version_number = data_ptr[0]; + std::uint32_t protocol_version_number = data_ptr[0]; if (RawTransportData::protocol_version != protocol_version_number) { return tl::make_unexpected( "couldn't parse header, since the protocol version mismatches: parser can parse: " @@ -151,35 +131,30 @@ tl::expected, std::strin ); } - uint32_t serialUUID = data_ptr[1]; - uint32_t data_size = data_ptr[2]; + std::uint32_t serialUUID = data_ptr[1]; + std::uint32_t data_size = data_ptr[2]; return std::tuple{ protocol_version_number, serialUUID, data_size }; } -tl::expected RawTransportData::read_checksum(RawBytes bytes, uint32_t data_size) { +tl::expected RawTransportData::read_checksum(RawBytes bytes, std::uint32_t data_size) { auto [start, length] = bytes; if (length < data_size + Transportable::checksum_size) { return tl::make_unexpected("couldn't read checksum, since the raw data is to small"); } - uint32_t calc_checksum = Transportable::checksum(RawBytes{ start, data_size }); + std::uint32_t calc_checksum = Transportable::checksum(RawBytes{ start, data_size }); - uint32_t* data_ptr = (uint32_t*) ((uint8_t*) start + data_size); + std::uint32_t* data_ptr = (std::uint32_t*) ((uint8_t*) start + data_size); - uint32_t read_checksum = data_ptr[0]; + std::uint32_t read_checksum = data_ptr[0]; if (read_checksum != calc_checksum) { return tl::make_unexpected( - "couldn't read data, since the checksum mismatches: read checksum: " + to_hex_str(read_checksum) - + "but calculated checksum: " + to_hex_str(calc_checksum) + "couldn't read data, since the checksum mismatches: read checksum: " + util::to_hex_str(read_checksum) + + "but calculated checksum: " + util::to_hex_str(calc_checksum) ); } return read_checksum; -} - -//TODO this shouldn't need an Transportable*, but just the type -bool RawTransportData::is_of_type(Transportable* transportable) { - return m_serialUUID == transportable->serialUUID(); } \ No newline at end of file diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp index 853402e1..5c8b45c6 100644 --- a/src/network/network_transportable.hpp +++ b/src/network/network_transportable.hpp @@ -5,10 +5,10 @@ #include #include #include -#include #include #include #include +#include #include #include @@ -18,25 +18,41 @@ using RawBytes = std::pair; public: virtual ~Transportable() = default; // if you change the protocol behaviour in some way, change this number and the number in RawTransportData - static constexpr uint32_t protocol_version = 1; - static constexpr uint32_t checksum_size = sizeof(uint32_t); - static constexpr uint32_t header_size = sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t); - - //TODO de-serialize - // static std::map deserialize_map = std::map; + static constexpr std::uint32_t protocol_version = 1; + static constexpr std::uint32_t checksum_size = sizeof(std::uint32_t); + static constexpr std::uint32_t header_size = sizeof(std::uint32_t) + sizeof(std::uint32_t) + sizeof(std::uint32_t); // every class has to have such a serialUUID in some way //TODO enforce this in some compile time way, that they are unique! - virtual uint32_t serialUUID() const = 0; - static RawBytes serialize(const Transportable* transportable, std::uint32_t data_size); + /* virtual */ static constexpr std::uint32_t serialUUID = 0; + //TODO fix inconsistency and don't raise exceptions, rather return tl:expected + template + static RawBytes serialize(const T* transportable, std::uint32_t data_size) { + + const std::uint32_t send_size = Transportable::header_size + data_size + Transportable::checksum_size; + + uint8_t* memory = (uint8_t*) std::malloc(send_size); + if (!memory) { + throw std::runtime_error{ "error in malloc for sending a message" }; + } + + + Transportable::write_header(RawBytes{ memory, Transportable::header_size }, T::serialUUID, data_size); - static uint32_t checksum(RawBytes bytes); + Transportable::write_data(RawBytes{ memory + Transportable::header_size, data_size }, transportable); + Transportable::write_checksum(RawBytes{ memory + Transportable::header_size, + data_size + Transportable::checksum_size }); + + return RawBytes{ memory, send_size }; + } + + static std::uint32_t checksum(RawBytes bytes); protected: explicit Transportable() { } private: - static void write_header(RawBytes bytes, uint32_t serialUUID, uint32_t data_size); + static void write_header(RawBytes bytes, std::uint32_t serialUUID, std::uint32_t data_size); static void write_data(RawBytes bytes, const Transportable* transportable); static void write_checksum(RawBytes bytes); }; @@ -46,19 +62,19 @@ using RawBytes = std::pair; struct RawTransportData { private: - uint32_t m_serialUUID; + std::uint32_t m_serialUUID; RawBytes m_data; public: - RawTransportData(uint32_t serialUUID, RawBytes data); + RawTransportData(std::uint32_t serialUUID, RawBytes data); // if you change the protocol behaviour in some way, change this number and the number in Transportable (this parses only packets with the same protocol_version) - static constexpr uint32_t protocol_version = 1; + static constexpr std::uint32_t protocol_version = 1; static tl::expected>, std::string> from_raw_bytes(RawBytes raw_bytes); static tl::expected, std::string> read_header(RawBytes bytes ); - static tl::expected read_checksum(RawBytes bytes, uint32_t data_size); + static tl::expected read_checksum(RawBytes bytes, std::uint32_t data_size); bool is_of_type(Transportable* transportable); }; @@ -66,22 +82,38 @@ struct RawTransportData { using MaybeRawTransportData = tl::expected>, std::string>; -template -std::string to_hex_str(T number) { - std::ostringstream ss{}; - ss << std::hex << number; - return ss.str(); -} - template using RawUniqueTransportData = std::unique_ptr>; + +template +bool raw_transport_data_is_of_type(RawTransportData& data) { + return data.m_serialUUID == T::serialUUID; +} + +template +bool ptr_raw_transport_data_is_of_type(P data) { + return data->m_serialUUID == T::serialUUID; +} + // TODO: RawTransportData data is invalid after this, that has to be enforced in some way (at compile time) template RawUniqueTransportData raw_transport_data_to(RawTransportData& data) { - //TODO pre-check this, after making serialUUID() static! - //if(RawTransportData::is_of_type(nullptr)) + if (!raw_transport_data_is_of_type(data)) { + throw std::bad_cast{}; + } // TODO: check if this is correct (with free. etc)! RawUniqueTransportData raw_unique_ptr{ std::move(data.m_data) /*.first*/, [](void* ptr) { free(ptr); } }; return raw_unique_ptr; } + +// TODO: RawTransportData data is invalid after this, that has to be enforced in some way (at compile time) +template +RawUniqueTransportData ptr_raw_transport_data_to(R data) { + if (!ptr_raw_transport_data_is_of_type(data)) { + throw std::bad_cast{}; + } + // TODO: check if this is correct (with free. etc)! + RawUniqueTransportData raw_unique_ptr{ std::move(*data.m_data) /*.first*/, [](void* ptr) { free(ptr); } }; + return raw_unique_ptr; +} \ No newline at end of file diff --git a/src/network/online_handler.cpp b/src/network/online_handler.cpp index bf9770d1..eaf172ed 100644 --- a/src/network/online_handler.cpp +++ b/src/network/online_handler.cpp @@ -7,7 +7,7 @@ #include #include -OnlineHandler::OnlineHandler(std::shared_ptr server, std::shared_ptr connection, uint32_t uuid) +OnlineHandler::OnlineHandler(std::shared_ptr server, std::shared_ptr connection, std::uint32_t uuid) : m_server{ server }, m_connection{ connection }, m_uuid{ uuid } {}; diff --git a/src/network/online_handler.hpp b/src/network/online_handler.hpp index 1afec893..d46679dc 100644 --- a/src/network/online_handler.hpp +++ b/src/network/online_handler.hpp @@ -12,6 +12,6 @@ struct OnlineHandler { std::uint32_t m_uuid; public: - OnlineHandler(std::shared_ptr server, std::shared_ptr connection, uint32_t uuid); + OnlineHandler(std::shared_ptr server, std::shared_ptr connection, std::uint32_t uuid); void handle_event(Input::Event event); }; \ No newline at end of file diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index 4a924591..dc619485 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -48,7 +48,7 @@ struct TetrisApplication : public Application { } //TODO if this is to big to handle num_players, we have to resize in some way - [[maybe_unused]] const size_t game_field_size = + [[maybe_unused]] const std::size_t game_field_size = (GameManager::size_per_field * num_players) + ((num_players - 1) * GameManager::space_between); diff --git a/src/util.hpp b/src/util.hpp new file mode 100644 index 00000000..7ebfa5e8 --- /dev/null +++ b/src/util.hpp @@ -0,0 +1,17 @@ + + +#pragma once + +#include +#include + +namespace util { + + + template + std::string to_hex_str(T number) { + std::ostringstream ss{}; + ss << std::hex << number; + return ss.str(); + } +} // namespace util From d878672fb444e4f84d7de41514f3dc9bf27cbca5 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 00:05:07 +0200 Subject: [PATCH 15/45] - fixed many free floating templates - made working with raw network sent data easier - fixed ownership of received data --- src/input.cpp | 23 ++++++++++- src/input.hpp | 1 - src/local_multiplayer.cpp | 2 +- src/network/connection_manager.cpp | 2 +- src/network/connection_manager.hpp | 19 ++++----- src/network/network_data.hpp | 4 +- src/network/network_transportable.cpp | 20 +++++---- src/network/network_transportable.hpp | 58 ++++++++------------------- src/network/online_handler.cpp | 4 +- 9 files changed, 63 insertions(+), 70 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index 68a69df2..6494e1bb 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1,6 +1,7 @@ #include "input.hpp" #include "application.hpp" #include "game_manager.hpp" +#include "network/network_data.hpp" #include void KeyboardInput::update() { @@ -65,5 +66,25 @@ void KeyboardInput::handle_event(const SDL_Event& event) { void OnlineInput::update() { Input::update(); - //TODO implement + auto data = m_connection->get_data(); + if (!data.has_value()) { + // TODO: print error here (to log e.g.) + // auto error = data.error(); + return; + } + + if (!data.value().has_value()) { + // no data given + return; + } + + const auto data_vector = data.value().value(); + for (const auto& data : data_vector) { + + if (data.is_of_type()) { + auto event = data.as_type(); + //TODO maybe handle return value ? + m_target_game_manager->handle_input_event(event->m_event); + } + } } diff --git a/src/input.hpp b/src/input.hpp index a89595dc..e0a1e279 100644 --- a/src/input.hpp +++ b/src/input.hpp @@ -53,7 +53,6 @@ struct KeyboardInput : public Input, public EventListener { struct OnlineInput : public Input { private: std::shared_ptr m_connection; - //TODO public: explicit OnlineInput(GameManager* target_game_manager, std::shared_ptr connection) diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index ca5e35c2..c23cddb2 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -96,7 +96,7 @@ std::unique_ptr LocalMultiplayer::get_input( auto connection = connection_result.value(); auto online_input = std::make_unique(associated_game_manager, connection); auto send_data = InitializationData{ InitializationDataType::Client, (std::uint32_t) index }; - const auto send_result = ptr_connection_send_data(connection, &send_data); + const auto send_result = connection->send_data(&send_data); if (send_result.has_value()) { } return online_input; } diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index b486b87a..469a26be 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -169,5 +169,5 @@ MaybeData Connection::get_data() { } free(raw_bytes.first); - return tl::make_optional(std::move(result.value())); + return tl::make_optional(result.value()); } diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index 750c760e..f1734914 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -13,7 +13,7 @@ #include -using MaybeData = tl::expected>>, std::string>; +using MaybeData = tl::expected>, std::string>; struct Connection { @@ -31,16 +31,11 @@ struct Connection { tl::expected is_data_available(Uint32 timeout_ms = 3); tl::expected get_all_data_blocking(); MaybeData get_data(); -}; - -template -tl::optional connection_send_data(Connection& connection, const T* transportable) { - return connection.send_data(transportable, sizeof(T)); -}; -template -tl::optional ptr_connection_send_data(C connection, const T* transportable) { - return connection->send_data(transportable, sizeof(T)); + template + tl::optional send_data(const T* transportable) { + return send_data(transportable, sizeof(T)); + } }; @@ -59,6 +54,6 @@ struct Server { template tl::optional send_all(const T* transportable) { - return server->send_all(transportable, sizeof(T)); - }; + return send_all(transportable, sizeof(T)); + } }; diff --git a/src/network/network_data.hpp b/src/network/network_data.hpp index d8f73bfb..7160ee3e 100644 --- a/src/network/network_data.hpp +++ b/src/network/network_data.hpp @@ -20,10 +20,8 @@ struct InitializationData : public Transportable { }; struct EventData : public Transportable { -private: - Input::Event m_event; - public: + Input::Event m_event; static constexpr std::uint32_t serialUUID = 2; explicit EventData(Input::Event event); }; \ No newline at end of file diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index 1a2c3699..48dbc823 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -65,12 +65,15 @@ void Transportable::write_checksum(RawBytes bytes) { data_ptr[0] = checksum; } -RawTransportData::RawTransportData(std::uint32_t serialUUID, RawBytes data) : m_serialUUID{ serialUUID }, m_data{ data } {}; +RawTransportData::RawTransportData(std::uint32_t serialUUID, std::shared_ptr data, uint32_t data_size) + : m_serialUUID{ serialUUID }, + m_data{ data }, + m_data_size{ data_size } {}; -MaybeRawTransportData RawTransportData::from_raw_bytes(RawBytes raw_bytes) { +tl::expected, std::string> RawTransportData::from_raw_bytes(RawBytes raw_bytes) { - auto result = std::vector>{}; + auto result = std::vector{}; auto [start, length] = raw_bytes; long remaining_length = length; @@ -96,17 +99,18 @@ MaybeRawTransportData RawTransportData::from_raw_bytes(RawBytes raw_bytes) { } //TODO check if implemented correctly - // this malloc get'S freed in the unique ptr destructor later - void* memory = std::malloc(data_size); + // this malloc get'S freed in the shared ptr destructor later + uint8_t* memory = (uint8_t*) std::malloc(data_size); if (!memory) { - return tl::make_unexpected("in RawTransportData::from_raw_bytes: error in malloc for RawTransportData"); + return tl::make_unexpected("in RawTransportData::from_raw_bytes: error in malloc for raw data"); } - auto data = RawBytes{ (uint8_t*) std::memcpy(memory, start, data_size), data_size }; + std::memcpy(memory, start, data_size); + std::shared_ptr data{ memory, std::free }; auto checksum = RawTransportData::read_checksum(RawBytes{ start, remaining_length }, data_size); advance(data_size + Transportable::checksum_size); - result.push_back(std::make_unique(serialUUID, data)); + result.emplace_back(serialUUID, data, data_size); } return result; diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp index 5c8b45c6..6bb6658b 100644 --- a/src/network/network_transportable.hpp +++ b/src/network/network_transportable.hpp @@ -63,57 +63,33 @@ using RawBytes = std::pair; struct RawTransportData { private: std::uint32_t m_serialUUID; - RawBytes m_data; + std::shared_ptr m_data; + uint32_t m_data_size; public: - RawTransportData(std::uint32_t serialUUID, RawBytes data); + RawTransportData(std::uint32_t serialUUID, std::shared_ptr data, uint32_t data_size); // if you change the protocol behaviour in some way, change this number and the number in Transportable (this parses only packets with the same protocol_version) static constexpr std::uint32_t protocol_version = 1; - static tl::expected>, std::string> from_raw_bytes(RawBytes raw_bytes); + static tl::expected, std::string> from_raw_bytes(RawBytes raw_bytes); static tl::expected, std::string> read_header(RawBytes bytes ); static tl::expected read_checksum(RawBytes bytes, std::uint32_t data_size); - bool is_of_type(Transportable* transportable); -}; - -using MaybeRawTransportData = tl::expected>, std::string>; - - -template -using RawUniqueTransportData = std::unique_ptr>; - - -template -bool raw_transport_data_is_of_type(RawTransportData& data) { - return data.m_serialUUID == T::serialUUID; -} + template + bool is_of_type() const { + return m_serialUUID == T::serialUUID; + } -template -bool ptr_raw_transport_data_is_of_type(P data) { - return data->m_serialUUID == T::serialUUID; -} + template + std::shared_ptr as_type() const { + if (!is_of_type()) { + throw std::bad_cast{}; + } -// TODO: RawTransportData data is invalid after this, that has to be enforced in some way (at compile time) -template -RawUniqueTransportData raw_transport_data_to(RawTransportData& data) { - if (!raw_transport_data_is_of_type(data)) { - throw std::bad_cast{}; - } - // TODO: check if this is correct (with free. etc)! - RawUniqueTransportData raw_unique_ptr{ std::move(data.m_data) /*.first*/, [](void* ptr) { free(ptr); } }; - return raw_unique_ptr; -} - -// TODO: RawTransportData data is invalid after this, that has to be enforced in some way (at compile time) -template -RawUniqueTransportData ptr_raw_transport_data_to(R data) { - if (!ptr_raw_transport_data_is_of_type(data)) { - throw std::bad_cast{}; + // using copy constructor, so that this only get's freed, when also the new usage of this raw malloced memory is done + std::shared_ptr data = std::reinterpret_pointer_cast(m_data); + return data; } - // TODO: check if this is correct (with free. etc)! - RawUniqueTransportData raw_unique_ptr{ std::move(*data.m_data) /*.first*/, [](void* ptr) { free(ptr); } }; - return raw_unique_ptr; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/network/online_handler.cpp b/src/network/online_handler.cpp index eaf172ed..f3e059e9 100644 --- a/src/network/online_handler.cpp +++ b/src/network/online_handler.cpp @@ -17,7 +17,7 @@ void OnlineHandler::handle_event(Input::Event event) { if (m_server) { auto event_data = EventData{ event }; //TODO handle error - ptr_server_send_all(m_server, &event_data); + m_server->send_all(&event_data); /* m_server.send_all_if(EventData{ event }, []() { @@ -26,7 +26,7 @@ void OnlineHandler::handle_event(Input::Event event) { } else if (m_connection) { auto event_data = EventData{ event }; //TODO handle error - ptr_connection_send_data(m_connection, &event_data); + m_connection->send_data(&event_data); } else { throw std::runtime_error{ "OnlineHandler needs either a connection (client mode) or server!" }; } From e28ff19db9b5a3e5285b49a08db3c5fbd67c316b Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 00:15:08 +0200 Subject: [PATCH 16/45] fixed draw position of the preview field --- src/grid.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/grid.cpp b/src/grid.cpp index c7ba3469..440a07d8 100644 --- a/src/grid.cpp +++ b/src/grid.cpp @@ -63,12 +63,12 @@ void Grid::draw_preview_background(const Application& app) const { const Point preview_top_left = to_screen_coords(preview_background_position); const Point preview_bottom_right = preview_top_left + Point{ tile_size().x * preview_extends.x - 1, tile_size().y * preview_extends.y - 1 }; - app.renderer().draw_rect_filled(m_start_point + Rect{ preview_top_left, preview_bottom_right }, background_color); + app.renderer().draw_rect_filled(Rect{ preview_top_left, preview_bottom_right }, background_color); const Point outline_top_left = preview_top_left - Point{ 1, 1 }; const Point outline_bottom_right = preview_bottom_right + Point{ 1, 1 }; const Rect outline_rect = Rect{ outline_top_left, outline_bottom_right }; - app.renderer().draw_rect_outline(m_start_point + outline_rect, border_color); + app.renderer().draw_rect_outline(outline_rect, border_color); } void Grid::draw_playing_field_background(const Application& app) const { From 1e3ef32e11c0d95691609a5f01c05f3054f5ab52 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 00:53:50 +0200 Subject: [PATCH 17/45] fixed sending data, now each client can send its inputs (but the start piece differs xD) --- src/input.cpp | 2 +- src/network/connection_manager.cpp | 32 ------------------------ src/network/connection_manager.hpp | 36 +++++++++++++++++++++++---- src/network/network_data.cpp | 8 +++++- src/network/network_data.hpp | 5 +++- src/network/network_transportable.cpp | 15 ++++++----- src/network/network_transportable.hpp | 5 ++-- src/util.hpp | 10 +++++--- 8 files changed, 62 insertions(+), 51 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index 6494e1bb..f161a37a 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -84,7 +84,7 @@ void OnlineInput::update() { if (data.is_of_type()) { auto event = data.as_type(); //TODO maybe handle return value ? - m_target_game_manager->handle_input_event(event->m_event); + m_target_game_manager->handle_input_event(event->event()); } } } diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index 469a26be..d96d3f5d 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -15,27 +15,6 @@ Connection::~Connection() { SDLNet_TCP_Close(m_socket); } -tl::optional Connection::send_data(const Transportable* transportable, std::uint32_t data_size) { - - auto [message, length] = Transportable::serialize(transportable, data_size); - - const auto result = SDLNet_TCP_Send(m_socket, message, length); - if (result == -1) { - std::free(message); - return tl::make_optional("SDLNet_TCP_Send: invalid socket"); - } - - if ((std::size_t) result != length) { - std::free(message); - std::string error = "SDLNet_TCP_Send: " + std::string{ SDLNet_GetError() }; - return tl::make_optional(error); - } - - std::free(message); - - return {}; -}; - Server::Server(TCPsocket socket) : m_socket{ socket }, m_connections{ std::vector>{} } {}; @@ -76,17 +55,6 @@ tl::optional> Server::get_client(Uint32 ms_delay, st } } -tl::optional Server::send_all(const Transportable* transportable, std::uint32_t data_size) { - - for (std::size_t i = 0; i < m_connections.size(); ++i) { - auto result = m_connections.at(i)->send_data(transportable, data_size); - if (result.has_value()) { - return tl::make_optional("Error while sending to client: " + std::to_string(i) + " : " + result.value()); - } - } - - return {}; -} tl::expected Connection::is_data_available(Uint32 timeout_ms) { diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index f1734914..4fed3f0d 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -12,7 +12,6 @@ #include #include - using MaybeData = tl::expected>, std::string>; @@ -26,7 +25,6 @@ struct Connection { public: explicit Connection(TCPsocket socket); ~Connection(); - tl::optional send_data(const Transportable* transportable, std::uint32_t data_size); tl::expected is_data_available(Uint32 timeout_ms = 3); tl::expected get_all_data_blocking(); @@ -34,7 +32,25 @@ struct Connection { template tl::optional send_data(const T* transportable) { - return send_data(transportable, sizeof(T)); + + + auto [message, length] = Transportable::serialize(transportable); + + const auto result = SDLNet_TCP_Send(m_socket, message, length); + if (result == -1) { + std::free(message); + return tl::make_optional("SDLNet_TCP_Send: invalid socket"); + } + + if ((std::size_t) result != length) { + std::free(message); + std::string error = "SDLNet_TCP_Send: " + std::string{ SDLNet_GetError() }; + return tl::make_optional(error); + } + + std::free(message); + + return {}; } }; @@ -50,10 +66,20 @@ struct Server { ~Server(); tl::optional> try_get_client(); tl::optional> get_client(Uint32 ms_delay = 100, std::size_t abort_after = 60 * 1000); - tl::optional send_all(const Transportable* transportable, std::uint32_t data_size); template tl::optional send_all(const T* transportable) { - return send_all(transportable, sizeof(T)); + + + for (std::size_t i = 0; i < m_connections.size(); ++i) { + auto result = m_connections.at(i)->send_data(transportable); + if (result.has_value()) { + return tl::make_optional( + "Error while sending to client: " + std::to_string(i) + " : " + result.value() + ); + } + } + + return {}; } }; diff --git a/src/network/network_data.cpp b/src/network/network_data.cpp index e6d66f7d..7a578e32 100644 --- a/src/network/network_data.cpp +++ b/src/network/network_data.cpp @@ -5,7 +5,13 @@ #include -InitializationData::InitializationData(InitializationDataType type, std::uint32_t uuid) : m_type{ type }, m_uuid{ uuid } {}; +InitializationData::InitializationData(InitializationDataType type, std::uint32_t uuid) + : m_type{ type }, + m_uuid{ uuid } {}; EventData::EventData(Input::Event event) : m_event{ event } {}; + +Input::Event EventData::event() const { + return m_event; +} \ No newline at end of file diff --git a/src/network/network_data.hpp b/src/network/network_data.hpp index 7160ee3e..007cca80 100644 --- a/src/network/network_data.hpp +++ b/src/network/network_data.hpp @@ -20,8 +20,11 @@ struct InitializationData : public Transportable { }; struct EventData : public Transportable { -public: +private: Input::Event m_event; + +public: static constexpr std::uint32_t serialUUID = 2; explicit EventData(Input::Event event); + Input::Event event() const; }; \ No newline at end of file diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index 48dbc823..460a7e61 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -29,7 +29,7 @@ std::uint32_t Transportable::checksum(RawBytes bytes) { } -void Transportable::write_header(RawBytes bytes, std::uint32_t serialUUID, std::uint32_t data_size) { +void Transportable::write_header(RawBytes bytes, std::uint32_t uuid, std::uint32_t data_size) { auto [start, length] = bytes; //TODO remove assert @@ -39,7 +39,7 @@ void Transportable::write_header(RawBytes bytes, std::uint32_t serialUUID, std:: data_ptr[0] = Transportable::protocol_version; - data_ptr[1] = serialUUID; + data_ptr[1] = uuid; data_ptr[2] = data_size; } @@ -76,6 +76,7 @@ tl::expected, std::string> RawTransportData::from_ auto result = std::vector{}; auto [start, length] = raw_bytes; + long remaining_length = length; auto advance = [&](std::uint32_t size) { remaining_length -= size; @@ -90,7 +91,7 @@ tl::expected, std::string> RawTransportData::from_ advance(Transportable::header_size); - auto [_protocol_version, serialUUID, data_size] = header.value(); + auto [_protocol_version, uuid, data_size] = header.value(); if (remaining_length < (long) data_size) { return tl::make_unexpected( @@ -110,7 +111,9 @@ tl::expected, std::string> RawTransportData::from_ auto checksum = RawTransportData::read_checksum(RawBytes{ start, remaining_length }, data_size); advance(data_size + Transportable::checksum_size); - result.emplace_back(serialUUID, data, data_size); + result.emplace_back(uuid, data, data_size); + + std::cout << "data was: serialUUID: " << uuid << " data_size: " << data_size << "\n"; } return result; @@ -135,9 +138,9 @@ tl::expected, std::strin ); } - std::uint32_t serialUUID = data_ptr[1]; + std::uint32_t uuid = data_ptr[1]; std::uint32_t data_size = data_ptr[2]; - return std::tuple{ protocol_version_number, serialUUID, data_size }; + return std::tuple{ protocol_version_number, uuid, data_size }; } diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp index 6bb6658b..eca01b52 100644 --- a/src/network/network_transportable.hpp +++ b/src/network/network_transportable.hpp @@ -27,7 +27,8 @@ using RawBytes = std::pair; /* virtual */ static constexpr std::uint32_t serialUUID = 0; //TODO fix inconsistency and don't raise exceptions, rather return tl:expected template - static RawBytes serialize(const T* transportable, std::uint32_t data_size) { + static RawBytes serialize(const T* transportable) { + constexpr std::uint32_t data_size = sizeof(T); const std::uint32_t send_size = Transportable::header_size + data_size + Transportable::checksum_size; @@ -52,7 +53,7 @@ using RawBytes = std::pair; explicit Transportable() { } private: - static void write_header(RawBytes bytes, std::uint32_t serialUUID, std::uint32_t data_size); + static void write_header(RawBytes bytes, std::uint32_t uuid, std::uint32_t data_size); static void write_data(RawBytes bytes, const Transportable* transportable); static void write_checksum(RawBytes bytes); }; diff --git a/src/util.hpp b/src/util.hpp index 7ebfa5e8..b80289b3 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -2,16 +2,20 @@ #pragma once +#include +#include #include #include namespace util { - template - std::string to_hex_str(T number) { + inline std::string to_hex_str(uint8_t number) { std::ostringstream ss{}; - ss << std::hex << number; + + ss << "0x" << std::hex << std::setfill('0'); + ss << std::hex << std::setw(2) << static_cast(number); return ss.str(); } + } // namespace util From ce7151b3c8fafa93f2b2e4ded837cc44334cb2ba Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 01:09:01 +0200 Subject: [PATCH 18/45] replaced c-like casts with static_cats where possible --- src/network/connection_manager.cpp | 8 ++++++-- src/network/network_transportable.cpp | 8 ++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index d96d3f5d..00ef3122 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -96,7 +96,7 @@ tl::expected Connection::get_all_data_blocking() { if (len != Connection::chunk_size) { - return RawBytes{ (uint8_t*) memory, data_size + len }; + return RawBytes{ static_cast(memory), data_size + len }; } data_size += Connection::chunk_size; @@ -110,7 +110,11 @@ tl::expected Connection::get_all_data_blocking() { return tl::make_unexpected("error in SDLNet_TCP_Recv: somehow exited the while loop"); } - +/* +On error tl::unexpected is returned +if no data is available tl::optional is returned +if data is available a vector of such data is returned, it is guaranteed to be nonempty! +*/ MaybeData Connection::get_data() { auto data_available = is_data_available(); diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index 460a7e61..8c6d42a2 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -47,7 +47,7 @@ void Transportable::write_data(RawBytes bytes, const Transportable* transportabl auto [start, length] = bytes; - uint8_t* data_ptr = (uint8_t*) transportable; + uint8_t* data_ptr = (uint8_t*) (transportable); std::memcpy(start, data_ptr, length); } @@ -60,7 +60,7 @@ void Transportable::write_checksum(RawBytes bytes) { std::uint32_t checksum = Transportable::checksum(RawBytes{ start, data_size }); - std::uint32_t* data_ptr = (std::uint32_t*) ((uint8_t*) start + data_size); + std::uint32_t* data_ptr = (std::uint32_t*) (static_cast(start) + data_size); data_ptr[0] = checksum; } @@ -101,7 +101,7 @@ tl::expected, std::string> RawTransportData::from_ //TODO check if implemented correctly // this malloc get'S freed in the shared ptr destructor later - uint8_t* memory = (uint8_t*) std::malloc(data_size); + uint8_t* memory = static_cast(std::malloc(data_size)); if (!memory) { return tl::make_unexpected("in RawTransportData::from_raw_bytes: error in malloc for raw data"); } @@ -152,7 +152,7 @@ tl::expected RawTransportData::read_checksum(RawByte std::uint32_t calc_checksum = Transportable::checksum(RawBytes{ start, data_size }); - std::uint32_t* data_ptr = (std::uint32_t*) ((uint8_t*) start + data_size); + std::uint32_t* data_ptr = (std::uint32_t*) (static_cast(start) + data_size); std::uint32_t read_checksum = data_ptr[0]; From 1b2522321211552652cebe876572d4c5a05dfb09 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 02:04:56 +0200 Subject: [PATCH 19/45] fixed so that also the client can receive data --- src/local_multiplayer.cpp | 12 +++++++++- src/network/connection_manager.cpp | 37 ++++++++++++++++++++++++++++-- src/network/connection_manager.hpp | 5 +++- src/network/network_manager.cpp | 9 ++++---- src/network/network_util.hpp | 23 +++++++++++++++++++ src/sdl_context.cpp | 3 ++- 6 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 src/network/network_util.hpp diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index c23cddb2..1c189dda 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -84,6 +84,14 @@ std::unique_ptr LocalMultiplayer::get_input( throw std::runtime_error{ "Waited to long for new client" }; } auto online_input = std::make_unique(associated_game_manager, client.value()); + + auto send_initializer = client.value()->wait_for_data(10 * 1000, 100); + if (!send_initializer.has_value()) { + throw std::runtime_error{ "client didn't send InitializationData in time: " + + send_initializer.error() }; + } + + return online_input; } else { @@ -97,7 +105,9 @@ std::unique_ptr LocalMultiplayer::get_input( auto online_input = std::make_unique(associated_game_manager, connection); auto send_data = InitializationData{ InitializationDataType::Client, (std::uint32_t) index }; const auto send_result = connection->send_data(&send_data); - if (send_result.has_value()) { } + if (send_result.has_value()) { + throw std::runtime_error{ "InitializationData failed to send" + send_result.value() }; + } return online_input; } } diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index 00ef3122..589e7158 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -40,7 +40,7 @@ tl::optional> Server::get_client(Uint32 ms_delay, st auto start_time = SDL_GetTicks64(); while (true) { /* try to accept a connection */ - auto client = this->try_get_client(); + auto client = try_get_client(); if (client.has_value()) { return client; } @@ -91,7 +91,7 @@ tl::expected Connection::get_all_data_blocking() { int len = SDLNet_TCP_Recv(m_socket, memory, Connection::chunk_size); if (len <= 0) { free(memory); - return tl::make_unexpected("SDLNet_TCP_Recv: " + std::string{ SDLNet_GetError() }); + return tl::make_unexpected("SDLNet_TCP_Recv: " + network_util::get_latest_sdl_net_error()); } if (len != Connection::chunk_size) { @@ -128,6 +128,7 @@ MaybeData Connection::get_data() { auto data = get_all_data_blocking(); if (!data.has_value()) { + printf("%s\n", data_available.error().c_str()); return tl::make_unexpected("in get_all_data_blocking: " + data_available.error()); } @@ -143,3 +144,35 @@ MaybeData Connection::get_data() { free(raw_bytes.first); return tl::make_optional(result.value()); } + + +tl::expected, std::string> +Connection::wait_for_data(std::size_t abort_after, Uint32 ms_delay) { + auto start_time = SDL_GetTicks64(); + while (true) { + /* try if data is available */ + auto is_data = is_data_available(); + if (is_data) { + auto result = get_data(); + if (!result.has_value()) { + return tl::make_unexpected("In Connection::wait_for_data: " + result.error()); + } + + if (!result.value().has_value()) { + return tl::make_unexpected( + "In Connection::wait_for_data: fatal, even if data is available, there is none??" + ); + } + + return result.value().value(); + } + + auto elapsed_time = SDL_GetTicks64() - start_time; + if (elapsed_time >= abort_after) { + return {}; + } + + SDL_Delay(ms_delay); + continue; + } +} \ No newline at end of file diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index 4fed3f0d..a7c39e4f 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -29,6 +29,9 @@ struct Connection { tl::expected is_data_available(Uint32 timeout_ms = 3); tl::expected get_all_data_blocking(); MaybeData get_data(); + tl::expected, std::string> + wait_for_data(std::size_t abort_after = 60 * 1000, Uint32 ms_delay = 100); + template tl::optional send_data(const T* transportable) { @@ -44,7 +47,7 @@ struct Connection { if ((std::size_t) result != length) { std::free(message); - std::string error = "SDLNet_TCP_Send: " + std::string{ SDLNet_GetError() }; + std::string error = "SDLNet_TCP_Send: " + network_util::get_latest_sdl_net_error(); return tl::make_optional(error); } diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index 48c1cf01..c0b41d83 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -1,6 +1,7 @@ #include "network_manager.hpp" #include "network_transportable.hpp" +#include "network_util.hpp" #include #include #include @@ -17,13 +18,13 @@ MaybeConnection NetworkManager::try_connect(const char* host, Uint16 port) { TCPsocket tcpsock; if (SDLNet_ResolveHost(&ip, host, port) == -1) { - std::string error = "SDLNet_ResolveHost: " + std::string{ SDLNet_GetError() }; + std::string error = "SDLNet_ResolveHost: " + network_util::get_latest_sdl_net_error(); return tl::make_unexpected(error); } tcpsock = SDLNet_TCP_Open(&ip); if (!tcpsock) { - std::string error = "SDLNet_TCP_Open: " + std::string{ SDLNet_GetError() }; + std::string error = "SDLNet_TCP_Open: " + network_util::get_latest_sdl_net_error(); return tl::make_unexpected(error); } @@ -35,12 +36,12 @@ MaybeServer NetworkManager::spawn_server(Uint16 port) { TCPsocket server; IPaddress ip; if (SDLNet_ResolveHost(&ip, NULL, port) == -1) { - std::string error = "SDLNet_ResolveHost: " + std::string{ SDLNet_GetError() }; + std::string error = "SDLNet_ResolveHost: " + network_util::get_latest_sdl_net_error(); return tl::make_unexpected(error); } server = SDLNet_TCP_Open(&ip); if (!server) { - std::string error = "SDLNet_TCP_Open: " + std::string{ SDLNet_GetError() }; + std::string error = "SDLNet_TCP_Open: " + network_util::get_latest_sdl_net_error(); return tl::make_unexpected(error); } diff --git a/src/network/network_util.hpp b/src/network/network_util.hpp new file mode 100644 index 00000000..e7c218ef --- /dev/null +++ b/src/network/network_util.hpp @@ -0,0 +1,23 @@ + + +#pragma once + +#include "string" +#include +#include + +namespace network_util { + + + std::string get_latest_sdl_net_error() { + + + const char* sdl_err = SDLNet_GetError(); + const std::string error_message = + sdl_err == nullptr || std::strlen(sdl_err) == 0 ? "Unknown SDL_net error" : std::string{ sdl_err }; + + return error_message; + } + + +} // namespace network_util \ No newline at end of file diff --git a/src/sdl_context.cpp b/src/sdl_context.cpp index d0891531..1dbc257e 100644 --- a/src/sdl_context.cpp +++ b/src/sdl_context.cpp @@ -1,4 +1,5 @@ #include "sdl_context.hpp" +#include "network/network_util.hpp" #include #include #include @@ -14,7 +15,7 @@ SdlContext::SdlContext() { } //TODO: if we don't need the network, this should be disabled if (SDLNet_Init() == -1) { - printf("SDLNet_Init: %s\n", SDLNet_GetError()); + printf("SDLNet_Init: %s\n", network_util::get_latest_sdl_net_error()); exit(2); } } From d0fb28825fa88b3fecda0d850b2826dc42e8a29a Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 02:06:10 +0200 Subject: [PATCH 20/45] added latest_sdl_net_error util --- src/network/connection_manager.cpp | 3 +-- src/network/connection_manager.hpp | 3 ++- src/network/network_manager.cpp | 8 ++++---- src/network/network_transportable.cpp | 2 +- src/network/network_util.hpp | 2 +- src/sdl_context.cpp | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index 589e7158..4c46be70 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -91,7 +91,7 @@ tl::expected Connection::get_all_data_blocking() { int len = SDLNet_TCP_Recv(m_socket, memory, Connection::chunk_size); if (len <= 0) { free(memory); - return tl::make_unexpected("SDLNet_TCP_Recv: " + network_util::get_latest_sdl_net_error()); + return tl::make_unexpected("SDLNet_TCP_Recv: " + network_util::latest_sdl_net_error()); } if (len != Connection::chunk_size) { @@ -128,7 +128,6 @@ MaybeData Connection::get_data() { auto data = get_all_data_blocking(); if (!data.has_value()) { - printf("%s\n", data_available.error().c_str()); return tl::make_unexpected("in get_all_data_blocking: " + data_available.error()); } diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index a7c39e4f..ca07dde9 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -5,6 +5,7 @@ #include "network_transportable.hpp" +#include "network_util.hpp" #include #include #include @@ -47,7 +48,7 @@ struct Connection { if ((std::size_t) result != length) { std::free(message); - std::string error = "SDLNet_TCP_Send: " + network_util::get_latest_sdl_net_error(); + std::string error = "SDLNet_TCP_Send: " + network_util::latest_sdl_net_error(); return tl::make_optional(error); } diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index c0b41d83..16c1a42b 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -18,13 +18,13 @@ MaybeConnection NetworkManager::try_connect(const char* host, Uint16 port) { TCPsocket tcpsock; if (SDLNet_ResolveHost(&ip, host, port) == -1) { - std::string error = "SDLNet_ResolveHost: " + network_util::get_latest_sdl_net_error(); + std::string error = "SDLNet_ResolveHost: " + network_util::latest_sdl_net_error(); return tl::make_unexpected(error); } tcpsock = SDLNet_TCP_Open(&ip); if (!tcpsock) { - std::string error = "SDLNet_TCP_Open: " + network_util::get_latest_sdl_net_error(); + std::string error = "SDLNet_TCP_Open: " + network_util::latest_sdl_net_error(); return tl::make_unexpected(error); } @@ -36,12 +36,12 @@ MaybeServer NetworkManager::spawn_server(Uint16 port) { TCPsocket server; IPaddress ip; if (SDLNet_ResolveHost(&ip, NULL, port) == -1) { - std::string error = "SDLNet_ResolveHost: " + network_util::get_latest_sdl_net_error(); + std::string error = "SDLNet_ResolveHost: " + network_util::latest_sdl_net_error(); return tl::make_unexpected(error); } server = SDLNet_TCP_Open(&ip); if (!server) { - std::string error = "SDLNet_TCP_Open: " + network_util::get_latest_sdl_net_error(); + std::string error = "SDLNet_TCP_Open: " + network_util::latest_sdl_net_error(); return tl::make_unexpected(error); } diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index 8c6d42a2..45d1cd09 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -17,7 +17,7 @@ std::uint32_t Transportable::checksum(RawBytes bytes) { auto [start, length] = bytes; - std::uint32_t table[256]; + std::uint32_t table[256] = {}; crc32::generate_table(table); std::uint32_t CRC = 0; for (std::uint32_t i = 0; i < length; ++i) { diff --git a/src/network/network_util.hpp b/src/network/network_util.hpp index e7c218ef..ed265f7d 100644 --- a/src/network/network_util.hpp +++ b/src/network/network_util.hpp @@ -9,7 +9,7 @@ namespace network_util { - std::string get_latest_sdl_net_error() { + inline std::string latest_sdl_net_error() { const char* sdl_err = SDLNet_GetError(); diff --git a/src/sdl_context.cpp b/src/sdl_context.cpp index 1dbc257e..e804b548 100644 --- a/src/sdl_context.cpp +++ b/src/sdl_context.cpp @@ -15,7 +15,7 @@ SdlContext::SdlContext() { } //TODO: if we don't need the network, this should be disabled if (SDLNet_Init() == -1) { - printf("SDLNet_Init: %s\n", network_util::get_latest_sdl_net_error()); + printf("SDLNet_Init: %s\n", network_util::latest_sdl_net_error().c_str()); exit(2); } } From 5fbda192472e60dcba63ca3eba761566d55eed46 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 02:18:24 +0200 Subject: [PATCH 21/45] fixed crash, where iI used the wrong variable --- src/local_multiplayer.cpp | 6 ++++-- src/network/connection_manager.cpp | 2 +- src/network/network_data.hpp | 3 +-- src/network/network_transportable.hpp | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index 1c189dda..047da0e9 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -83,9 +83,11 @@ std::unique_ptr LocalMultiplayer::get_input( if (!client.has_value()) { throw std::runtime_error{ "Waited to long for new client" }; } - auto online_input = std::make_unique(associated_game_manager, client.value()); - auto send_initializer = client.value()->wait_for_data(10 * 1000, 100); + const auto connection = client.value(); + auto online_input = std::make_unique(associated_game_manager, connection); + + auto send_initializer = connection->wait_for_data(10 * 1000, 100); if (!send_initializer.has_value()) { throw std::runtime_error{ "client didn't send InitializationData in time: " + send_initializer.error() }; diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index 4c46be70..664f9336 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -128,7 +128,7 @@ MaybeData Connection::get_data() { auto data = get_all_data_blocking(); if (!data.has_value()) { - return tl::make_unexpected("in get_all_data_blocking: " + data_available.error()); + return tl::make_unexpected("in get_all_data_blocking: " + data.error()); } diff --git a/src/network/network_data.hpp b/src/network/network_data.hpp index 007cca80..47bc4f1a 100644 --- a/src/network/network_data.hpp +++ b/src/network/network_data.hpp @@ -9,13 +9,12 @@ enum class InitializationDataType : uint8_t { Client, Server }; struct InitializationData : public Transportable { - static constexpr std::uint32_t serialUUID = 1; - private: InitializationDataType m_type; std::uint32_t m_uuid; public: + static constexpr std::uint32_t serialUUID = 1; explicit InitializationData(InitializationDataType type, std::uint32_t uuid); }; diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp index eca01b52..b5cc88e4 100644 --- a/src/network/network_transportable.hpp +++ b/src/network/network_transportable.hpp @@ -21,10 +21,10 @@ using RawBytes = std::pair; static constexpr std::uint32_t protocol_version = 1; static constexpr std::uint32_t checksum_size = sizeof(std::uint32_t); static constexpr std::uint32_t header_size = sizeof(std::uint32_t) + sizeof(std::uint32_t) + sizeof(std::uint32_t); - // every class has to have such a serialUUID in some way //TODO enforce this in some compile time way, that they are unique! /* virtual */ static constexpr std::uint32_t serialUUID = 0; + //TODO fix inconsistency and don't raise exceptions, rather return tl:expected template static RawBytes serialize(const T* transportable) { From d89ef53c77c9f27a34bedd5a1f4a46b9ba2ddc7a Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 05:07:19 +0200 Subject: [PATCH 22/45] added better initialization (WIP), untested --- src/game_manager.cpp | 59 ++++-- src/game_manager.hpp | 7 +- src/local_multiplayer.cpp | 261 +++++++++++++++++++++----- src/local_multiplayer.hpp | 42 ++++- src/network/connection_manager.cpp | 8 +- src/network/connection_manager.hpp | 13 +- src/network/network_data.cpp | 6 +- src/network/network_data.hpp | 14 +- src/network/network_transportable.cpp | 2 - src/network/online_handler.cpp | 20 +- src/network/online_handler.hpp | 10 +- src/play_manager.cpp | 14 +- src/play_manager.hpp | 20 +- src/tetris_application.hpp | 14 +- 14 files changed, 381 insertions(+), 109 deletions(-) diff --git a/src/game_manager.cpp b/src/game_manager.cpp index 3ffa05d2..cdd95d55 100644 --- a/src/game_manager.cpp +++ b/src/game_manager.cpp @@ -13,18 +13,29 @@ GameManager::GameManager(std::size_t uuid) m_fonts.push_back(std::make_shared("assets/fonts/PressStart2P.ttf", 18)); - m_score_text = Text{ - Point{ m_grid.to_screen_coords(Grid::preview_tetromino_position + Point{ 0, Grid::preview_extends.y }) }, - Color::white(), "score: 0", m_fonts.front() - }; - m_level_text = Text{ - Point{ m_grid.to_screen_coords(Grid::preview_tetromino_position + Point{ 0, Grid::preview_extends.y + 1 }) }, - Color::white(), "level: 0", m_fonts.front() - }; - m_cleared_lines_text = Text{ - Point{ m_grid.to_screen_coords(Grid::preview_tetromino_position + Point{ 0, Grid::preview_extends.y + 2 }) }, - Color::white(), "lines: 0", m_fonts.front() - }; + m_text_rows = std::vector{}; + m_text_rows.emplace_back( + Point{ m_grid.to_screen_coords(Grid::preview_tetromino_position + Point{ 0, Grid::preview_extends.y }) }, + Color::white(), "score: 0", m_fonts.front() + ); + m_text_rows.emplace_back( + Point{ m_grid.to_screen_coords( + Grid::preview_tetromino_position + Point{ 0, Grid::preview_extends.y + 1 } + ) }, + Color::white(), "level: 0", m_fonts.front() + ); + m_text_rows.emplace_back( + Point{ m_grid.to_screen_coords( + Grid::preview_tetromino_position + Point{ 0, Grid::preview_extends.y + 2 } + ) }, + Color::white(), "lines: 0", m_fonts.front() + ); + m_text_rows.emplace_back( + Point{ m_grid.to_screen_coords( + Grid::preview_tetromino_position + Point{ 0, Grid::preview_extends.y + 3 } + ) }, + Color::white(), "player: 0", m_fonts.front() + ); } void GameManager::update() { @@ -57,9 +68,10 @@ void GameManager::render(const Application& app) const { if (m_preview_tetromino) { m_preview_tetromino->render(app, m_grid); } - m_score_text.render(app); - m_level_text.render(app); - m_cleared_lines_text.render(app); + + for (const auto& text : m_text_rows) { + text.render(app); + } } bool GameManager::handle_input_event(Input::Event event) { @@ -199,18 +211,29 @@ void GameManager::set_online_handler(std::unique_ptr online_handl m_online_handler = std::move(online_handler); } +void GameManager::set_player_num(std::size_t player_num) { + m_player_num = player_num; +} + void GameManager::refresh_texts() { + auto i = 0; std::stringstream stream; stream << "score: " << m_score; - m_score_text.set_text(stream.str()); + m_text_rows.at(i++).set_text(stream.str()); stream = {}; stream << "level: " << m_level; - m_level_text.set_text(stream.str()); + m_text_rows.at(i++).set_text(stream.str()); stream = {}; stream << "lines: " << m_lines_cleared; - m_cleared_lines_text.set_text(stream.str()); + m_text_rows.at(i++).set_text(stream.str()); + + if (m_player_num.has_value()) { + stream = {}; + stream << "player " << m_player_num.value(); + m_text_rows.at(i++).set_text(stream.str()); + } } void GameManager::clear_fully_occupied_lines() { diff --git a/src/game_manager.hpp b/src/game_manager.hpp index 44208565..9a41fbed 100644 --- a/src/game_manager.hpp +++ b/src/game_manager.hpp @@ -10,6 +10,7 @@ #include #include #include +#include struct Application; @@ -44,13 +45,12 @@ struct GameManager final { std::array m_sequence_bags{ Bag{}, Bag{} }; int m_sequence_index = 0; std::vector> m_fonts; - Text m_score_text; + std::vector m_text_rows; int m_score = 0; - Text m_level_text; - Text m_cleared_lines_text; bool m_down_key_pressed = false; bool m_is_accelerated_down_movement = false; std::unique_ptr m_online_handler = nullptr; + tl::optional m_player_num = tl::nullopt; public: GameManager(std::size_t uuid); @@ -67,6 +67,7 @@ struct GameManager final { bool move_tetromino_right(); bool drop_tetromino(); void set_online_handler(std::unique_ptr online_handler); + void set_player_num(std::size_t player_num); private: void refresh_texts(); diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index 047da0e9..6b1138c5 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -9,21 +9,21 @@ #include #include #include +#include +#include LocalMultiplayer::LocalMultiplayer(std::size_t num_players, bool is_server) : PlayManager{}, m_num_players{ num_players }, m_is_server{ is_server }, m_network_manager{ NetworkManager{} }, - m_server{ nullptr } {}; + m_server{ nullptr }, + m_input_connections{ ConnectionStore{} } {}; -std::size_t LocalMultiplayer::get_num_players() { - return m_num_players; -}; -tl::optional LocalMultiplayer::init() { +tl::expected LocalMultiplayer::init() { if (m_num_players > 4 || m_num_players < 2) { - return tl::make_optional( + return tl::make_unexpected( "The LocalMultiplayer mode only allows between 2 and 4 players, but got: " + std::to_string(m_num_players) ); @@ -32,17 +32,184 @@ tl::optional LocalMultiplayer::init() { if (m_is_server) { auto server = m_network_manager.spawn_server(); if (!server.has_value()) { - return tl::make_optional("Error in initializing the server: " + server.error()); + return tl::make_unexpected("Error in initializing the server: " + server.error()); } m_server = server.value(); + m_input_connections.reserve(m_num_players - 1); + + //TODO in here refactor some routines to separate functions! + for (std::size_t i = 0; i < m_num_players - 1; ++i) { + + auto client = m_server->get_client(); + if (!client.has_value()) { + throw std::runtime_error{ "Waited to long for new client nr. " + std::to_string(i) }; + } + + const auto connection = client.value(); + + + auto send_initializer = connection->wait_for_data(10 * 1000, 100); + if (!send_initializer.has_value()) { + throw std::runtime_error{ "client nr. " + std::to_string(i) + + " didn't send InitializationData in time: " + send_initializer.error() }; + } + + const auto send_vector = send_initializer.value(); + if (send_vector.size() != 1) { + throw std::runtime_error{ + "client nr. "+ std::to_string(i)+" did send InitializationData in time, but also to much data afterwards, it can't be handled " + "here!" + }; + } + + const auto received_value = send_vector.at(0); + + if (!received_value.is_of_type()) { + throw std::runtime_error{ "client nr. " + std::to_string(i) + + " didn't send InitializationData but another serializable message!" }; + } + + const auto initializer_message = received_value.as_type(); + + if (initializer_message->m_type == InitializationDataType::Send) { + + //TODO assert + // initializer_message->m_uuid == 0; + + //this is correct fro online_input, set this client connection as player index, query conenction for info like starting piece and eventual other information + + //check if client game version is compatible with this game, otherwise reject (with a message to teh client and to the server host!) + + // send the information about how many players partecipate in response, await the creation of x-1 sockets to send them the event from them, not that the client doesn't have a static player number, only the server can (later select this from a menu) set this size + + + auto send_data = ClientInitializationData{ (std::uint32_t) m_num_players, (std::uint32_t) i + 1 }; + const auto send_result = connection->send_data(&send_data); + if (send_result.has_value()) { + throw std::runtime_error{ "ClientInitializationData failed to send" + send_result.value() }; + } + + + auto receive_connections = std::vector>>{}; + receive_connections.reserve(m_num_players - 1); + + for (std::size_t j = 0; j < m_num_players - 1; ++j) { + + auto receive_client = m_server->get_client(); + if (!receive_client.has_value()) { + throw std::runtime_error{ "Waited to long for new receive client nr. " + std::to_string(j) }; + } + + const auto receive_connection = receive_client.value(); + + + auto receive_send_initializer = receive_connection->wait_for_data(10 * 1000, 100); + if (!receive_send_initializer.has_value()) { + throw std::runtime_error{ "receive client nr. " + std::to_string(j) + + " didn't send InitializationData in time: " + + receive_send_initializer.error() }; + } + + const auto receive_send_vector = receive_send_initializer.value(); + if (send_vector.size() != 1) { + throw std::runtime_error{ + "receive client nr. "+ std::to_string(j)+" did send InitializationData in time, but also to much data afterwards, it can't be handled " + "here!" + }; + } + + const auto receive_received_value = receive_send_vector.at(0); + + if (!receive_received_value.is_of_type()) { + throw std::runtime_error{ + "receive client nr. " + std::to_string(j) + + " didn't send InitializationData but another serializable message!" + }; + } + + const auto receive_initializer_message = receive_received_value.as_type(); + + if (receive_initializer_message->m_type == InitializationDataType::Receive) { + + receive_connections.emplace_back(receive_initializer_message->m_uuid, receive_connection); + + } else { + throw std::runtime_error{ + "receive client didn't send InitializationDataType::Receive as first message!" + }; + } + } + + + m_input_connections.emplace_back( + std::pair>{ 0, connection }, receive_connections + ); + } else { + throw std::runtime_error{ "client didn't send InitializationDataType::Send as first message!" }; + } + } + + + } else { + + + // client start here + + auto connection_result = get_connection_to_server(); + + if (!connection_result.has_value()) { + throw std::runtime_error{ connection_result.error() }; + } + + auto connection = connection_result.value(); + auto send_data = InitializationData{ InitializationDataType::Send, (std::uint32_t) 0 }; + const auto send_result = connection->send_data(&send_data); + if (send_result.has_value()) { + throw std::runtime_error{ "InitializationData failed to send" + send_result.value() }; + } + + const auto data = await_exact_one(connection); + + auto receive_connections = std::vector>>{}; + receive_connections.reserve(m_num_players - 1); + + + const auto my_player_id = data->your_player_id; + + for (std::uint32_t i = 0; i < data->player_num; ++i) { + if (i == my_player_id) { + continue; + } + + auto receive_connection_result = get_connection_to_server(); + + if (!receive_connection_result.has_value()) { + throw std::runtime_error{ receive_connection_result.error() }; + } + + auto receive_connection = receive_connection_result.value(); + auto receive_send_data = InitializationData{ InitializationDataType::Receive, (std::uint32_t) i + 1 }; + const auto receive_send_result = connection->send_data(&receive_send_data); + if (receive_send_result.has_value()) { + throw std::runtime_error{ "InitializationData failed to send" + receive_send_result.value() }; + } + + receive_connections.emplace_back(i, receive_connection); + } + + m_input_connections.emplace_back( + std::pair>{ (std ::size_t) my_player_id, connection }, + receive_connections + ); } + return {}; } -std::unique_ptr LocalMultiplayer::get_input( +std::pair> LocalMultiplayer::get_input( std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher @@ -54,63 +221,71 @@ std::unique_ptr LocalMultiplayer::get_input( if (index == 0) { if (m_is_server) { - associated_game_manager->set_online_handler(std::make_unique(m_server, nullptr, 0)); + constexpr std::size_t my_id = 0; + auto send_to = std::vector>{}; + for (const auto& client_bundle : m_input_connections) { + + for (const auto& [num, receive_connection] : client_bundle.second) { + if (my_id == num) { + send_to.push_back(receive_connection); + } + } + } + + associated_game_manager->set_online_handler(std::make_unique(m_server, nullptr, send_to)); auto keyboard_input = std::make_unique(associated_game_manager); event_dispatcher->register_listener(keyboard_input.get()); - return keyboard_input; + return std::pair>{ 0, std::move(keyboard_input) }; } else { - auto connection_result = get_connection_to_server(); - if (!connection_result.has_value()) { - throw std::runtime_error{ connection_result.error() }; - } - - auto connection = connection_result.value(); + //TODO assert + // m_input_connections.size() == 1 - associated_game_manager->set_online_handler(std::make_unique(nullptr, connection, 0)); + associated_game_manager->set_online_handler( + std::make_unique(nullptr, m_input_connections.at(0).first.second) + ); auto keyboard_input = std::make_unique(associated_game_manager); event_dispatcher->register_listener(keyboard_input.get()); - return keyboard_input; + return std::pair>{ m_input_connections.at(0).first.first, + std::move(keyboard_input) }; } } else { if (m_is_server) { - auto client = m_server->get_client(); - if (!client.has_value()) { - throw std::runtime_error{ "Waited to long for new client" }; - } - - const auto connection = client.value(); - auto online_input = std::make_unique(associated_game_manager, connection); - auto send_initializer = connection->wait_for_data(10 * 1000, 100); - if (!send_initializer.has_value()) { - throw std::runtime_error{ "client didn't send InitializationData in time: " - + send_initializer.error() }; - } - - - return online_input; + auto online_input = std::make_unique( + associated_game_manager, m_input_connections.at(index - 1).first.second + ); + return std::pair>{ index, std::move(online_input) }; } else { - auto connection_result = get_connection_to_server(); + // assert m_input_connections.size() == 1 - if (!connection_result.has_value()) { - throw std::runtime_error{ connection_result.error() }; + std::shared_ptr connection = nullptr; + std::size_t player_num = 0; + const auto my_id = m_input_connections.at(0).first.first; + for (auto [num, receive_connection] : m_input_connections.at(0).second) { + const auto actual_index = index <= my_id ? index - 1 : index; + if (actual_index == num) { + connection = receive_connection; + player_num = num; + break; + } } - auto connection = connection_result.value(); - auto online_input = std::make_unique(associated_game_manager, connection); - auto send_data = InitializationData{ InitializationDataType::Client, (std::uint32_t) index }; - const auto send_result = connection->send_data(&send_data); - if (send_result.has_value()) { - throw std::runtime_error{ "InitializationData failed to send" + send_result.value() }; + if (!connection) { + throw std::runtime_error{ "fatal initialization error, no connection for index " + std::to_string(index) + + " found!" }; } - return online_input; + + + auto online_input = std::make_unique(associated_game_manager, connection); + + return std::pair>{ player_num, std::move(online_input) }; } } } diff --git a/src/local_multiplayer.hpp b/src/local_multiplayer.hpp index f8e85415..77f884e1 100644 --- a/src/local_multiplayer.hpp +++ b/src/local_multiplayer.hpp @@ -2,13 +2,21 @@ #pragma once +#include "network/network_data.hpp" #include "network/network_manager.hpp" #include "play_manager.hpp" #include #include +#include #include #include #include +#include +#include + +using ConnectionStore = std::vector>, + std::vector>>>>; struct LocalMultiplayer : public PlayManager { private: @@ -16,14 +24,42 @@ struct LocalMultiplayer : public PlayManager { bool m_is_server; NetworkManager m_network_manager; std::shared_ptr m_server; + ConnectionStore m_input_connections; tl::expected, std::string> get_connection_to_server(std::uint32_t delay_between_attempts = 200, std::uint32_t connection_attempts = 10); public: explicit LocalMultiplayer(std::size_t num_players, bool is_server); - std::size_t get_num_players() override; - tl::optional init() override; - std::unique_ptr + tl::expected init() override; + std::pair> get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher) override; }; + + +// little fast helper, +//TODO refactor better and move in some reasonable cpp, and don't use exceptions to handle this +template +std::shared_ptr await_exact_one(std::shared_ptr connection) { + //TODO make this number customizable + auto send_initializer = connection->wait_for_data(10 * 1000, 100); + if (!send_initializer.has_value()) { + throw std::runtime_error{ "didn't receive data in time: " + send_initializer.error() }; + } + + const auto send_vector = send_initializer.value(); + if (send_vector.size() != 1) { + throw std::runtime_error{ + "did receive data in time, but also to much data afterwards, it can't be handled " + "here!" + }; + } + + const auto received_value = send_vector.at(0); + + if (!received_value.is_of_type()) { + throw std::runtime_error{ "didn't receive correct data but another serializable message!" }; + } + + return received_value.as_type(); +} \ No newline at end of file diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index 664f9336..bf584828 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -66,17 +66,18 @@ tl::expected Connection::is_data_available(Uint32 timeout_ms) auto num_sockets = SDLNet_TCP_AddSocket(set, m_socket); if (num_sockets != 1) { + SDLNet_FreeSocketSet(set); return tl::make_unexpected("SDLNet_AddSocket failed, this is an implementation error"); } auto result = SDLNet_CheckSockets(set, timeout_ms); if (result == -1) { + SDLNet_FreeSocketSet(set); return tl::make_unexpected("SDLNet_CheckSockets error (select() system call error)"); } SDLNet_FreeSocketSet(set); - return result == 1; } @@ -158,9 +159,8 @@ Connection::wait_for_data(std::size_t abort_after, Uint32 ms_delay) { } if (!result.value().has_value()) { - return tl::make_unexpected( - "In Connection::wait_for_data: fatal, even if data is available, there is none??" - ); + SDL_Delay(ms_delay); + continue; } return result.value().value(); diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index ca07dde9..97ac733a 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -72,11 +72,10 @@ struct Server { tl::optional> get_client(Uint32 ms_delay = 100, std::size_t abort_after = 60 * 1000); template - tl::optional send_all(const T* transportable) { - + tl::optional send_all(const T* transportable, std::vector> send_to) { - for (std::size_t i = 0; i < m_connections.size(); ++i) { - auto result = m_connections.at(i)->send_data(transportable); + for (std::size_t i = 0; i < send_to.size(); ++i) { + auto result = send_to.at(i)->send_data(transportable); if (result.has_value()) { return tl::make_optional( "Error while sending to client: " + std::to_string(i) + " : " + result.value() @@ -86,4 +85,10 @@ struct Server { return {}; } + + template + tl::optional send_all(const T* transportable) { + + return send_all(transportable, m_connections); + } }; diff --git a/src/network/network_data.cpp b/src/network/network_data.cpp index 7a578e32..c5522737 100644 --- a/src/network/network_data.cpp +++ b/src/network/network_data.cpp @@ -14,4 +14,8 @@ EventData::EventData(Input::Event event) : m_event{ event } {}; Input::Event EventData::event() const { return m_event; -} \ No newline at end of file +} + +ClientInitializationData::ClientInitializationData(std::uint32_t player_num, std::uint32_t your_player_id) + : player_num{ player_num }, + your_player_id{ your_player_id } {}; \ No newline at end of file diff --git a/src/network/network_data.hpp b/src/network/network_data.hpp index 47bc4f1a..f6365a80 100644 --- a/src/network/network_data.hpp +++ b/src/network/network_data.hpp @@ -6,14 +6,13 @@ #include "network_transportable.hpp" #include -enum class InitializationDataType : uint8_t { Client, Server }; +enum class InitializationDataType : uint8_t { Receive, Send }; struct InitializationData : public Transportable { -private: +public: InitializationDataType m_type; std::uint32_t m_uuid; -public: static constexpr std::uint32_t serialUUID = 1; explicit InitializationData(InitializationDataType type, std::uint32_t uuid); }; @@ -26,4 +25,13 @@ struct EventData : public Transportable { static constexpr std::uint32_t serialUUID = 2; explicit EventData(Input::Event event); Input::Event event() const; +}; + +struct ClientInitializationData : public Transportable { +public: + std::uint32_t player_num; + std::uint32_t your_player_id; + + static constexpr std::uint32_t serialUUID = 3; + explicit ClientInitializationData(std::uint32_t player_num, std::uint32_t your_player_id); }; \ No newline at end of file diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index 45d1cd09..6936cb76 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -112,8 +112,6 @@ tl::expected, std::string> RawTransportData::from_ advance(data_size + Transportable::checksum_size); result.emplace_back(uuid, data, data_size); - - std::cout << "data was: serialUUID: " << uuid << " data_size: " << data_size << "\n"; } return result; diff --git a/src/network/online_handler.cpp b/src/network/online_handler.cpp index f3e059e9..76741b82 100644 --- a/src/network/online_handler.cpp +++ b/src/network/online_handler.cpp @@ -7,22 +7,28 @@ #include #include -OnlineHandler::OnlineHandler(std::shared_ptr server, std::shared_ptr connection, std::uint32_t uuid) +OnlineHandler::OnlineHandler(std::shared_ptr server, std::shared_ptr connection) : m_server{ server }, m_connection{ connection }, - m_uuid{ uuid } {}; + m_send_to{ std::vector>{} } {}; + + +OnlineHandler::OnlineHandler( + std::shared_ptr server, + std::shared_ptr connection, + std::vector> send_to +) + : m_server{ server }, + m_connection{ connection }, + m_send_to{ send_to } {}; void OnlineHandler::handle_event(Input::Event event) { if (m_server) { auto event_data = EventData{ event }; //TODO handle error - m_server->send_all(&event_data); - - /* m_server.send_all_if(EventData{ event }, []() { - + m_server->send_all(&event_data, m_send_to); - }); */ } else if (m_connection) { auto event_data = EventData{ event }; //TODO handle error diff --git a/src/network/online_handler.hpp b/src/network/online_handler.hpp index d46679dc..eefa1cff 100644 --- a/src/network/online_handler.hpp +++ b/src/network/online_handler.hpp @@ -4,14 +4,20 @@ #include "../input.hpp" #include "connection_manager.hpp" #include +#include struct OnlineHandler { private: std::shared_ptr m_server; std::shared_ptr m_connection; - std::uint32_t m_uuid; + std::vector> m_send_to; public: - OnlineHandler(std::shared_ptr server, std::shared_ptr connection, std::uint32_t uuid); + OnlineHandler(std::shared_ptr server, std::shared_ptr connection); + OnlineHandler( + std::shared_ptr server, + std::shared_ptr connection, + std::vector> send_to + ); void handle_event(Input::Event event); }; \ No newline at end of file diff --git a/src/play_manager.cpp b/src/play_manager.cpp index b4c3ea3b..912d06e7 100644 --- a/src/play_manager.cpp +++ b/src/play_manager.cpp @@ -3,21 +3,21 @@ #include "play_manager.hpp" #include "game_manager.hpp" #include +#include #include +#include +#include PlayManager::PlayManager(){}; SinglePlayer::SinglePlayer() : PlayManager{} {}; -std::size_t SinglePlayer::get_num_players() { - return 1; -}; -tl::optional SinglePlayer::init() { - return {}; +tl::expected SinglePlayer::init() { + return StartState{ 1 }; } -std::unique_ptr +std::pair> SinglePlayer::get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher) { if (index != 0) { @@ -26,5 +26,5 @@ SinglePlayer::get_input(std::size_t index, GameManager* associated_game_manager, auto keyboard_input = std::make_unique(associated_game_manager); event_dispatcher->register_listener(keyboard_input.get()); - return keyboard_input; + return std::pair>{ 0, std::move(keyboard_input) }; } diff --git a/src/play_manager.hpp b/src/play_manager.hpp index 741d0075..c7162f44 100644 --- a/src/play_manager.hpp +++ b/src/play_manager.hpp @@ -6,16 +6,23 @@ #include #include #include -#include +#include +#include +#include + +struct StartState { + std::size_t num_players; + // std::vector state; + //TODO +}; /* abstract */ struct PlayManager { public: explicit PlayManager(); virtual ~PlayManager() = default; - virtual std::size_t get_num_players() = 0; - virtual tl::optional init() = 0; - virtual std::unique_ptr + virtual tl::expected init() = 0; + virtual std::pair> get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher) = 0; }; @@ -23,8 +30,7 @@ struct SinglePlayer : public PlayManager { public: explicit SinglePlayer(); - std::size_t get_num_players() override; - tl::optional init() override; - std::unique_ptr + tl::expected init() override; + std::pair> get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher) override; }; diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index dc619485..9d8b980a 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -31,17 +31,21 @@ struct TetrisApplication : public Application { m_manager{ std::move(manager) } { auto is_valid = m_manager->init(); - if (is_valid.has_value()) { - std::cerr << "Error in initializing PlayManager: " << is_valid.value() << "\n"; + if (!is_valid.has_value()) { + std::cerr << "Error in initializing PlayManager: " << is_valid.error() << "\n"; std::exit(2); } - auto num_players = m_manager->get_num_players(); + auto start_state = is_valid.value(); + const auto num_players = start_state.num_players; for (std::size_t i = 0; i < num_players; ++i) { + //TODO get the start state from the client: + // m_game_managers.push_back(std::make_unique(i, start_state.state.at(i))); m_game_managers.push_back(std::make_unique(i)); - std::cout << "initializing manager input at " << i << " (online atm)\n"; - m_inputs.push_back(m_manager->get_input(i, m_game_managers.back().get(), &m_event_dispatcher)); + auto input_pair = m_manager->get_input(i, m_game_managers.back().get(), &m_event_dispatcher); + m_game_managers.back().get()->set_player_num(input_pair.first); + m_inputs.push_back(std::move(input_pair.second)); } for (const auto& game_manager : m_game_managers) { game_manager->spawn_next_tetromino(); From 32eacbdc3173bbce413e4f83d5298da6efdd5a8a Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 13:19:54 +0200 Subject: [PATCH 23/45] removed unnecessary debug printing --- src/bag.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/bag.cpp b/src/bag.cpp index bc8ecdc6..561b1f53 100644 --- a/src/bag.cpp +++ b/src/bag.cpp @@ -28,9 +28,6 @@ Bag::Bag() : m_tetromino_sequence{} { break; } } - if (type_is_okay) { - std::cerr << static_cast(m_tetromino_sequence[i]) << " "; - } } while (!type_is_okay); } std::cerr << "\n"; From 1197a8e4265fcbd3d2abd53f87055bc35f3702b7 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 13:22:25 +0200 Subject: [PATCH 24/45] fixed remaining bugs: - fixed return value errors (dont use {}, use tl::nullopt, if you want to use a null optional!) fixed some receive - send errors --- src/local_multiplayer.cpp | 30 ++++++++++++++++-------------- src/network/connection_manager.cpp | 17 ++++++++++------- src/network/connection_manager.hpp | 4 ++-- src/network/network_data.hpp | 1 + src/play_manager.hpp | 2 +- src/tetris_application.hpp | 1 - 6 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index 6b1138c5..d24916b5 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -22,14 +22,16 @@ LocalMultiplayer::LocalMultiplayer(std::size_t num_players, bool is_server) tl::expected LocalMultiplayer::init() { - if (m_num_players > 4 || m_num_players < 2) { - return tl::make_unexpected( - "The LocalMultiplayer mode only allows between 2 and 4 players, but got: " - + std::to_string(m_num_players) - ); - } + if (m_is_server) { + + if (m_num_players > 4 || m_num_players < 2) { + return tl::make_unexpected( + "The LocalMultiplayer mode only allows between 2 and 4 players, but got: " + + std::to_string(m_num_players) + ); + } auto server = m_network_manager.spawn_server(); if (!server.has_value()) { return tl::make_unexpected("Error in initializing the server: " + server.error()); @@ -40,7 +42,6 @@ tl::expected LocalMultiplayer::init() { //TODO in here refactor some routines to separate functions! for (std::size_t i = 0; i < m_num_players - 1; ++i) { - auto client = m_server->get_client(); if (!client.has_value()) { throw std::runtime_error{ "Waited to long for new client nr. " + std::to_string(i) }; @@ -84,6 +85,8 @@ tl::expected LocalMultiplayer::init() { // send the information about how many players partecipate in response, await the creation of x-1 sockets to send them the event from them, not that the client doesn't have a static player number, only the server can (later select this from a menu) set this size + // TODO add seed here, the server generates one seed + auto send_data = ClientInitializationData{ (std::uint32_t) m_num_players, (std::uint32_t) i + 1 }; const auto send_result = connection->send_data(&send_data); if (send_result.has_value()) { @@ -95,7 +98,6 @@ tl::expected LocalMultiplayer::init() { receive_connections.reserve(m_num_players - 1); for (std::size_t j = 0; j < m_num_players - 1; ++j) { - auto receive_client = m_server->get_client(); if (!receive_client.has_value()) { throw std::runtime_error{ "Waited to long for new receive client nr. " + std::to_string(j) }; @@ -151,9 +153,9 @@ tl::expected LocalMultiplayer::init() { } - } else { - + return StartState{ m_num_players }; + } else { // client start here auto connection_result = get_connection_to_server(); @@ -189,8 +191,8 @@ tl::expected LocalMultiplayer::init() { } auto receive_connection = receive_connection_result.value(); - auto receive_send_data = InitializationData{ InitializationDataType::Receive, (std::uint32_t) i + 1 }; - const auto receive_send_result = connection->send_data(&receive_send_data); + auto receive_send_data = InitializationData{ InitializationDataType::Receive, (std::uint32_t) i }; + const auto receive_send_result = receive_connection->send_data(&receive_send_data); if (receive_send_result.has_value()) { throw std::runtime_error{ "InitializationData failed to send" + receive_send_result.value() }; } @@ -202,10 +204,10 @@ tl::expected LocalMultiplayer::init() { std::pair>{ (std ::size_t) my_player_id, connection }, receive_connections ); - } - return {}; + return StartState{ data->player_num }; + } } diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index bf584828..ba418091 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -33,7 +33,7 @@ tl::optional> Server::try_get_client() { m_connections.push_back(connection); return connection; } - return {}; + return tl::nullopt; } tl::optional> Server::get_client(Uint32 ms_delay, std::size_t abort_after) { @@ -47,7 +47,7 @@ tl::optional> Server::get_client(Uint32 ms_delay, st auto elapsed_time = SDL_GetTicks64() - start_time; if (elapsed_time >= abort_after) { - return {}; + return tl::nullopt; } SDL_Delay(ms_delay); @@ -124,7 +124,7 @@ MaybeData Connection::get_data() { } if (!data_available.value()) { - return {}; + return tl::nullopt; } auto data = get_all_data_blocking(); @@ -152,15 +152,18 @@ Connection::wait_for_data(std::size_t abort_after, Uint32 ms_delay) { while (true) { /* try if data is available */ auto is_data = is_data_available(); - if (is_data) { + if (!is_data.has_value()) { + return tl::make_unexpected("In Connection::wait_for_data: " + is_data.error()); + } + + if (is_data.value()) { auto result = get_data(); if (!result.has_value()) { return tl::make_unexpected("In Connection::wait_for_data: " + result.error()); } if (!result.value().has_value()) { - SDL_Delay(ms_delay); - continue; + return tl::make_unexpected("In Connection::wait_for_data: " + result.error()); } return result.value().value(); @@ -168,7 +171,7 @@ Connection::wait_for_data(std::size_t abort_after, Uint32 ms_delay) { auto elapsed_time = SDL_GetTicks64() - start_time; if (elapsed_time >= abort_after) { - return {}; + return tl::make_unexpected("In Connection::wait_for_data: took to long"); } SDL_Delay(ms_delay); diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index 97ac733a..c56ced3a 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -54,7 +54,7 @@ struct Connection { std::free(message); - return {}; + return tl::nullopt; } }; @@ -83,7 +83,7 @@ struct Server { } } - return {}; + return tl::nullopt; } template diff --git a/src/network/network_data.hpp b/src/network/network_data.hpp index f6365a80..1df79752 100644 --- a/src/network/network_data.hpp +++ b/src/network/network_data.hpp @@ -31,6 +31,7 @@ struct ClientInitializationData : public Transportable { public: std::uint32_t player_num; std::uint32_t your_player_id; + //TODO add seed here static constexpr std::uint32_t serialUUID = 3; explicit ClientInitializationData(std::uint32_t player_num, std::uint32_t your_player_id); diff --git a/src/play_manager.hpp b/src/play_manager.hpp index c7162f44..951b67e9 100644 --- a/src/play_manager.hpp +++ b/src/play_manager.hpp @@ -13,7 +13,7 @@ struct StartState { std::size_t num_players; // std::vector state; - //TODO + //TODO add seed here }; diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index 9d8b980a..d3e8b1b8 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -38,7 +38,6 @@ struct TetrisApplication : public Application { auto start_state = is_valid.value(); const auto num_players = start_state.num_players; - for (std::size_t i = 0; i < num_players; ++i) { //TODO get the start state from the client: // m_game_managers.push_back(std::make_unique(i, start_state.state.at(i))); From e5cd7e0e5a2a1b827928a958ceb108f0c602519a Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 14:22:11 +0200 Subject: [PATCH 25/45] fixed todo: removed assert and replace it with throw --- src/network/network_transportable.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index 6936cb76..4cd4b5d2 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -32,8 +32,9 @@ std::uint32_t Transportable::checksum(RawBytes bytes) { void Transportable::write_header(RawBytes bytes, std::uint32_t uuid, std::uint32_t data_size) { auto [start, length] = bytes; - //TODO remove assert - assert(length == Transportable::header_size); + if (length != Transportable::header_size) { + throw std::runtime_error{ "Wrong call of Transportable::write_header : header size mismatch" }; + } std::uint32_t* data_ptr = (std::uint32_t*) start; From 0565a3129da8a9ff92cfa076ef4002ce240d5ccb Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 14:22:24 +0200 Subject: [PATCH 26/45] refined todo --- src/network/network_transportable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index 4cd4b5d2..ddd6e301 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -100,7 +100,7 @@ tl::expected, std::string> RawTransportData::from_ ); } - //TODO check if implemented correctly + //TODO check if implemented correctly (check with valgrind --tool=memcheck --leak-check=full) // this malloc get'S freed in the shared ptr destructor later uint8_t* memory = static_cast(std::malloc(data_size)); if (!memory) { From db2194cab8490a816692539e1150482085bbc0e1 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 14:22:45 +0200 Subject: [PATCH 27/45] fixed todo: - use tl::expected instead of exception --- src/network/connection_manager.hpp | 7 ++++++- src/network/network_transportable.hpp | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index c56ced3a..dac6c6b9 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -38,7 +38,12 @@ struct Connection { tl::optional send_data(const T* transportable) { - auto [message, length] = Transportable::serialize(transportable); + const auto serialized = Transportable::serialize(transportable); + if (!serialized.has_value()) { + return tl::make_optional("In Connection::send_data: " + serialized.error()); + } + + auto [message, length] = serialized.value(); const auto result = SDLNet_TCP_Send(m_socket, message, length); if (result == -1) { diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp index b5cc88e4..19ba4568 100644 --- a/src/network/network_transportable.hpp +++ b/src/network/network_transportable.hpp @@ -27,14 +27,14 @@ using RawBytes = std::pair; //TODO fix inconsistency and don't raise exceptions, rather return tl:expected template - static RawBytes serialize(const T* transportable) { + static tl::expected serialize(const T* transportable) { constexpr std::uint32_t data_size = sizeof(T); const std::uint32_t send_size = Transportable::header_size + data_size + Transportable::checksum_size; uint8_t* memory = (uint8_t*) std::malloc(send_size); if (!memory) { - throw std::runtime_error{ "error in malloc for sending a message" }; + return tl::make_unexpected("error in malloc for sending a message"); } From ba482bf6cad772cce4f59e26c8faeaeef419ef3e Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 14:26:58 +0200 Subject: [PATCH 28/45] removed todo: - throw on wrong implementation of LocalMultiplayer::init --- src/local_multiplayer.cpp | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index d24916b5..bc1139d4 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -75,8 +75,10 @@ tl::expected LocalMultiplayer::init() { if (initializer_message->m_type == InitializationDataType::Send) { - //TODO assert - // initializer_message->m_uuid == 0; + if (initializer_message->m_uuid != 0) { + throw std::runtime_error{ "Wrong InitializationData: first connection must have uuid 0 but has " + + std::to_string(initializer_message->m_uuid) }; + } //this is correct fro online_input, set this client connection as player index, query conenction for info like starting piece and eventual other information @@ -243,8 +245,13 @@ std::pair> LocalMultiplayer::get_input( auto connection_result = get_connection_to_server(); - //TODO assert - // m_input_connections.size() == 1 + if (m_input_connections.size() != 1) { + throw std::runtime_error{ + "LocalMultiplayer::init was implemented wrong, client only has one value in m_input_connections, " + "but were: " + + std::to_string(m_input_connections.size()) + }; + } associated_game_manager->set_online_handler( std::make_unique(nullptr, m_input_connections.at(0).first.second) @@ -265,7 +272,13 @@ std::pair> LocalMultiplayer::get_input( return std::pair>{ index, std::move(online_input) }; } else { - // assert m_input_connections.size() == 1 + if (m_input_connections.size() != 1) { + throw std::runtime_error{ + "LocalMultiplayer::init was implemented wrong, client only has one value in m_input_connections, " + "but were: " + + std::to_string(m_input_connections.size()) + }; + } std::shared_ptr connection = nullptr; std::size_t player_num = 0; From b2f49ff66911e8f5c2b8a91804fd4b2a31783a62 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 14:30:17 +0200 Subject: [PATCH 29/45] refined todo --- src/tetris_application.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index d3e8b1b8..fdca0b68 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -50,7 +50,8 @@ struct TetrisApplication : public Application { game_manager->spawn_next_tetromino(); } - //TODO if this is to big to handle num_players, we have to resize in some way + //TODO if the window is to small to handle num_players, we have to resize in some way , + //TODO: if it's to big it has to be resized into an appropiate width [[maybe_unused]] const std::size_t game_field_size = (GameManager::size_per_field * num_players) + ((num_players - 1) * GameManager::space_between); From 30ed3f32bf8bfaa6d04aed0004e12ca66e3dbd38 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Fri, 7 Apr 2023 15:22:27 +0200 Subject: [PATCH 30/45] added GCC warning -Wold-style-cast - fixed teh resulting warnings --- meson.build | 2 +- src/game_manager.cpp | 2 +- src/local_multiplayer.cpp | 11 +++++++---- src/network/connection_manager.hpp | 2 +- src/network/network_transportable.cpp | 12 ++++++------ src/network/network_transportable.hpp | 2 +- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/meson.build b/meson.build index 6a1e4fe8..01252813 100644 --- a/meson.build +++ b/meson.build @@ -14,7 +14,7 @@ cpp = meson.get_compiler('cpp') if cpp.get_id() == 'msvc' add_project_arguments('/std:c++latest', language: 'cpp') elif cpp.get_id() == 'gcc' - add_project_arguments('-std=c++23', language: 'cpp') + add_project_arguments('-std=c++23', '-Wold-style-cast', language: 'cpp') else add_project_arguments('-std=c++20', language: 'cpp') endif diff --git a/src/game_manager.cpp b/src/game_manager.cpp index cdd95d55..e7951875 100644 --- a/src/game_manager.cpp +++ b/src/game_manager.cpp @@ -7,7 +7,7 @@ GameManager::GameManager(std::size_t uuid) : m_uuid{ uuid }, m_grid{ Point{ - (int) ((uuid * GameManager::size_per_field) + (uuid * GameManager::space_between)), 0 + static_cast((uuid * GameManager::size_per_field) + (uuid * GameManager::space_between)), 0 }, tile_size }, m_next_gravity_step_time{ Application::elapsed_time() + get_gravity_delay() } { diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index bc1139d4..6648d11d 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -89,7 +89,8 @@ tl::expected LocalMultiplayer::init() { // TODO add seed here, the server generates one seed - auto send_data = ClientInitializationData{ (std::uint32_t) m_num_players, (std::uint32_t) i + 1 }; + auto send_data = ClientInitializationData{ static_cast(m_num_players), + static_cast(i + 1) }; const auto send_result = connection->send_data(&send_data); if (send_result.has_value()) { throw std::runtime_error{ "ClientInitializationData failed to send" + send_result.value() }; @@ -167,7 +168,7 @@ tl::expected LocalMultiplayer::init() { } auto connection = connection_result.value(); - auto send_data = InitializationData{ InitializationDataType::Send, (std::uint32_t) 0 }; + auto send_data = InitializationData{ InitializationDataType::Send, static_cast(0) }; const auto send_result = connection->send_data(&send_data); if (send_result.has_value()) { throw std::runtime_error{ "InitializationData failed to send" + send_result.value() }; @@ -193,7 +194,8 @@ tl::expected LocalMultiplayer::init() { } auto receive_connection = receive_connection_result.value(); - auto receive_send_data = InitializationData{ InitializationDataType::Receive, (std::uint32_t) i }; + auto receive_send_data = + InitializationData{ InitializationDataType::Receive, static_cast(i) }; const auto receive_send_result = receive_connection->send_data(&receive_send_data); if (receive_send_result.has_value()) { throw std::runtime_error{ "InitializationData failed to send" + receive_send_result.value() }; @@ -203,7 +205,8 @@ tl::expected LocalMultiplayer::init() { } m_input_connections.emplace_back( - std::pair>{ (std ::size_t) my_player_id, connection }, + std::pair>{ static_cast(my_player_id), + connection }, receive_connections ); diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index dac6c6b9..33eb43de 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -51,7 +51,7 @@ struct Connection { return tl::make_optional("SDLNet_TCP_Send: invalid socket"); } - if ((std::size_t) result != length) { + if (static_cast(result) != length) { std::free(message); std::string error = "SDLNet_TCP_Send: " + network_util::latest_sdl_net_error(); return tl::make_optional(error); diff --git a/src/network/network_transportable.cpp b/src/network/network_transportable.cpp index ddd6e301..07dab56a 100644 --- a/src/network/network_transportable.cpp +++ b/src/network/network_transportable.cpp @@ -36,7 +36,7 @@ void Transportable::write_header(RawBytes bytes, std::uint32_t uuid, std::uint32 throw std::runtime_error{ "Wrong call of Transportable::write_header : header size mismatch" }; } - std::uint32_t* data_ptr = (std::uint32_t*) start; + std::uint32_t* data_ptr = reinterpret_cast(start); data_ptr[0] = Transportable::protocol_version; @@ -48,7 +48,7 @@ void Transportable::write_data(RawBytes bytes, const Transportable* transportabl auto [start, length] = bytes; - uint8_t* data_ptr = (uint8_t*) (transportable); + const uint8_t* data_ptr = reinterpret_cast(transportable); std::memcpy(start, data_ptr, length); } @@ -61,7 +61,7 @@ void Transportable::write_checksum(RawBytes bytes) { std::uint32_t checksum = Transportable::checksum(RawBytes{ start, data_size }); - std::uint32_t* data_ptr = (std::uint32_t*) (static_cast(start) + data_size); + std::uint32_t* data_ptr = reinterpret_cast(static_cast(start) + data_size); data_ptr[0] = checksum; } @@ -94,7 +94,7 @@ tl::expected, std::string> RawTransportData::from_ auto [_protocol_version, uuid, data_size] = header.value(); - if (remaining_length < (long) data_size) { + if (remaining_length < static_cast(data_size)) { return tl::make_unexpected( "in RawTransportData::from_raw_bytes: couldn't read data, since the raw data is to small" ); @@ -126,7 +126,7 @@ tl::expected, std::strin return tl::make_unexpected("couldn't read header, since the raw data is to small"); } - std::uint32_t* data_ptr = (std::uint32_t*) start; + std::uint32_t* data_ptr = reinterpret_cast(start); std::uint32_t protocol_version_number = data_ptr[0]; if (RawTransportData::protocol_version != protocol_version_number) { @@ -151,7 +151,7 @@ tl::expected RawTransportData::read_checksum(RawByte std::uint32_t calc_checksum = Transportable::checksum(RawBytes{ start, data_size }); - std::uint32_t* data_ptr = (std::uint32_t*) (static_cast(start) + data_size); + std::uint32_t* data_ptr = reinterpret_cast(static_cast(start) + data_size); std::uint32_t read_checksum = data_ptr[0]; diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp index 19ba4568..e782b761 100644 --- a/src/network/network_transportable.hpp +++ b/src/network/network_transportable.hpp @@ -32,7 +32,7 @@ using RawBytes = std::pair; const std::uint32_t send_size = Transportable::header_size + data_size + Transportable::checksum_size; - uint8_t* memory = (uint8_t*) std::malloc(send_size); + uint8_t* memory = static_cast(std::malloc(send_size)); if (!memory) { return tl::make_unexpected("error in malloc for sending a message"); } From 5ab737f6b94cdc01078f80784feae1adc3256388 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Sat, 8 Apr 2023 04:55:26 +0200 Subject: [PATCH 31/45] added better help for cli --- src/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index 2bb3fa44..e8174902 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -31,6 +31,10 @@ std::tuple, PlayMode, bool> parse_args(std:: return { tl::nullopt, PlayMode::SinglePlayer, false }; } case 2: { + if ("--help" == args.at(1) or "-h" == args.at(1) or "?" == args.at(1)) { + usage(args.at(0)); + std::exit(0); + } return { args.at(1), PlayMode::SinglePlayer, false }; } case 3: { From fe3048ba323660e9597ce65c55e94a62fbb4d287 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Sat, 8 Apr 2023 05:06:01 +0200 Subject: [PATCH 32/45] WIP: seed addition --- src/local_multiplayer.cpp | 2 -- src/network/network_data.hpp | 3 ++- src/network/network_transportable.hpp | 1 - src/play_manager.cpp | 24 ++++++++++++------------ src/play_manager.hpp | 3 ++- src/tetris_application.hpp | 23 +++++++++++++---------- 6 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index 8db38212..153203ee 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -94,8 +94,6 @@ tl::expected LocalMultiplayer::init(Settings settings) // send the information about how many players partecipate in response, await the creation of x-1 sockets to send them the event from them, not that the client doesn't have a static player number, only the server can (later select this from a menu) set this size - // TODO add seed here, the server generates one seed - auto send_data = ClientInitializationData{ static_cast(m_num_players), static_cast(i + 1) }; const auto send_result = connection->send_data(&send_data); diff --git a/src/network/network_data.hpp b/src/network/network_data.hpp index e81e10c4..2758236e 100644 --- a/src/network/network_data.hpp +++ b/src/network/network_data.hpp @@ -5,6 +5,7 @@ #include "../input.hpp" #include "network_transportable.hpp" #include +#include "../random.hpp" enum class InitializationDataType : uint8_t { Receive, Send }; @@ -31,7 +32,7 @@ struct ClientInitializationData : public Transportable { public: std::uint32_t player_num; std::uint32_t your_player_id; - //TODO add seed here + //TODO: Random::Seed seed; static constexpr std::uint32_t serialUUID = 3; explicit ClientInitializationData(std::uint32_t player_num, std::uint32_t your_player_id); diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp index e782b761..a0729495 100644 --- a/src/network/network_transportable.hpp +++ b/src/network/network_transportable.hpp @@ -25,7 +25,6 @@ using RawBytes = std::pair; //TODO enforce this in some compile time way, that they are unique! /* virtual */ static constexpr std::uint32_t serialUUID = 0; - //TODO fix inconsistency and don't raise exceptions, rather return tl:expected template static tl::expected serialize(const T* transportable) { constexpr std::uint32_t data_size = sizeof(T); diff --git a/src/play_manager.cpp b/src/play_manager.cpp index c614cbbc..7e37df06 100644 --- a/src/play_manager.cpp +++ b/src/play_manager.cpp @@ -34,23 +34,23 @@ SinglePlayer::get_input(std::size_t index, GameManager* associated_game_manager, throw std::range_error{ "SinglePlayer mode: error in index of get_input" }; } - auto keyboard_input = std::make_unique(associated_game_manager, util::assert_is_keyboard_controls(settings().controls.at(index))); + auto keyboard_input = std::make_unique( + associated_game_manager, util::assert_is_keyboard_controls(settings().controls.at(index)) + ); + event_dispatcher->register_listener(keyboard_input.get()); return std::pair>{ 0, std::move(keyboard_input) }; } - - namespace util { - KeyboardControls assert_is_keyboard_controls(Controls& controls){ - return std::visit( - overloaded{ [&](KeyboardControls& keyboard_controls) ->KeyboardControls { - return keyboard_controls; - }, - [&](ReplayControls& replay_controls) -> KeyboardControls { - throw std::runtime_error{"in assert_is_keyboard_controls: input is not KeyboardControls"}; - } }, + KeyboardControls assert_is_keyboard_controls(Controls& controls) { + return std::visit( + overloaded{ + [&](KeyboardControls& keyboard_controls) -> KeyboardControls { return keyboard_controls; }, + [&]([[maybe_unused]] ReplayControls& replay_controls) -> KeyboardControls { + throw std::runtime_error{ "in assert_is_keyboard_controls: input is not KeyboardControls" }; + } }, controls ); } -} +} // namespace util diff --git a/src/play_manager.hpp b/src/play_manager.hpp index 9caac57e..847aabcf 100644 --- a/src/play_manager.hpp +++ b/src/play_manager.hpp @@ -9,11 +9,12 @@ #include #include #include +#include "random.hpp" struct StartState { std::size_t num_players; // std::vector state; - //TODO add seed here + //TODO: Random::Seed seed; }; diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index b3814bdb..4a271e56 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -48,15 +48,6 @@ struct TetrisApplication : public Application { //TODO assert that enough settings are here fro example 4 players! (could be more eventually!) - auto is_valid = m_manager->init(m_settings); - if (!is_valid.has_value()) { - std::cerr << "Error in initializing PlayManager: " << is_valid.error() << "\n"; - std::exit(2); - } - - auto start_state = is_valid.value(); - const auto num_players = start_state.num_players; - const auto is_recording = recording_path.has_value(); auto recording = [&]() -> tl::optional { if (is_recording) { @@ -69,7 +60,18 @@ struct TetrisApplication : public Application { //TODO the seed gets generated by the player manager, either by the client or the server in multiplayer scenarios //TODO: to handle recording support for multiplayer, we have to say in some way, which player is recorded, this has to be done with a custom PlayerManager, like ghostdata in Mario kart you can battle against a recorded data from yourself our from the cloud/ online ranking / leaderboard - const auto random_seed = (is_recording and num_players == 1 ? recording->seed() : Random::generate_seed()); + const auto random_seed = (is_recording ? recording->seed() : Random::generate_seed()); + + auto is_valid = m_manager->init(m_settings/* , random_seed */); + if (!is_valid.has_value()) { + std::cerr << "Error in initializing PlayManager: " << is_valid.error() << "\n"; + std::exit(2); + } + + auto start_state = is_valid.value(); + const auto num_players = start_state.num_players; + + for (std::size_t i = 0; i < num_players; ++i) { @@ -77,6 +79,7 @@ struct TetrisApplication : public Application { //TODO get a more detailed start state from the client: // m_game_managers.push_back(std::make_unique(i, start_state.state.at(i))); const auto record_game = not is_recording; + //TODO get these from the manager, before calling get_input()! m_game_managers.push_back(std::make_unique(i, random_seed, record_game)); if (is_recording) { m_inputs.push_back(create_input(ReplayControls{ std::move(*recording) }, m_game_managers.back().get())); From 3883d06054189b958a6c660655b53425eb547cef Mon Sep 17 00:00:00 2001 From: Totto16 Date: Sat, 8 Apr 2023 13:31:56 +0200 Subject: [PATCH 33/45] added seed support --- src/local_multiplayer.cpp | 33 ++++++++++++++++++++------------- src/local_multiplayer.hpp | 3 ++- src/network/network_data.cpp | 10 ++++++++-- src/network/network_data.hpp | 4 ++-- src/play_manager.cpp | 11 ++++++----- src/play_manager.hpp | 6 +++--- src/tetris_application.hpp | 10 ++++------ 7 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/local_multiplayer.cpp b/src/local_multiplayer.cpp index 153203ee..c875c359 100644 --- a/src/local_multiplayer.cpp +++ b/src/local_multiplayer.cpp @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include LocalMultiplayer::LocalMultiplayer(std::size_t num_players, bool is_server) : PlayManager{}, @@ -22,8 +22,8 @@ LocalMultiplayer::LocalMultiplayer(std::size_t num_players, bool is_server) m_input_connections{ ConnectionStore{} } {}; -tl::expected LocalMultiplayer::init(Settings settings) { - PlayManager::init(settings); +tl::expected LocalMultiplayer::init(Settings settings, Random::Seed seed) { + PlayManager::init(settings, seed); if (m_is_server) { @@ -35,8 +35,10 @@ tl::expected LocalMultiplayer::init(Settings settings) } if (settings.controls.size() < m_num_players) { - return tl::make_unexpected("Noz enough controls provided: needed: " + std::to_string(m_num_players) - + " got: " + std::to_string(settings.controls.size())); + return tl::make_unexpected( + "Not enough controls provided: needed: " + std::to_string(m_num_players) + + " got: " + std::to_string(settings.controls.size()) + ); } auto server = m_network_manager.spawn_server(); @@ -95,7 +97,7 @@ tl::expected LocalMultiplayer::init(Settings settings) auto send_data = ClientInitializationData{ static_cast(m_num_players), - static_cast(i + 1) }; + static_cast(i + 1), seed }; const auto send_result = connection->send_data(&send_data); if (send_result.has_value()) { throw std::runtime_error{ "ClientInitializationData failed to send" + send_result.value() }; @@ -161,7 +163,7 @@ tl::expected LocalMultiplayer::init(Settings settings) } - return StartState{ m_num_players }; + return StartState{ m_num_players, seed }; } else { // client start here @@ -179,14 +181,16 @@ tl::expected LocalMultiplayer::init(Settings settings) } const auto data = await_exact_one(connection); + const auto num_players = data->player_num; + m_num_players = num_players; auto receive_connections = std::vector>>{}; - receive_connections.reserve(m_num_players - 1); + receive_connections.reserve(num_players - 1); const auto my_player_id = data->your_player_id; - for (std::uint32_t i = 0; i < data->player_num; ++i) { + for (std::uint32_t i = 0; i < num_players; ++i) { if (i == my_player_id) { continue; } @@ -215,7 +219,7 @@ tl::expected LocalMultiplayer::init(Settings settings) ); - return StartState{ data->player_num }; + return StartState{ num_players , data->seed}; } } @@ -246,8 +250,9 @@ std::pair> LocalMultiplayer::get_input( associated_game_manager->set_online_handler(std::make_unique(m_server, nullptr, send_to)); - - auto keyboard_input = std::make_unique(associated_game_manager, util::assert_is_keyboard_controls(settings().controls.at(index))); + auto keyboard_input = std::make_unique( + associated_game_manager, util::assert_is_keyboard_controls(settings().controls.at(index)) + ); event_dispatcher->register_listener(keyboard_input.get()); return std::pair>{ 0, std::move(keyboard_input) }; } else { @@ -266,7 +271,9 @@ std::pair> LocalMultiplayer::get_input( std::make_unique(nullptr, m_input_connections.at(0).first.second) ); - auto keyboard_input = std::make_unique(associated_game_manager, util::assert_is_keyboard_controls(settings().controls.at(index))); + auto keyboard_input = std::make_unique( + associated_game_manager, util::assert_is_keyboard_controls(settings().controls.at(index)) + ); event_dispatcher->register_listener(keyboard_input.get()); return std::pair>{ m_input_connections.at(0).first.first, std::move(keyboard_input) }; diff --git a/src/local_multiplayer.hpp b/src/local_multiplayer.hpp index 3c28f0e0..22192bcd 100644 --- a/src/local_multiplayer.hpp +++ b/src/local_multiplayer.hpp @@ -5,6 +5,7 @@ #include "network/network_data.hpp" #include "network/network_manager.hpp" #include "play_manager.hpp" +#include "random.hpp" #include #include #include @@ -31,7 +32,7 @@ struct LocalMultiplayer : public PlayManager { public: explicit LocalMultiplayer(std::size_t num_players, bool is_server); - tl::expected init(Settings settings) override; + tl::expected init(Settings settings, Random::Seed seed) override; std::pair> get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher) override; }; diff --git a/src/network/network_data.cpp b/src/network/network_data.cpp index 1c52bccf..814777c7 100644 --- a/src/network/network_data.cpp +++ b/src/network/network_data.cpp @@ -1,6 +1,7 @@ #include "network_data.hpp" +#include "../random.hpp" #include "network_transportable.hpp" #include @@ -16,6 +17,11 @@ InputEvent EventData::event() const { return m_event; } -ClientInitializationData::ClientInitializationData(std::uint32_t player_num, std::uint32_t your_player_id) +ClientInitializationData::ClientInitializationData( + std::uint32_t player_num, + std::uint32_t your_player_id, + Random::Seed seed +) : player_num{ player_num }, - your_player_id{ your_player_id } {}; \ No newline at end of file + your_player_id{ your_player_id }, + seed{ seed } {}; \ No newline at end of file diff --git a/src/network/network_data.hpp b/src/network/network_data.hpp index 2758236e..7ffeb040 100644 --- a/src/network/network_data.hpp +++ b/src/network/network_data.hpp @@ -32,8 +32,8 @@ struct ClientInitializationData : public Transportable { public: std::uint32_t player_num; std::uint32_t your_player_id; - //TODO: Random::Seed seed; + Random::Seed seed; static constexpr std::uint32_t serialUUID = 3; - explicit ClientInitializationData(std::uint32_t player_num, std::uint32_t your_player_id); + explicit ClientInitializationData(std::uint32_t player_num, std::uint32_t your_player_id, Random::Seed seed); }; \ No newline at end of file diff --git a/src/play_manager.cpp b/src/play_manager.cpp index 7e37df06..badd7983 100644 --- a/src/play_manager.cpp +++ b/src/play_manager.cpp @@ -2,6 +2,7 @@ #include "play_manager.hpp" #include "game_manager.hpp" +#include "random.hpp" #include #include #include @@ -10,9 +11,9 @@ PlayManager::PlayManager(){}; -tl::expected PlayManager::init(Settings settings) { +tl::expected PlayManager::init(Settings settings, Random::Seed seed) { m_settings = settings; - return StartState{ 0 }; + return StartState{ 0, seed }; }; Settings PlayManager::settings() { @@ -22,9 +23,9 @@ Settings PlayManager::settings() { SinglePlayer::SinglePlayer() : PlayManager{} {}; -tl::expected SinglePlayer::init(Settings settings) { - PlayManager::init(settings); - return StartState{ 1 }; +tl::expected SinglePlayer::init(Settings settings, Random::Seed seed) { + PlayManager::init(settings, seed); + return StartState{ 1, seed }; } std::pair> diff --git a/src/play_manager.hpp b/src/play_manager.hpp index 847aabcf..284e0f3a 100644 --- a/src/play_manager.hpp +++ b/src/play_manager.hpp @@ -14,7 +14,7 @@ struct StartState { std::size_t num_players; // std::vector state; - //TODO: Random::Seed seed; + Random::Seed seed; }; @@ -24,7 +24,7 @@ struct StartState { public: explicit PlayManager(); virtual ~PlayManager() = default; - virtual tl::expected init(Settings settings); + virtual tl::expected init(Settings settings, Random::Seed seed); virtual std::pair> get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher) = 0; Settings settings(); @@ -34,7 +34,7 @@ struct StartState { struct SinglePlayer : public PlayManager { public: explicit SinglePlayer(); - tl::expected init(Settings settings) override; + tl::expected init(Settings settings, Random::Seed seed) override; std::pair> get_input(std::size_t index, GameManager* associated_game_manager, EventDispatcher* event_dispatcher) override; }; diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index 4a271e56..188b6bbc 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -62,7 +62,7 @@ struct TetrisApplication : public Application { //TODO: to handle recording support for multiplayer, we have to say in some way, which player is recorded, this has to be done with a custom PlayerManager, like ghostdata in Mario kart you can battle against a recorded data from yourself our from the cloud/ online ranking / leaderboard const auto random_seed = (is_recording ? recording->seed() : Random::generate_seed()); - auto is_valid = m_manager->init(m_settings/* , random_seed */); + auto is_valid = m_manager->init(m_settings, random_seed); if (!is_valid.has_value()) { std::cerr << "Error in initializing PlayManager: " << is_valid.error() << "\n"; std::exit(2); @@ -70,17 +70,15 @@ struct TetrisApplication : public Application { auto start_state = is_valid.value(); const auto num_players = start_state.num_players; - - - + const auto seed_to_use = start_state.seed; for (std::size_t i = 0; i < num_players; ++i) { - spdlog::info("seed for player {}: {}", i + 1, random_seed); + spdlog::info("seed for player {}: {}", i + 1, seed_to_use); //TODO get a more detailed start state from the client: // m_game_managers.push_back(std::make_unique(i, start_state.state.at(i))); const auto record_game = not is_recording; //TODO get these from the manager, before calling get_input()! - m_game_managers.push_back(std::make_unique(i, random_seed, record_game)); + m_game_managers.push_back(std::make_unique(i, seed_to_use, record_game)); if (is_recording) { m_inputs.push_back(create_input(ReplayControls{ std::move(*recording) }, m_game_managers.back().get())); } else { From 875671f32a852d6bf6469977a5e937f62ca5ff7b Mon Sep 17 00:00:00 2001 From: Totto16 Date: Sat, 8 Apr 2023 13:46:29 +0200 Subject: [PATCH 34/45] refactored player text display, and fixed the crash --- src/game_manager.cpp | 21 ++++++++++++--------- src/game_manager.hpp | 3 ++- src/tetris_application.hpp | 5 +---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/game_manager.cpp b/src/game_manager.cpp index 2a915c2b..c76973dd 100644 --- a/src/game_manager.cpp +++ b/src/game_manager.cpp @@ -7,14 +7,14 @@ #include #include -GameManager::GameManager(const std::size_t field_num, const Random::Seed random_seed, const bool record_game) +GameManager::GameManager(const std::size_t field_num, const Random::Seed random_seed, const bool record_game, const bool use_player_text) : m_field_num{ field_num }, m_random{ random_seed }, m_grid{ Point{ static_cast((field_num * GameManager::size_per_field) + (field_num * GameManager::space_between)), 0 }, tile_size }, m_next_gravity_simulation_step_index{ get_gravity_delay_frames()},m_recording{ random_seed }, - m_record_game{ record_game } { + m_record_game{ record_game }, m_use_player_text{use_player_text} { m_fonts.push_back(std::make_shared("assets/fonts/PressStart2P.ttf", 18)); m_text_rows = std::vector{}; m_text_rows.emplace_back( @@ -33,12 +33,14 @@ GameManager::GameManager(const std::size_t field_num, const Random::Seed random_ ) }, Color::white(), "lines: 0", m_fonts.front() ); - m_text_rows.emplace_back( - Point{ m_grid.to_screen_coords( - Grid::preview_tetromino_position + Point{ 0, Grid::preview_extends.y + 3 } - ) }, - Color::white(), "", m_fonts.front() - ); + if (m_use_player_text) { + m_text_rows.emplace_back( + Point{ m_grid.to_screen_coords( + Grid::preview_tetromino_position + Point{ 0, Grid::preview_extends.y + 3 } + ) }, + Color::white(), "player: 0", m_fonts.front() + ); + } } void GameManager::update() { @@ -261,6 +263,7 @@ void GameManager::refresh_texts() { stream << "score: " << m_score; m_text_rows.at(i++).set_text(stream.str()); + stream = {}; stream << "level: " << m_level; m_text_rows.at(i++).set_text(stream.str()); @@ -268,7 +271,7 @@ void GameManager::refresh_texts() { stream << "lines: " << m_lines_cleared; m_text_rows.at(i++).set_text(stream.str()); - if (m_player_num.has_value()) { + if (m_use_player_text and m_player_num.has_value()) { stream = {}; stream << "player " << m_player_num.value(); m_text_rows.at(i++).set_text(stream.str()); diff --git a/src/game_manager.hpp b/src/game_manager.hpp index 12f6a160..bf7023ba 100644 --- a/src/game_manager.hpp +++ b/src/game_manager.hpp @@ -60,9 +60,10 @@ struct GameManager final { bool m_allowed_to_hold = true; std::unique_ptr m_online_handler = nullptr; tl::optional m_player_num = tl::nullopt; + bool m_use_player_text; public: - GameManager(const std::size_t field_num, const Random::Seed random_seed, const bool record_game); + GameManager(const std::size_t field_num, const Random::Seed random_seed, const bool record_game, const bool use_player_text); void update(); void render(const Application& app) const; diff --git a/src/tetris_application.hpp b/src/tetris_application.hpp index 188b6bbc..9dc31118 100644 --- a/src/tetris_application.hpp +++ b/src/tetris_application.hpp @@ -74,11 +74,8 @@ struct TetrisApplication : public Application { for (std::size_t i = 0; i < num_players; ++i) { spdlog::info("seed for player {}: {}", i + 1, seed_to_use); - //TODO get a more detailed start state from the client: - // m_game_managers.push_back(std::make_unique(i, start_state.state.at(i))); const auto record_game = not is_recording; - //TODO get these from the manager, before calling get_input()! - m_game_managers.push_back(std::make_unique(i, seed_to_use, record_game)); + m_game_managers.push_back(std::make_unique(i, seed_to_use, record_game, num_players > 1)); if (is_recording) { m_inputs.push_back(create_input(ReplayControls{ std::move(*recording) }, m_game_managers.back().get())); } else { From f8612ae367f6f500fef50dd516c01cba12cd3306 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Sat, 8 Apr 2023 13:50:08 +0200 Subject: [PATCH 35/45] added overload for util::to_hex_str --- src/util.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/util.hpp b/src/util.hpp index b80289b3..d032e5ca 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -18,4 +18,12 @@ namespace util { return ss.str(); } + inline std::string to_hex_str(uint32_t number) { + std::ostringstream ss{}; + + ss << "0x" << std::hex << std::setfill('0'); + ss << std::hex << std::setw(8) << static_cast(number); + return ss.str(); + } + } // namespace util From bb6a2ac8dd6a12f36b69346d2fd2ade44dd50e4d Mon Sep 17 00:00:00 2001 From: Totto16 Date: Sat, 8 Apr 2023 13:51:17 +0200 Subject: [PATCH 36/45] removed duplicate use of variable name in scope --- src/input.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/input.cpp b/src/input.cpp index 16050bba..17d7a14b 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -116,10 +116,10 @@ void OnlineInput::update() { } const auto data_vector = data.value().value(); - for (const auto& data : data_vector) { + for (const auto& received_data : data_vector) { - if (data.is_of_type()) { - auto event = data.as_type(); + if (received_data.is_of_type()) { + auto event = received_data.as_type(); //TODO maybe handle return value ? m_target_game_manager->handle_input_event(event->event()); } From dc756d16f43d262035aa6a7b58f09c96c8552b53 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Sat, 8 Apr 2023 14:03:09 +0200 Subject: [PATCH 37/45] some small render imrpovements --- src/game_manager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/game_manager.cpp b/src/game_manager.cpp index c76973dd..714f0fed 100644 --- a/src/game_manager.cpp +++ b/src/game_manager.cpp @@ -241,6 +241,7 @@ void GameManager::set_online_handler(std::unique_ptr online_handl void GameManager::set_player_num(std::size_t player_num) { m_player_num = player_num; + refresh_texts(); } void GameManager::hold_tetromino() { @@ -273,7 +274,8 @@ void GameManager::refresh_texts() { if (m_use_player_text and m_player_num.has_value()) { stream = {}; - stream << "player " << m_player_num.value(); + // for humans it' more readable, when it's 1 indexed + stream << "player " << m_player_num.value() +1 ; m_text_rows.at(i++).set_text(stream.str()); } } From 3d88c7240a26728d60f552931b024f97c259b8b2 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Sat, 8 Apr 2023 14:07:07 +0200 Subject: [PATCH 38/45] update sdl2 wrap --- subprojects/sdl2.wrap | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/subprojects/sdl2.wrap b/subprojects/sdl2.wrap index 4f8cfa58..e1d72eec 100644 --- a/subprojects/sdl2.wrap +++ b/subprojects/sdl2.wrap @@ -1,12 +1,13 @@ [wrap-file] -directory = SDL2-2.26.0 -source_url = https://github.com/libsdl-org/SDL/releases/download/release-2.26.0/SDL2-2.26.0.tar.gz -source_filename = SDL2-2.26.0.tar.gz -source_hash = 8000d7169febce93c84b6bdf376631f8179132fd69f7015d4dadb8b9c2bdb295 -patch_filename = sdl2_2.26.0-2_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/sdl2_2.26.0-2/get_patch -patch_hash = a5db2f591a8a55aefd1d12022a5472d4263eb220d83ca7f07e2f560077daa88f -wrapdb_version = 2.26.0-2 +directory = SDL2-2.26.5 +source_url = https://github.com/libsdl-org/SDL/releases/download/release-2.26.5/SDL2-2.26.5.tar.gz +source_filename = SDL2-2.26.5.tar.gz +source_hash = ad8fea3da1be64c83c45b1d363a6b4ba8fd60f5bde3b23ec73855709ec5eabf7 +patch_filename = sdl2_2.26.5-1_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/sdl2_2.26.5-1/get_patch +patch_hash = bd135e66d9d345411f147d95ae348c1b3fa3dea6f7f89466f5c76695811871dc +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/sdl2_2.26.5-1/SDL2-2.26.5.tar.gz +wrapdb_version = 2.26.5-1 [provide] sdl2 = sdl2_dep From c390f11d714e68d0a82fd391fedb8288b05d3928 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Sat, 8 Apr 2023 14:12:21 +0200 Subject: [PATCH 39/45] fixed merge error --- src/game_manager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/game_manager.cpp b/src/game_manager.cpp index 714f0fed..9a11eee6 100644 --- a/src/game_manager.cpp +++ b/src/game_manager.cpp @@ -252,6 +252,7 @@ void GameManager::hold_tetromino() { if (not m_tetromino_on_hold.has_value()) { m_tetromino_on_hold = Tetromino{ Grid::hold_tetromino_position, m_active_tetromino->type() }; spawn_next_tetromino(); + } else { const auto on_hold = m_tetromino_on_hold->type(); m_tetromino_on_hold = Tetromino{ Grid::hold_tetromino_position, m_active_tetromino->type() }; spawn_next_tetromino(on_hold); @@ -275,7 +276,7 @@ void GameManager::refresh_texts() { if (m_use_player_text and m_player_num.has_value()) { stream = {}; // for humans it' more readable, when it's 1 indexed - stream << "player " << m_player_num.value() +1 ; + stream << "player " << m_player_num.value() + 1; m_text_rows.at(i++).set_text(stream.str()); } } From 028a2799c14a7726e3612a1882aef1977d994506 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Sat, 8 Apr 2023 14:22:28 +0200 Subject: [PATCH 40/45] fixed meson file --- meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index a374fbc0..3c7732a6 100644 --- a/meson.build +++ b/meson.build @@ -39,15 +39,15 @@ else deps += sdl2_ttf_dep endif -sdl2_ttf_dep = dependency('sdl2_net', required: false) -if not sdl2_ttf_dep.found() +sdl2_net_dep = dependency('sdl2_net', required: false) +if not sdl2_net_dep.found() deps += dependency( 'sdl2_net', required: true, fallback: 'sdl2_net', ) else - deps += sdl2_ttf_dep + deps += sdl2_net_dep endif spdlog_dep = dependency('spdlog', required: false) From 4dec9e58a10450861653264ffb4b87b4089978af Mon Sep 17 00:00:00 2001 From: Totto16 Date: Sun, 9 Apr 2023 10:46:17 +0200 Subject: [PATCH 41/45] use concepts for templates --- src/local_multiplayer.hpp | 4 +++- src/network/connection_manager.hpp | 4 ++++ src/network/network_transportable.hpp | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/local_multiplayer.hpp b/src/local_multiplayer.hpp index 22192bcd..480145e0 100644 --- a/src/local_multiplayer.hpp +++ b/src/local_multiplayer.hpp @@ -6,6 +6,7 @@ #include "network/network_manager.hpp" #include "play_manager.hpp" #include "random.hpp" +#include #include #include #include @@ -41,7 +42,8 @@ struct LocalMultiplayer : public PlayManager { // little fast helper, //TODO refactor better and move in some reasonable cpp, and don't use exceptions to handle this template -std::shared_ptr await_exact_one(std::shared_ptr connection) { +requires std::derived_from std::shared_ptr await_exact_one(std::shared_ptr connection +) { //TODO make this number customizable auto send_initializer = connection->wait_for_data(10 * 1000, 100); if (!send_initializer.has_value()) { diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index 33eb43de..940d2b71 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -12,6 +12,7 @@ #include #include #include +#include using MaybeData = tl::expected>, std::string>; @@ -35,6 +36,7 @@ struct Connection { template + requires std::derived_from std::shared_ptr tl::optional send_data(const T* transportable) { @@ -77,6 +79,7 @@ struct Server { tl::optional> get_client(Uint32 ms_delay = 100, std::size_t abort_after = 60 * 1000); template + requires std::derived_from std::shared_ptr tl::optional send_all(const T* transportable, std::vector> send_to) { for (std::size_t i = 0; i < send_to.size(); ++i) { @@ -92,6 +95,7 @@ struct Server { } template + requires std::derived_from std::shared_ptr tl::optional send_all(const T* transportable) { return send_all(transportable, m_connections); diff --git a/src/network/network_transportable.hpp b/src/network/network_transportable.hpp index a0729495..8679f667 100644 --- a/src/network/network_transportable.hpp +++ b/src/network/network_transportable.hpp @@ -26,6 +26,7 @@ using RawBytes = std::pair; /* virtual */ static constexpr std::uint32_t serialUUID = 0; template + requires std::derived_from std::shared_ptr static tl::expected serialize(const T* transportable) { constexpr std::uint32_t data_size = sizeof(T); @@ -78,11 +79,13 @@ struct RawTransportData { static tl::expected read_checksum(RawBytes bytes, std::uint32_t data_size); template + requires std::derived_from std::shared_ptr bool is_of_type() const { return m_serialUUID == T::serialUUID; } template + requires std::derived_from std::shared_ptr std::shared_ptr as_type() const { if (!is_of_type()) { throw std::bad_cast{}; From 8667c2f8271033c24fa2edf9261ca7cdf0087f86 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Tue, 18 Apr 2023 23:16:53 +0200 Subject: [PATCH 42/45] removed technical debt: - remove helper function that was used during debugging of a crash --- src/network/connection_manager.cpp | 2 +- src/network/connection_manager.hpp | 3 +-- src/network/network_manager.cpp | 9 ++++----- src/network/network_util.hpp | 23 ----------------------- src/sdl_context.cpp | 3 +-- 5 files changed, 7 insertions(+), 33 deletions(-) delete mode 100644 src/network/network_util.hpp diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index ba418091..d377b87e 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -92,7 +92,7 @@ tl::expected Connection::get_all_data_blocking() { int len = SDLNet_TCP_Recv(m_socket, memory, Connection::chunk_size); if (len <= 0) { free(memory); - return tl::make_unexpected("SDLNet_TCP_Recv: " + network_util::latest_sdl_net_error()); + return tl::make_unexpected("SDLNet_TCP_Recv: " +std::string{ SDLNet_GetError() }); } if (len != Connection::chunk_size) { diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index aeace964..a37f34d6 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -5,7 +5,6 @@ #include "network_transportable.hpp" -#include "network_util.hpp" #include #include #include @@ -58,7 +57,7 @@ struct Connection { if (static_cast(result) != length) { std::free(message); - std::string error = "SDLNet_TCP_Send: " + network_util::latest_sdl_net_error(); + std::string error = "SDLNet_TCP_Send: " + std::string{ SDLNet_GetError() }; return tl::make_optional(error); } diff --git a/src/network/network_manager.cpp b/src/network/network_manager.cpp index 16c1a42b..48c1cf01 100644 --- a/src/network/network_manager.cpp +++ b/src/network/network_manager.cpp @@ -1,7 +1,6 @@ #include "network_manager.hpp" #include "network_transportable.hpp" -#include "network_util.hpp" #include #include #include @@ -18,13 +17,13 @@ MaybeConnection NetworkManager::try_connect(const char* host, Uint16 port) { TCPsocket tcpsock; if (SDLNet_ResolveHost(&ip, host, port) == -1) { - std::string error = "SDLNet_ResolveHost: " + network_util::latest_sdl_net_error(); + std::string error = "SDLNet_ResolveHost: " + std::string{ SDLNet_GetError() }; return tl::make_unexpected(error); } tcpsock = SDLNet_TCP_Open(&ip); if (!tcpsock) { - std::string error = "SDLNet_TCP_Open: " + network_util::latest_sdl_net_error(); + std::string error = "SDLNet_TCP_Open: " + std::string{ SDLNet_GetError() }; return tl::make_unexpected(error); } @@ -36,12 +35,12 @@ MaybeServer NetworkManager::spawn_server(Uint16 port) { TCPsocket server; IPaddress ip; if (SDLNet_ResolveHost(&ip, NULL, port) == -1) { - std::string error = "SDLNet_ResolveHost: " + network_util::latest_sdl_net_error(); + std::string error = "SDLNet_ResolveHost: " + std::string{ SDLNet_GetError() }; return tl::make_unexpected(error); } server = SDLNet_TCP_Open(&ip); if (!server) { - std::string error = "SDLNet_TCP_Open: " + network_util::latest_sdl_net_error(); + std::string error = "SDLNet_TCP_Open: " + std::string{ SDLNet_GetError() }; return tl::make_unexpected(error); } diff --git a/src/network/network_util.hpp b/src/network/network_util.hpp deleted file mode 100644 index ed265f7d..00000000 --- a/src/network/network_util.hpp +++ /dev/null @@ -1,23 +0,0 @@ - - -#pragma once - -#include "string" -#include -#include - -namespace network_util { - - - inline std::string latest_sdl_net_error() { - - - const char* sdl_err = SDLNet_GetError(); - const std::string error_message = - sdl_err == nullptr || std::strlen(sdl_err) == 0 ? "Unknown SDL_net error" : std::string{ sdl_err }; - - return error_message; - } - - -} // namespace network_util \ No newline at end of file diff --git a/src/sdl_context.cpp b/src/sdl_context.cpp index e804b548..d0891531 100644 --- a/src/sdl_context.cpp +++ b/src/sdl_context.cpp @@ -1,5 +1,4 @@ #include "sdl_context.hpp" -#include "network/network_util.hpp" #include #include #include @@ -15,7 +14,7 @@ SdlContext::SdlContext() { } //TODO: if we don't need the network, this should be disabled if (SDLNet_Init() == -1) { - printf("SDLNet_Init: %s\n", network_util::latest_sdl_net_error().c_str()); + printf("SDLNet_Init: %s\n", SDLNet_GetError()); exit(2); } } From b31b66bac26fa1125aaf8a634f84860f741ba102 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Wed, 19 Apr 2023 00:16:34 +0200 Subject: [PATCH 43/45] refactor and review Connection and Server class: - added [[nodiscard]] where it's usefull and makes sense - started with docs - refactored some API for better convenience in use --- src/network/connection_manager.cpp | 165 ++++++++++++++--------------- src/network/connection_manager.hpp | 74 ++++++++++--- src/network/online_handler.cpp | 8 +- src/network/online_input.cpp | 5 +- 4 files changed, 145 insertions(+), 107 deletions(-) diff --git a/src/network/connection_manager.cpp b/src/network/connection_manager.cpp index d377b87e..84b4c45e 100644 --- a/src/network/connection_manager.cpp +++ b/src/network/connection_manager.cpp @@ -9,95 +9,53 @@ #include #include + Connection::Connection(TCPsocket socket) : m_socket{ socket } {}; Connection::~Connection() { SDLNet_TCP_Close(m_socket); } +[[nodiscard]] tl::expected Connection::is_data_available(Uint32 timeout_ms) { -Server::Server(TCPsocket socket) : m_socket{ socket }, m_connections{ std::vector>{} } {}; - -Server::~Server() { - SDLNet_TCP_Close(m_socket); -} - - -tl::optional> Server::try_get_client() { - - TCPsocket client; - /* try to accept a connection */ - client = SDLNet_TCP_Accept(m_socket); - if (client) { /* no connection accepted */ - auto connection = std::make_shared(client); - m_connections.push_back(connection); - return connection; - } - return tl::nullopt; -} - -tl::optional> Server::get_client(Uint32 ms_delay, std::size_t abort_after) { - auto start_time = SDL_GetTicks64(); - while (true) { - /* try to accept a connection */ - auto client = try_get_client(); - if (client.has_value()) { - return client; - } - - auto elapsed_time = SDL_GetTicks64() - start_time; - if (elapsed_time >= abort_after) { - return tl::nullopt; - } - - SDL_Delay(ms_delay); - continue; - } -} - - -tl::expected Connection::is_data_available(Uint32 timeout_ms) { - - SDLNet_SocketSet set = SDLNet_AllocSocketSet(1); - if (!set) { + SDLNet_SocketSet socket_set = SDLNet_AllocSocketSet(1); + if (!socket_set) { return tl::make_unexpected("no more memory for creating a SDLNet_SocketSet"); } - auto num_sockets = SDLNet_TCP_AddSocket(set, m_socket); + const auto num_sockets = SDLNet_TCP_AddSocket(socket_set, m_socket); if (num_sockets != 1) { - SDLNet_FreeSocketSet(set); + SDLNet_FreeSocketSet(socket_set); return tl::make_unexpected("SDLNet_AddSocket failed, this is an implementation error"); } - auto result = SDLNet_CheckSockets(set, timeout_ms); + const auto result = SDLNet_CheckSockets(socket_set, timeout_ms); if (result == -1) { - SDLNet_FreeSocketSet(set); + SDLNet_FreeSocketSet(socket_set); return tl::make_unexpected("SDLNet_CheckSockets error (select() system call error)"); } - - SDLNet_FreeSocketSet(set); + SDLNet_FreeSocketSet(socket_set); return result == 1; } - -tl::expected Connection::get_all_data_blocking() { +[[nodiscard]] tl::expected Connection::get_all_data_blocking() { void* memory = std::malloc(Connection::chunk_size); if (!memory) { return tl::make_unexpected("error in malloc for receiving a socket message"); } std::uint32_t data_size = 0; while (true) { - int len = SDLNet_TCP_Recv(m_socket, memory, Connection::chunk_size); - if (len <= 0) { + const int received_length = SDLNet_TCP_Recv(m_socket, memory, Connection::chunk_size); + if (received_length <= 0) { free(memory); - return tl::make_unexpected("SDLNet_TCP_Recv: " +std::string{ SDLNet_GetError() }); + return tl::make_unexpected("SDLNet_TCP_Recv: " + std::string{ SDLNet_GetError() }); } - if (len != Connection::chunk_size) { + if (received_length != Connection::chunk_size) { - return RawBytes{ static_cast(memory), data_size + len }; + return RawBytes{ static_cast(memory), data_size + received_length }; } data_size += Connection::chunk_size; @@ -108,68 +66,65 @@ tl::expected Connection::get_all_data_blocking() { } memory = new_memory; } - - return tl::make_unexpected("error in SDLNet_TCP_Recv: somehow exited the while loop"); } -/* -On error tl::unexpected is returned -if no data is available tl::optional is returned -if data is available a vector of such data is returned, it is guaranteed to be nonempty! -*/ -MaybeData Connection::get_data() { - - auto data_available = is_data_available(); + +[[nodiscard]] tl::expected, std::string> Connection::get_data() { + + const auto data_available = is_data_available(); if (!data_available.has_value()) { return tl::make_unexpected("in is_data_available: " + data_available.error()); } if (!data_available.value()) { - return tl::nullopt; + return std::vector{}; } - auto data = get_all_data_blocking(); + const auto data = get_all_data_blocking(); if (!data.has_value()) { return tl::make_unexpected("in get_all_data_blocking: " + data.error()); } - RawBytes raw_bytes = data.value(); + const RawBytes raw_bytes = data.value(); - auto result = RawTransportData::from_raw_bytes(raw_bytes); + const auto result = RawTransportData::from_raw_bytes(raw_bytes); if (!result.has_value()) { free(raw_bytes.first); return tl::make_unexpected("in RawTransportData::from_raw_bytes: " + result.error()); } free(raw_bytes.first); - return tl::make_optional(result.value()); + return result.value(); } - -tl::expected, std::string> +[[nodiscard]] tl::expected, std::string> Connection::wait_for_data(std::size_t abort_after, Uint32 ms_delay) { - auto start_time = SDL_GetTicks64(); + const auto start_time = SDL_GetTicks64(); while (true) { /* try if data is available */ - auto is_data = is_data_available(); - if (!is_data.has_value()) { - return tl::make_unexpected("In Connection::wait_for_data: " + is_data.error()); + const auto data_available = is_data_available(); + if (!data_available.has_value()) { + return tl::make_unexpected("In Connection::wait_for_data: " + data_available.error()); } - if (is_data.value()) { - auto result = get_data(); + if (data_available.value()) { + const auto result = get_data(); if (!result.has_value()) { return tl::make_unexpected("In Connection::wait_for_data: " + result.error()); } - if (!result.value().has_value()) { - return tl::make_unexpected("In Connection::wait_for_data: " + result.error()); + const auto data_vector = result.value(); + if (data_vector.size() == 0) { + return tl::make_unexpected( + "In Connection::wait_for_data: waited for data, but now no data is available?? This is an " + "implementation bug!" + ); } - return result.value().value(); + return data_vector; } - auto elapsed_time = SDL_GetTicks64() - start_time; + const auto elapsed_time = SDL_GetTicks64() - start_time; if (elapsed_time >= abort_after) { return tl::make_unexpected("In Connection::wait_for_data: took to long"); } @@ -177,4 +132,44 @@ Connection::wait_for_data(std::size_t abort_after, Uint32 ms_delay) { SDL_Delay(ms_delay); continue; } -} \ No newline at end of file +} + + +Server::Server(TCPsocket socket) : m_socket{ socket }, m_connections{ std::vector>{} } {}; + +Server::~Server() { + SDLNet_TCP_Close(m_socket); +} + +[[nodiscard]] tl::optional> Server::try_get_client() { + + TCPsocket client; + /* try to accept a connection */ + client = SDLNet_TCP_Accept(m_socket); + if (!client) { + return tl::nullopt; + } + + const auto connection = std::make_shared(client); + m_connections.push_back(connection); + return connection; +} + +[[nodiscard]] tl::optional> Server::get_client(Uint32 ms_delay, std::size_t abort_after) { + const auto start_time = SDL_GetTicks64(); + while (true) { + + const auto client = try_get_client(); + if (client.has_value()) { + return client; + } + + const auto elapsed_time = SDL_GetTicks64() - start_time; + if (elapsed_time >= abort_after) { + return tl::nullopt; + } + + SDL_Delay(ms_delay); + continue; + } +} diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index a37f34d6..5ef3e823 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -1,6 +1,4 @@ - - #pragma once @@ -13,33 +11,74 @@ #include #include -using MaybeData = tl::expected>, std::string>; - - -//TODO write a method to check if the connection was destroyed +/** + * @brief This is a wrapper for a client TCPSocket. It has convenience functions to interact with the socket and send serialized data over it, + * note that the type 'TCPsocket' is a typedef for some '_TCPsocket *' so it's a raw pointer. You can't a connection yourself, + * to get one you have to call 'Server::try_get_client()' instead. That will handle everything for you automatically. + * If you would like to add some friend classes to do it manually, here are some notes about that: + * The lifetime of the underlying TCPsocket is managed by SDL and therefore it's closed and deallocated on Destruction of this class. + * By passing in a socket you give the connection the ownership of this pointer! + */ struct Connection { private: TCPsocket m_socket; + /** + * @brief static variable for the chunk size for receiving data + */ static constexpr std::size_t chunk_size = 1024; public: explicit Connection(TCPsocket socket); ~Connection(); - tl::expected is_data_available(Uint32 timeout_ms = 3); - tl::expected get_all_data_blocking(); - MaybeData get_data(); - tl::expected, std::string> + //TODO write a method to check if the connection was destroyed + + /** + * @brief check if the underlying socket has some data available + * @param timeout_ms an optional paramater how long to wait for new data, default is 3 ms + * @return tl::expected with the expected value being a bool that describes if data is available and error state is the error as string + */ + [[nodiscard]] tl::expected is_data_available(Uint32 timeout_ms = 3); + + /** + * @brief get all data that is available. this is a blocking function, so not calling 'Connection::is_data_available()' + * before this function may block for a long time! + * This isn't supposed to be used, since it returns raw bytes. But you can use it nevertheless, just note, that the underlying data + * is malloced and has to be freed. If you only transfer classes that are children of 'Transportable' don't use this, + * but use 'Connection::get_data()' + * @return tl::expected with the expected value being a pointer and length wrapped in a pair. note that teh data was malloced + * and has to be freed. Note also that the data is continuous and may be multiple Message one after another. + * To avoid this use 'Connection::get_data()' The error state is the error as string + */ + [[nodiscard]] tl::expected get_all_data_blocking(); + + /** + * @brief get the available data of the Connection. + * @return tl::expected with the expected value being a std::vector and the error the error as string + * the vector contains 0 or more elements of the struct RawTransportData. A vector with length 0 means no data was available. + * 'RawTransportData' has some helpful template member function that can get it's real type and get the underlying type. + * Every data is handled in form of shared pointers that wrap malloced data, it's automatically freed + * in the destructor of the std::shared_ptr and all function that are designed to be used with handle this correctly. + */ + tl::expected, std::string> get_data(); + + /** + * @brief get the available data of the Connection. + * @param timeout_ms an optional paramater how long to wait for new data, default is 3 ms + * @return tl::expected with the expected value being a std::vector and the error the error as string + * the vector contains 0 or more elements of the struct RawTransportData. A vectro with length 0 means no data was available, This has some helpful template member function that can get it's real type and get the underlying type. Every dat is handled in form of shared pointers that wrap malloced data, it'S automatically freed in teh destructor of the std::shared_ptr and all function that are designed to be used with it are + */ + [[nodiscard]] tl::expected, std::string> wait_for_data(std::size_t abort_after = 60 * 1000, Uint32 ms_delay = 100); template //TODO add again //requires std::derived_from std::shared_ptr - tl::optional send_data(const T* transportable) { + [[nodiscard]] tl::optional send_data(const T* transportable) { const auto serialized = Transportable::serialize(transportable); @@ -67,10 +106,11 @@ struct Connection { } }; -//TODO shutdown server on quit struct Server { + //TODO shutdown server on quit + private: TCPsocket m_socket; std::vector> m_connections; @@ -78,13 +118,15 @@ struct Server { public: explicit Server(TCPsocket socket); ~Server(); - tl::optional> try_get_client(); - tl::optional> get_client(Uint32 ms_delay = 100, std::size_t abort_after = 60 * 1000); + [[nodiscard]] tl::optional> try_get_client(); + [[nodiscard]] tl::optional> + get_client(Uint32 ms_delay = 100, std::size_t abort_after = 60 * 1000); template //TODO add again //requires std::derived_from std::shared_ptr - tl::optional send_all(const T* transportable, std::vector> send_to) { + [[nodiscard]] tl::optional + send_all(const T* transportable, std::vector> send_to) { for (std::size_t i = 0; i < send_to.size(); ++i) { auto result = send_to.at(i)->send_data(transportable); @@ -101,7 +143,7 @@ struct Server { template //TODO add again //requires std::derived_from std::shared_ptr - tl::optional send_all(const T* transportable) { + [[nodiscard]] tl::optional send_all(const T* transportable) { return send_all(transportable, m_connections); } diff --git a/src/network/online_handler.cpp b/src/network/online_handler.cpp index 1228757c..d5f67a81 100644 --- a/src/network/online_handler.cpp +++ b/src/network/online_handler.cpp @@ -34,13 +34,13 @@ OnlineHandler::OnlineHandler( void OnlineHandler::handle_event(InputEvent event, u64 simulation_step_index) { if (m_server) { auto event_data = EventData{ event, simulation_step_index }; - //TODO handle error - m_server->send_all(&event_data, m_send_to); + //TODO handle error with spdlog + [[maybe_unused]] auto temp = m_server->send_all(&event_data, m_send_to); } else if (m_connection) { auto event_data = EventData{ event, simulation_step_index }; - //TODO handle error - m_connection->send_data(&event_data); + //TODO handle error with spdlog + [[maybe_unused]] auto temp = m_connection->send_data(&event_data); } else { throw std::runtime_error{ "OnlineHandler needs either a connection (client mode) or server!" }; } diff --git a/src/network/online_input.cpp b/src/network/online_input.cpp index b313a224..3d7858cf 100644 --- a/src/network/online_input.cpp +++ b/src/network/online_input.cpp @@ -11,12 +11,13 @@ void OnlineInput::get_data() { return; } - if (!data.value().has_value()) { + const auto data_vector = data.value(); + + if (data_vector.size() == 0) { // no data given return; } - const auto data_vector = data.value().value(); for (const auto& received_data : data_vector) { if (received_data.is_of_type()) { From 3b2a8e97840a60cabb564c416bcc2ef9c2884729 Mon Sep 17 00:00:00 2001 From: Totto16 Date: Wed, 19 Apr 2023 00:24:31 +0200 Subject: [PATCH 44/45] deleted copy constructor cause copying sockets would cause false behaviour! --- src/network/connection_manager.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index 5ef3e823..42642f06 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -33,6 +33,9 @@ struct Connection { public: explicit Connection(TCPsocket socket); ~Connection(); + // these have to be deleted, since a Socket can't be copied, since a copied socket would be closed by destruction of the original and vice versa + explicit Connection(Connection& connection) = delete; + [[nodiscard]] Connection& operator=(Connection& other) = delete; //TODO write a method to check if the connection was destroyed @@ -118,10 +121,15 @@ struct Server { public: explicit Server(TCPsocket socket); ~Server(); + // these have to be deleted, since a Socket can't be copied, since a copied socket would be closed by destruction of the original and vice versa + explicit Server(Server& server) = delete; + [[nodiscard]] Server& operator=(Server& other) = delete; + [[nodiscard]] tl::optional> try_get_client(); [[nodiscard]] tl::optional> get_client(Uint32 ms_delay = 100, std::size_t abort_after = 60 * 1000); + template //TODO add again //requires std::derived_from std::shared_ptr From 66c0a01b832aaf191ad2730d21d843c1e786690f Mon Sep 17 00:00:00 2001 From: Totto16 Date: Wed, 19 Apr 2023 00:25:44 +0200 Subject: [PATCH 45/45] refined Connection class docs --- src/network/connection_manager.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/network/connection_manager.hpp b/src/network/connection_manager.hpp index 42642f06..d837fd94 100644 --- a/src/network/connection_manager.hpp +++ b/src/network/connection_manager.hpp @@ -14,10 +14,10 @@ /** * @brief This is a wrapper for a client TCPSocket. It has convenience functions to interact with the socket and send serialized data over it, - * note that the type 'TCPsocket' is a typedef for some '_TCPsocket *' so it's a raw pointer. You can't a connection yourself, + * note that the type 'TCPsocket' is a typedef for some '_TCPsocket *' so it's a raw pointer. You shouldn't create a connection yourself, * to get one you have to call 'Server::try_get_client()' instead. That will handle everything for you automatically. - * If you would like to add some friend classes to do it manually, here are some notes about that: - * The lifetime of the underlying TCPsocket is managed by SDL and therefore it's closed and deallocated on Destruction of this class. + * If you would like to create a Connection manually, here are some notes about that: + * The lifetime of the underlying TCPsocket is managed by SDL functions and therefore it's closed and deallocated on Destruction of this class. * By passing in a socket you give the connection the ownership of this pointer! */ struct Connection {