From 067b53d8508b54677b7e20ca2aba5d98c0ba4836 Mon Sep 17 00:00:00 2001 From: dank_meme01 <42031238+dankmeme01@users.noreply.github.com> Date: Tue, 5 Dec 2023 20:34:49 +0100 Subject: [PATCH] server switcher + some big refactors --- CMakeLists.txt | 6 +- server/Cargo.toml | 11 +- server/central/Cargo.toml | 4 +- .../{game-derives => game-derive}/Cargo.toml | 2 +- .../{game-derives => game-derive}/src/lib.rs | 7 +- server/game/Cargo.toml | 2 +- server/game/src/data/mod.rs | 2 +- server/game/src/server_thread/handlers/mod.rs | 9 + server/protocol.md | 6 +- src/audio/audio_manager.hpp | 4 +- src/audio/audio_stream.cpp | 1 - src/audio/opus_codec.hpp | 3 +- src/audio/voice_playback_manager.hpp | 1 - src/crypto/base_box.hpp | 4 +- src/crypto/box.cpp | 4 +- src/crypto/box.hpp | 3 - src/crypto/secret_box.cpp | 3 +- src/crypto/secret_box.hpp | 2 - src/data/bitbuffer.hpp | 3 +- src/data/bytebuffer.cpp | 3 - src/data/bytebuffer.hpp | 9 +- src/data/packets/all.cpp | 1 + src/data/packets/all.hpp | 2 - src/data/packets/client/game.hpp | 1 - src/data/packets/client/misc.hpp | 1 - src/data/packets/packet.hpp | 3 +- src/data/packets/server/game.hpp | 1 - src/defs.hpp | 2 - src/defs/assert.hpp | 10 +- src/defs/basic.hpp | 5 +- src/managers/account_manager.cpp | 17 ++ src/managers/account_manager.hpp | 6 +- src/managers/central_server_manager.cpp | 146 +++++++++++++ src/managers/central_server_manager.hpp | 66 ++++++ src/managers/error_queues.hpp | 3 +- src/managers/game_server_manager.cpp | 129 ++++++++++++ src/managers/game_server_manager.hpp | 70 +++++++ src/managers/server_manager.cpp | 155 -------------- src/managers/server_manager.hpp | 86 -------- src/net/game_socket.cpp | 1 + src/net/game_socket.hpp | 1 + src/net/network_manager.cpp | 28 +-- src/net/network_manager.hpp | 19 +- src/net/udp_socket.cpp | 1 - src/ui/error_check_node.hpp | 2 +- src/ui/hooks/menu_layer.hpp | 4 +- src/ui/hooks/play_layer.hpp | 7 +- src/ui/menu/main/globed_menu_layer.cpp | 192 +++++++++--------- src/ui/menu/main/globed_menu_layer.hpp | 10 +- src/ui/menu/main/server_list_cell.cpp | 36 ++-- src/ui/menu/main/server_list_cell.hpp | 12 +- src/ui/menu/main/signup_layer.cpp | 4 +- src/ui/menu/main/signup_layer.hpp | 2 +- src/ui/menu/main/signup_popup.cpp | 37 ++-- src/ui/menu/main/signup_popup.hpp | 2 +- src/ui/menu/player_list/player_list_cell.cpp | 9 +- src/ui/menu/player_list/player_list_cell.hpp | 2 +- src/ui/menu/player_list/player_list_popup.cpp | 7 +- src/ui/menu/player_list/player_list_popup.hpp | 2 +- .../menu/server_switcher/add_server_popup.cpp | 125 ++++++++++++ .../menu/server_switcher/add_server_popup.hpp | 26 +++ .../server_switcher/direct_connect_popup.cpp | 81 ++++++++ .../server_switcher/direct_connect_popup.hpp | 17 ++ src/ui/menu/server_switcher/server_cell.cpp | 111 ++++++++++ src/ui/menu/server_switcher/server_cell.hpp | 23 +++ .../server_switcher/server_switcher_popup.cpp | 93 +++++++++ .../server_switcher/server_switcher_popup.hpp | 19 ++ .../server_switcher/server_test_popup.cpp | 111 ++++++++++ .../server_switcher/server_test_popup.hpp | 24 +++ src/util/collections.hpp | 13 ++ src/util/crypto.cpp | 5 +- src/util/crypto.hpp | 5 +- src/util/data.hpp | 3 +- src/util/debugging.cpp | 13 +- src/util/debugging.hpp | 14 +- src/util/formatting.cpp | 10 + src/util/formatting.hpp | 4 + src/util/net.cpp | 27 ++- src/util/net.hpp | 4 +- src/util/rng.hpp | 1 + src/util/sync.hpp | 6 +- src/util/time.hpp | 16 +- src/util/ui.cpp | 5 +- src/util/ui.hpp | 7 +- 84 files changed, 1394 insertions(+), 540 deletions(-) rename server/{game-derives => game-derive}/Cargo.toml (92%) rename server/{game-derives => game-derive}/src/lib.rs (97%) create mode 100644 src/managers/central_server_manager.cpp create mode 100644 src/managers/central_server_manager.hpp create mode 100644 src/managers/game_server_manager.cpp create mode 100644 src/managers/game_server_manager.hpp delete mode 100644 src/managers/server_manager.cpp delete mode 100644 src/managers/server_manager.hpp create mode 100644 src/ui/menu/server_switcher/add_server_popup.cpp create mode 100644 src/ui/menu/server_switcher/add_server_popup.hpp create mode 100644 src/ui/menu/server_switcher/direct_connect_popup.cpp create mode 100644 src/ui/menu/server_switcher/direct_connect_popup.hpp create mode 100644 src/ui/menu/server_switcher/server_cell.cpp create mode 100644 src/ui/menu/server_switcher/server_cell.hpp create mode 100644 src/ui/menu/server_switcher/server_switcher_popup.cpp create mode 100644 src/ui/menu/server_switcher/server_switcher_popup.hpp create mode 100644 src/ui/menu/server_switcher/server_test_popup.cpp create mode 100644 src/ui/menu/server_switcher/server_test_popup.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5510d9bf..2d0444e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,14 +4,14 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_OSX_ARCHITECTURES "x86_64") set(CMAKE_CXX_VISIBILITY_PRESET hidden) -# Enable LTO (2.5x less binary size, no noticable compile time hit) -set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) - project(globed2 VERSION 1.0.0) option(ENABLE_DEBUG "Debug mode" OFF) if (ENABLE_DEBUG) add_compile_definitions(GLOBED_DEBUG=1) +else() + # Enable LTO in release (2.5x less binary size, no noticable compile time hit) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) endif() file(GLOB_RECURSE SOURCES diff --git a/server/Cargo.toml b/server/Cargo.toml index eb053df4..9a999b51 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -1,10 +1,7 @@ [workspace] -members = ["central", "game", "game-derives", "shared"] +members = ["central", "game", "game-derive", "shared"] resolver = "2" -# my observation thus far with LTO: -# compile times -> ~100% increase -# executable size -> ~30% decrease -# performance -> too lazy to benchmark but probably a very minor improvement -# so.. good enough to keep! -# TODO Bring back in release + +# TODO bring back in release +[profile.release] # lto = "fat" diff --git a/server/central/Cargo.toml b/server/central/Cargo.toml index deb295c5..40e062d3 100644 --- a/server/central/Cargo.toml +++ b/server/central/Cargo.toml @@ -15,6 +15,8 @@ base64 = "0.21.5" blake2 = "0.10.6" digest = "0.10.7" hmac = "0.12.1" +ipnet = "2.9.0" +iprange = "0.6.7" rand = "0.8.5" reqwest = "0.11.22" roa = { version = "0.6.1", features = ["router"] } @@ -23,5 +25,3 @@ serde_json = "1.0.108" sha2 = "0.10.8" tokio = { version = "1.34.0", features = ["full"] } totp-rs = "5.4.0" -iprange = "0.6.7" -ipnet = "2.9.0" diff --git a/server/game-derives/Cargo.toml b/server/game-derive/Cargo.toml similarity index 92% rename from server/game-derives/Cargo.toml rename to server/game-derive/Cargo.toml index b17f30fd..4dcca8ce 100644 --- a/server/game-derives/Cargo.toml +++ b/server/game-derive/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "globed-derives" +name = "globed-derive" version = "1.0.0" edition = "2021" diff --git a/server/game-derives/src/lib.rs b/server/game-derive/src/lib.rs similarity index 97% rename from server/game-derives/src/lib.rs rename to server/game-derive/src/lib.rs index 3d8dc484..18e56bd2 100644 --- a/server/game-derives/src/lib.rs +++ b/server/game-derive/src/lib.rs @@ -24,7 +24,12 @@ pub fn derive_encodable(input: TokenStream) -> TokenStream { .fields .iter() .map(|field| { - let ident = field.ident.as_ref().unwrap(); + let Some(ident) = field.ident.as_ref() else { + return quote! { + compile_error!("Encodable cannot be derived for tuple structs"); + }; + }; + quote! { buf.write_value(&self.#ident); } diff --git a/server/game/Cargo.toml b/server/game/Cargo.toml index 04358e87..b4857133 100644 --- a/server/game/Cargo.toml +++ b/server/game/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] globed-shared = { path = "../shared" } -globed-derives = { path = "../game-derives" } +globed-derive = { path = "../game-derive" } alloca = "0.4.0" anyhow = "1.0.75" diff --git a/server/game/src/data/mod.rs b/server/game/src/data/mod.rs index 711c5ed0..19489473 100644 --- a/server/game/src/data/mod.rs +++ b/server/game/src/data/mod.rs @@ -4,6 +4,6 @@ pub mod types; /* re-export all important types, packets and macros */ pub use bytebufferext::*; -pub use globed_derives::*; +pub use globed_derive::*; pub use packets::*; pub use types::*; diff --git a/server/game/src/server_thread/handlers/mod.rs b/server/game/src/server_thread/handlers/mod.rs index c5457e60..e78bd6dd 100644 --- a/server/game/src/server_thread/handlers/mod.rs +++ b/server/game/src/server_thread/handlers/mod.rs @@ -68,6 +68,15 @@ macro_rules! gs_alloca_check_size { ($size:expr) => { let size = $size; if size > crate::server_thread::handlers::MAX_ALLOCA_SIZE { + // panic in debug, return an error in release mode + if cfg!(debug_assertions) { + panic!( + "attempted to allocate {} bytes on the stack - this is above the limit of {} and indicates a logic error.", + size, + crate::server_thread::handlers::MAX_ALLOCA_SIZE + ); + } + let err = crate::server_thread::error::PacketHandlingError::DangerousAllocation(size); return Err(err); } diff --git a/server/protocol.md b/server/protocol.md index 08a0fdd9..90345c9b 100644 --- a/server/protocol.md +++ b/server/protocol.md @@ -1,8 +1,10 @@ ## Protocol -this is a brief protocol description so that I don't forget what everything does :p +if you somehow stumbled upon this file, hi! this is a brief protocol description so that I don't forget what everything does :p -plus sign means encrypted packet +plus sign means encrypted packet. + +i will probably forget to update this very often ### Client diff --git a/src/audio/audio_manager.hpp b/src/audio/audio_manager.hpp index 14ac85bb..60e747dc 100644 --- a/src/audio/audio_manager.hpp +++ b/src/audio/audio_manager.hpp @@ -6,10 +6,10 @@ #include #include #include -#include -#include "opus_codec.hpp" + #include "audio_frame.hpp" #include "audio_sample_queue.hpp" +#include using util::sync::WrappingMutex; using util::sync::AtomicBool; diff --git a/src/audio/audio_stream.cpp b/src/audio/audio_stream.cpp index c5015d0e..b330a169 100644 --- a/src/audio/audio_stream.cpp +++ b/src/audio/audio_stream.cpp @@ -2,7 +2,6 @@ #if GLOBED_VOICE_SUPPORT -#include "audio_frame.hpp" #include "audio_manager.hpp" AudioStream::AudioStream() { diff --git a/src/audio/opus_codec.hpp b/src/audio/opus_codec.hpp index 9c828a89..db342e65 100644 --- a/src/audio/opus_codec.hpp +++ b/src/audio/opus_codec.hpp @@ -3,9 +3,10 @@ #if GLOBED_VOICE_SUPPORT +#include + #include #include -#include const size_t VOICE_MAX_BYTES_IN_FRAME = 1000; // on avg 200 bytes but y'know, just in case diff --git a/src/audio/voice_playback_manager.hpp b/src/audio/voice_playback_manager.hpp index 9d6f9558..4a7ea8f8 100644 --- a/src/audio/voice_playback_manager.hpp +++ b/src/audio/voice_playback_manager.hpp @@ -3,7 +3,6 @@ #if GLOBED_VOICE_SUPPORT -#include "audio_frame.hpp" #include "audio_stream.hpp" /* diff --git a/src/crypto/base_box.hpp b/src/crypto/base_box.hpp index 564478dd..8e8456de 100644 --- a/src/crypto/base_box.hpp +++ b/src/crypto/base_box.hpp @@ -1,8 +1,10 @@ #pragma once #include -#include + #include +#include + /* * This class contains no crypto implementation and is here just for boilerplate code. * Implementers must override: diff --git a/src/crypto/box.cpp b/src/crypto/box.cpp index 4e3c20cb..f5d322d3 100644 --- a/src/crypto/box.cpp +++ b/src/crypto/box.cpp @@ -1,9 +1,9 @@ #include "box.hpp" -#include -#include // std::runtime_error #include // std::memcpy +#include + using namespace util::data; CryptoBox::CryptoBox(byte* key) { diff --git a/src/crypto/box.hpp b/src/crypto/box.hpp index 5e664abf..74caafd2 100644 --- a/src/crypto/box.hpp +++ b/src/crypto/box.hpp @@ -1,11 +1,8 @@ #pragma once #include "base_box.hpp" -#include -#include #include - class CryptoBox : public BaseCryptoBox { public: diff --git a/src/crypto/secret_box.cpp b/src/crypto/secret_box.cpp index bd164c34..d9536458 100644 --- a/src/crypto/secret_box.cpp +++ b/src/crypto/secret_box.cpp @@ -1,8 +1,9 @@ #include "secret_box.hpp" -#include #include // std::memcpy +#include + using namespace util::data; SecretBox::SecretBox(bytevector key) { diff --git a/src/crypto/secret_box.hpp b/src/crypto/secret_box.hpp index 2efb5b7d..bf80b67b 100644 --- a/src/crypto/secret_box.hpp +++ b/src/crypto/secret_box.hpp @@ -1,7 +1,5 @@ #pragma once #include "base_box.hpp" -#include -#include #include diff --git a/src/data/bitbuffer.hpp b/src/data/bitbuffer.hpp index f79b871f..2ecf37fd 100644 --- a/src/data/bitbuffer.hpp +++ b/src/data/bitbuffer.hpp @@ -1,8 +1,9 @@ #pragma once +#include + #include #include #include -#include /* * BitBuffer - a simple interface that allows you to read/write bits diff --git a/src/data/bytebuffer.cpp b/src/data/bytebuffer.cpp index 549d3070..ea3a893b 100644 --- a/src/data/bytebuffer.cpp +++ b/src/data/bytebuffer.cpp @@ -116,7 +116,6 @@ void ByteBuffer::writeBytes(const bytevector& vec) { } /* cocos/gd */ -#ifndef GLOBED_ROOT_NO_GEODE cocos2d::ccColor3B ByteBuffer::readColor3() { auto r = this->readU8(); @@ -157,8 +156,6 @@ void ByteBuffer::writePoint(cocos2d::CCPoint point) { this->writeF32(point.y); } -#endif // GLOBED_ROOT_NO_GEODE - bytevector ByteBuffer::getData() const { return _data; } diff --git a/src/data/bytebuffer.hpp b/src/data/bytebuffer.hpp index 2e4221e5..cc13bfe2 100644 --- a/src/data/bytebuffer.hpp +++ b/src/data/bytebuffer.hpp @@ -1,9 +1,11 @@ #pragma once -#include "bitbuffer.hpp" #include -#include + #include +#include "bitbuffer.hpp" +#include + class ByteBuffer; // Represents a data type that can be easily written to a ByteBuffer @@ -209,6 +211,7 @@ class ByteBuffer { // Write a list of `Encodable` objects, prefixed with 4 bytes indicating the count. template void writeValueVector(const std::vector& values) { + this->writeU32(values.size()); for (const T& value : values) { value.encode(*this); } @@ -297,7 +300,6 @@ class ByteBuffer { return static_cast(this->readPrimitive

()); } -#ifndef GLOBED_ROOT_NO_GEODE /* * Cocos/GD serializable methods */ @@ -315,7 +317,6 @@ class ByteBuffer { void writeColor4(cocos2d::ccColor4B color); // Write a CCPoint (2 floats) void writePoint(cocos2d::CCPoint point); -#endif /* * Misc util functions diff --git a/src/data/packets/all.cpp b/src/data/packets/all.cpp index ab721872..5e8acb0a 100644 --- a/src/data/packets/all.cpp +++ b/src/data/packets/all.cpp @@ -11,6 +11,7 @@ std::shared_ptr matchPacket(packetid_t packetId) { PACKET(KeepaliveResponsePacket); PACKET(ServerDisconnectPacket); PACKET(LoggedInPacket); + PACKET(LoginFailedPacket); PACKET(ServerNoticePacket); // game related diff --git a/src/data/packets/all.hpp b/src/data/packets/all.hpp index e0312504..da42be61 100644 --- a/src/data/packets/all.hpp +++ b/src/data/packets/all.hpp @@ -13,8 +13,6 @@ */ #pragma once -#include - #include "packet.hpp" #include "client/connection.hpp" diff --git a/src/data/packets/client/game.hpp b/src/data/packets/client/game.hpp index 665f0f54..2a615a5b 100644 --- a/src/data/packets/client/game.hpp +++ b/src/data/packets/client/game.hpp @@ -1,5 +1,4 @@ #pragma once -#include #include #include diff --git a/src/data/packets/client/misc.hpp b/src/data/packets/client/misc.hpp index f80be63a..45f46ca0 100644 --- a/src/data/packets/client/misc.hpp +++ b/src/data/packets/client/misc.hpp @@ -1,5 +1,4 @@ #pragma once -#include #include #include diff --git a/src/data/packets/packet.hpp b/src/data/packets/packet.hpp index 1aaa021c..52e6b462 100644 --- a/src/data/packets/packet.hpp +++ b/src/data/packets/packet.hpp @@ -1,6 +1,5 @@ #pragma once #include -#include using packetid_t = uint16_t; #define GLOBED_PACKET(id,enc) \ @@ -48,7 +47,7 @@ class PacketHeader { id = buf.readU16(); encrypted = buf.readBool(); } - + packetid_t id; bool encrypted; }; \ No newline at end of file diff --git a/src/data/packets/server/game.hpp b/src/data/packets/server/game.hpp index 1287dfeb..6ce8d7fd 100644 --- a/src/data/packets/server/game.hpp +++ b/src/data/packets/server/game.hpp @@ -1,5 +1,4 @@ #pragma once -#include #include #include diff --git a/src/defs.hpp b/src/defs.hpp index 55d5c2b7..10ca13a8 100644 --- a/src/defs.hpp +++ b/src/defs.hpp @@ -1,6 +1,4 @@ #pragma once -#include - #include #include diff --git a/src/defs/assert.hpp b/src/defs/assert.hpp index 572bce9e..067d1101 100644 --- a/src/defs/assert.hpp +++ b/src/defs/assert.hpp @@ -25,11 +25,7 @@ * GLOBED_UNIMPL - throws a runtime error as the method was not implemented and isn't meant to be called */ -#ifndef GLOBED_ROOT_NO_GEODE -# define GLOBED_REQUIRE_LOG geode::log::error -#endif - -#if GLOBED_CAN_USE_SOURCE_LOCATION && !defined(GLOBED_ROOT_NO_GEODE) +#if GLOBED_CAN_USE_SOURCE_LOCATION # define GLOBED_REQUIRE(condition,message) \ if (!(condition)) [[unlikely]] { \ auto ev_msg = (message); \ @@ -48,13 +44,13 @@ # define GLOBED_REQUIRE(condition,message) \ if (!(condition)) [[unlikely]] { \ auto ev_msg = (message); \ - GLOBED_REQUIRE_LOG(std::string("Condition failed: ") + ev_msg); \ + geode::log::error(std::string("Condition failed: ") + ev_msg); \ throw std::runtime_error(std::string(ev_msg)); \ } # define GLOBED_HARD_ASSERT(condition,message) \ if (!(condition)) [[unlikely]] { \ auto ev_msg = (message); \ - GLOBED_REQUIRE_LOG(std::string("Condition failed: ") + ev_msg); \ + geode::log::error(std::string("Condition failed: ") + ev_msg); \ GLOBED_SUICIDE; \ } #endif diff --git a/src/defs/basic.hpp b/src/defs/basic.hpp index d9519a03..d0d3a74c 100644 --- a/src/defs/basic.hpp +++ b/src/defs/basic.hpp @@ -1,6 +1,3 @@ #pragma once #include - -#ifndef GLOBED_ROOT_NO_GEODE -# include -#endif \ No newline at end of file +#include \ No newline at end of file diff --git a/src/managers/account_manager.cpp b/src/managers/account_manager.cpp index bc9a0f74..8379055c 100644 --- a/src/managers/account_manager.cpp +++ b/src/managers/account_manager.cpp @@ -1,5 +1,7 @@ #include "account_manager.hpp" +#include +#include #include GLOBED_SINGLETON_DEF(GlobedAccountManager) @@ -20,6 +22,21 @@ void GlobedAccountManager::initialize(const std::string& name, int accountId, co initialized = true; } +void GlobedAccountManager::autoInitialize() { + auto* gjam = GJAccountManager::get(); + auto& csm = CentralServerManager::get(); + auto& gsm = GameServerManager::get(); + + std::string activeCentralUrl = ""; + + auto activeCentral = csm.getActive(); + if (activeCentral) { + activeCentralUrl = activeCentral.value().url; + } + + this->initialize(gjam->m_username, gjam->m_accountID, gjam->getGJP(), activeCentralUrl); +} + std::string GlobedAccountManager::generateAuthCode() { GLOBED_REQUIRE(initialized, "Attempting to call GlobedAccountManager::generateAuthCode before initializing the instance") diff --git a/src/managers/account_manager.hpp b/src/managers/account_manager.hpp index 4ab40d83..f251e7ed 100644 --- a/src/managers/account_manager.hpp +++ b/src/managers/account_manager.hpp @@ -1,6 +1,6 @@ #pragma once - #include + #include #include @@ -21,12 +21,14 @@ class GlobedAccountManager { util::sync::AtomicBool initialized = false; util::sync::WrappingMutex gdData; util::sync::WrappingMutex authToken; - + GlobedAccountManager(); // This method can be called multiple times, and in fact it is even advised that you do so often. // It must be called at least once before calling any other method or they will throw an exception. void initialize(const std::string& name, int accountId, const std::string& gjp, const std::string& central); + // Grabs the values from other manager classes and calls `initialize` for you. + void autoInitialize(); void storeAuthKey(const util::data::byte* source, size_t size); void storeAuthKey(const util::data::bytevector& source); diff --git a/src/managers/central_server_manager.cpp b/src/managers/central_server_manager.cpp new file mode 100644 index 00000000..619c8c4c --- /dev/null +++ b/src/managers/central_server_manager.cpp @@ -0,0 +1,146 @@ +#include "central_server_manager.hpp" + +#include + +GLOBED_SINGLETON_DEF(CentralServerManager) + +CentralServerManager::CentralServerManager() { + this->reload(); + + // if empty (as it would be by default), add our server + + bool empty = _servers.lock()->empty(); + + if (empty) { + this->addServer(CentralServer { + .name = "Main server", + // .url = "https://globed.dankmeme.dev" + // TODO prod ^ + .url = "http://127.0.0.1:41000" + }); + } + + // if we have a stored active server, use it. otherwise use idx 0 + + _activeIdx = 0; + + auto storedActive = geode::Mod::get()->getSavedValue(ACTIVE_SERVER_KEY); + if (!storedActive.empty()) { + int idx = std::stoi(storedActive); + + if (idx > 0 && idx < _servers.lock()->size()) { + this->setActive(idx); + } + } +} + +void CentralServerManager::setActive(int index) { + _activeIdx = index; + geode::Mod::get()->setSavedValue(ACTIVE_SERVER_KEY, std::to_string(index)); +} + +std::optional CentralServerManager::getActive() { + int idx = _activeIdx.load(); + + if (idx == STANDALONE_IDX) { + return CentralServer { + .name = "Standalone server", + .url = "__standalone__url__sub__", + }; + } + + auto servers = _servers.lock(); + + if (idx < 0 || idx >= servers->size()) { + return std::nullopt; + } + + return servers->at(idx); +} + +std::vector CentralServerManager::getAllServers() { + return *_servers.lock(); +} + +CentralServer CentralServerManager::getServer(int index) { + return _servers.lock()->at(index); +} + +void CentralServerManager::setStandalone(bool status) { + this->setActive(status ? STANDALONE_IDX : 0); +} + +bool CentralServerManager::standalone() { + return _activeIdx == STANDALONE_IDX; +} + +size_t CentralServerManager::count() { + return _servers.lock()->size(); +} + +void CentralServerManager::addServer(const CentralServer& data) { + _servers.lock()->push_back(data); + this->save(); +} + +void CentralServerManager::removeServer(int index) { + auto servers = _servers.lock(); + + if (index < 0 || index >= servers->size()) { + return; + } + + // we may want to recalculate the active server index if there's an active server right now + + int active = _activeIdx.load(); + if (active >= 0 && active < servers->size() && active >= index) { + _activeIdx = active - 1; + } + + servers->erase(servers->begin() + index); +} + +void CentralServerManager::modifyServer(int index, const CentralServer& data) { + auto servers = _servers.lock(); + + if (index < 0 || index >= servers->size()) { + return; + } + + servers->at(index) = data; + servers.unlock(); + + this->save(); +} + +void CentralServerManager::reload() { + auto servers = _servers.lock(); + servers->clear(); + + try { + auto b64value = geode::Mod::get()->getSavedValue(SETTING_KEY); + if (b64value.empty()) return; + + auto decoded = util::crypto::base64Decode(b64value); + if (decoded.empty()) return; + + ByteBuffer buf(decoded); + *servers = buf.readValueVector(); + } catch (const std::exception& e) { + ErrorQueues::get().warn(std::string("failed to load servers: ") + e.what()); + } +} + +// lmao at this point i decided it's easier to just binary encode it than mess with base64 string concatenation +void CentralServerManager::save() { + auto servers = _servers.lock(); + + ByteBuffer buf; + buf.writeValueVector(*servers); + auto data = util::crypto::base64Encode(buf.getDataRef()); + + geode::Mod::get()->setSavedValue(SETTING_KEY, data); + + geode::Mod::get()->setSavedValue(ACTIVE_SERVER_KEY, std::to_string(_activeIdx.load())); +} + diff --git a/src/managers/central_server_manager.hpp b/src/managers/central_server_manager.hpp new file mode 100644 index 00000000..93ff825d --- /dev/null +++ b/src/managers/central_server_manager.hpp @@ -0,0 +1,66 @@ +#pragma once +#include + +#include // mutex +#include // base64 +#include + +struct CentralServer { + std::string name; + std::string url; + + GLOBED_ENCODE { + buf.writeString(name); + buf.writeString(url); + } + + GLOBED_DECODE { + name = buf.readString(); + url = buf.readString(); + } +}; + +class CentralServerManager { +public: + GLOBED_SINGLETON(CentralServerManager); + CentralServerManager(); + + // set the current active server, thread safe + void setActive(int index); + // get the current active server, thread safe + std::optional getActive(); + + // get all central servers, thread safe + std::vector getAllServers(); + // get a central server at index, thread safe + CentralServer getServer(int index); + + // set the current active server to an emulated central server instance for a standalone game server + void setStandalone(bool status = true); + bool standalone(); + + // get the total amount of servers + size_t count(); + + // add a new central server and save. only call from main thread + void addServer(const CentralServer& data); + // remove a central server and save. only call from main thread + void removeServer(int index); + // modify a central server and save. only call from main thread + void modifyServer(int index, const CentralServer& data); + + // reload all saved central servers, only call from main thread + void reload(); + + util::sync::AtomicBool recentlySwitched = false; + +protected: + util::sync::WrappingMutex> _servers; + util::sync::AtomicI32 _activeIdx = -1; + + constexpr static const char* SETTING_KEY = "_central-server-list"; + constexpr static const char* ACTIVE_SERVER_KEY = "_central-server-active"; + constexpr static int STANDALONE_IDX = -2; + + void save(); +}; \ No newline at end of file diff --git a/src/managers/error_queues.hpp b/src/managers/error_queues.hpp index 51521f9f..b6a408f4 100644 --- a/src/managers/error_queues.hpp +++ b/src/managers/error_queues.hpp @@ -1,7 +1,8 @@ #pragma once #include + #include -#include + #include /* diff --git a/src/managers/game_server_manager.cpp b/src/managers/game_server_manager.cpp new file mode 100644 index 00000000..122515c9 --- /dev/null +++ b/src/managers/game_server_manager.cpp @@ -0,0 +1,129 @@ +#include "game_server_manager.hpp" + +#include +#include +#include + +GLOBED_SINGLETON_DEF(GameServerManager) +GameServerManager::GameServerManager() {} + +void GameServerManager::addServer(const std::string& serverId, const std::string& name, const std::string& address, const std::string& region) { + auto addr = util::net::splitAddress(address, 41001); + GameServer server = { + .id = serverId, + .name = name, + .region = region, + .address = GameServerAddress { + .ip = addr.first, + .port = addr.second + }, + .ping = -1, + .playerCount = 0, + }; + + auto data = _data.lock(); + data->servers.insert(std::make_pair(serverId, server)); +} + +void GameServerManager::clear() { + _data.lock()->servers.clear(); +} + +size_t GameServerManager::count() { + return _data.lock()->servers.size(); +} + +void GameServerManager::setActive(const std::string id) { + auto data = _data.lock(); + data->active = id; +} + +void GameServerManager::clearActive() { + auto data = _data.lock(); + data->active.clear(); +} + +std::string GameServerManager::active() { + return _data.lock()->active; +} + +std::optional GameServerManager::getActiveServer() { + auto active = this->active(); + if (active.empty()) { + return std::nullopt; + } + + return this->getServer(active); +} + +GameServer GameServerManager::getServer(const std::string& id) { + auto data = _data.lock(); + if (!data->servers.contains(id)) { + throw std::runtime_error(std::string("invalid server ID, no such server exists: ") + id); + } + + return data->servers.at(id).server; +} + +std::unordered_map GameServerManager::getAllServers() { + auto data = _data.lock(); + auto values = util::collections::mapValuesBorrowed(data->servers); + + std::unordered_map out; + + for (auto* gsd : values) { + out.insert(std::make_pair(gsd->server.id, gsd->server)); + } + + return out; +} + + +uint32_t GameServerManager::startPing(const std::string& serverId) { + auto pingId = util::rng::Random::get().generate(); + + auto data = _data.lock(); + auto& gsdata = data->servers.at(serverId); + + if (gsdata.pendingPings.size() > 50) { + geode::log::warn("over 50 pending pings for the game server {}, clearing", serverId); + gsdata.pendingPings.clear(); + } + + auto now = util::time::now(); + gsdata.pendingPings[pingId] = now; + + return pingId; +} + +void GameServerManager::finishPing(uint32_t pingId, uint32_t playerCount) { + auto now = util::time::now(); + + auto data = _data.lock(); + + for (auto* server : util::collections::mapValuesBorrowed(data->servers)) { + if (server->pendingPings.contains(pingId)) { + auto start = server->pendingPings.at(pingId); + auto timeTook = util::time::asMillis(now - start); + + server->server.ping = timeTook; + server->server.playerCount = playerCount; + server->pendingPings.erase(pingId); + return; + } + } +} + +void GameServerManager::startKeepalive() { + std::string active = _data.lock()->active; + + if (!active.empty()) { + auto pingId = this->startPing(active); + _data.lock()->activePingId = pingId; + } +} + +void GameServerManager::finishKeepalive(uint32_t playerCount) { + uint32_t activePingId = _data.lock()->activePingId; + this->finishPing(activePingId, playerCount); +} diff --git a/src/managers/game_server_manager.hpp b/src/managers/game_server_manager.hpp new file mode 100644 index 00000000..d97ee5ca --- /dev/null +++ b/src/managers/game_server_manager.hpp @@ -0,0 +1,70 @@ +#pragma once +#include + +#include + +#include // mutex +#include // base64 +#include + +struct GameServerAddress { + std::string ip; + unsigned short port; +}; + +struct GameServer { + std::string id; + std::string name; + std::string region; + + GameServerAddress address; + + int ping; + uint32_t playerCount; +}; + +// This class is fully thread safe to use. +class GameServerManager { +public: + GLOBED_SINGLETON(GameServerManager); + GameServerManager(); + + constexpr static const char* STANDALONE_ID = "__standalone__server_id__"; + + util::sync::AtomicBool pendingChanges; + + void addServer(const std::string& serverId, const std::string& name, const std::string& address, const std::string& region); + void clear(); + size_t count(); + + void setActive(const std::string id); + std::string active(); + void clearActive(); + + std::optional getActiveServer(); + GameServer getServer(const std::string& id); + std::unordered_map getAllServers(); + + /* pings */ + + uint32_t startPing(const std::string& serverId); + void finishPing(uint32_t pingId, uint32_t playerCount); + + void startKeepalive(); + void finishKeepalive(uint32_t playerCount); + +protected: + // expansion of GameServer with pending pings + struct GameServerData { + GameServer server; + std::unordered_map pendingPings; + }; + + struct InnerData { + std::unordered_map servers; + std::string active; // current game server ID + uint32_t activePingId; + }; + + util::sync::WrappingMutex _data; +}; \ No newline at end of file diff --git a/src/managers/server_manager.cpp b/src/managers/server_manager.cpp deleted file mode 100644 index 60f4a49b..00000000 --- a/src/managers/server_manager.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "server_manager.hpp" -#include -#include -#include - -GLOBED_SINGLETON_DEF(GlobedServerManager) - -GlobedServerManager::GlobedServerManager() { - auto storedActive = geode::Mod::get()->getSavedValue("active-central-server"); - if (storedActive.empty()) { - // storedActive = "https://globed.dankmeme.dev"; - // TODO prod ^^^ - storedActive = "http://192.168.0.100:41000"; - } - - _data.lock()->central = storedActive; -} - -void GlobedServerManager::setCentral(std::string address) { - if (!address.empty() && address.ends_with('/')) { - address.pop_back(); - } - - geode::Mod::get()->setSavedValue("active-central-server", address); - - auto data = _data.lock(); - data->central = address; - data->servers.clear(); -} - -std::string GlobedServerManager::getCentral() { - return _data.lock()->central; -} - -void GlobedServerManager::addGameServer(const std::string& serverId, const std::string& name, const std::string& address, const std::string& region) { - auto addr = util::net::splitAddress(address); - _data.lock()->servers[serverId] = GameServerInfo { - .name = name, - .region = region, - .address = {.ip = addr.first, .port = addr.second}, - .ping = -1, - .playerCount = 0, - }; -} - -void GlobedServerManager::setActiveGameServer(const std::string& serverId) { - _data.lock()->game = serverId; -} - -std::string GlobedServerManager::getActiveGameServer() { - return _data.lock()->game; -} - -void GlobedServerManager::clearGameServers() { - auto data = _data.lock(); - data->servers.clear(); - data->activePingId = 0; - data->game = ""; -} - -size_t GlobedServerManager::gameServerCount() { - return _data.lock()->servers.size(); -} - -uint32_t GlobedServerManager::pingStart(const std::string& serverId) { - uint32_t pingId = util::rng::Random::get().generate(); - - auto data = _data.lock(); - auto& gsi = data->servers.at(serverId); - - if (gsi.pendingPings.size() > 50) { - geode::log::warn("over 50 pending pings for the game server {}, clearing", serverId); - gsi.pendingPings.clear(); - } - - auto now = util::time::sinceEpoch(); - gsi.pendingPings[pingId] = now; - - return pingId; -} - -void GlobedServerManager::pingStartActive() { - std::string gameServer = _data.lock()->game; - - if (!gameServer.empty()) { - auto pingId = this->pingStart(gameServer); - _data.lock()->activePingId = pingId; - } -} - -void GlobedServerManager::pingFinish(uint32_t pingId, uint32_t playerCount) { - auto now = util::time::sinceEpoch(); - - auto data = _data.lock(); - for (auto* server : util::collections::mapValuesBorrowed(data->servers)) { - if (server->pendingPings.contains(pingId)) { - auto start = server->pendingPings.at(pingId); - auto timeTook = now - start; - server->ping = chrono::duration_cast(timeTook).count(); - server->playerCount = playerCount; - server->pingHistory.push(timeTook); - server->pendingPings.erase(pingId); - return; - } - } - - geode::log::warn("Ping ID doesn't exist in any known server: {}", pingId); -} - -void GlobedServerManager::pingFinishActive(uint32_t playerCount) { - uint32_t pingId = _data.lock()->activePingId; - - this->pingFinish(pingId, playerCount); -} - -GameServerView GlobedServerManager::getGameServer(const std::string& serverId) { - auto data = _data.lock(); - auto& gsi = data->servers.at(serverId); - return GameServerView { - .id = serverId, - .name = gsi.name, - .region = gsi.region, - .address = gsi.address, - .ping = gsi.ping, - .playerCount = gsi.playerCount - }; -} - -std::vector GlobedServerManager::getPingHistory(const std::string& serverId) { - auto data = _data.lock(); - auto& gsi = data->servers.at(serverId); - return gsi.pingHistory.extract(); -} - -std::unordered_map GlobedServerManager::extractGameServers() { - std::unordered_map out; - - auto data = _data.lock(); - for (const auto& [serverId, gsi] : data->servers) { - if (serverId == STANDALONE_SERVER_ID) { - continue; - } - - out[serverId] = GameServerView { - .id = serverId, - .name = gsi.name, - .region = gsi.region, - .address = gsi.address, - .ping = gsi.ping, - .playerCount = gsi.playerCount, - }; - } - - return out; -} \ No newline at end of file diff --git a/src/managers/server_manager.hpp b/src/managers/server_manager.hpp deleted file mode 100644 index 46a7528b..00000000 --- a/src/managers/server_manager.hpp +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once -#include -#include -#include -#include - -#include - -struct GameServerAddress { - std::string ip; - unsigned short port; -}; - -struct GameServerInfo { - std::string name; - std::string region; - - GameServerAddress address; - - int ping; - std::unordered_map pendingPings; - util::collections::CappedQueue pingHistory; - - uint32_t playerCount; -}; - -// provides a view with a bit less data than GameServerInfo -struct GameServerView { - std::string id; - std::string name; - std::string region; - - GameServerAddress address; - - int ping; - uint32_t playerCount; -}; - -// This class is fully thread safe to use. -class GlobedServerManager { -public: - GLOBED_SINGLETON(GlobedServerManager); - GlobedServerManager(); - - static constexpr const char* STANDALONE_SERVER_ID = "__standalone_gs__"; - - /* central server control */ - - void setCentral(std::string address); - std::string getCentral(); - - /* game servers */ - void addGameServer(const std::string& serverId, const std::string& name, const std::string& address, const std::string& region); - void setActiveGameServer(const std::string& serverId); - std::string getActiveGameServer(); - void clearGameServers(); - - size_t gameServerCount(); - - // start a ping on the active game server - void pingStartActive(); - // finish a ping on the active game server - void pingFinishActive(uint32_t playerCount); - - // start a ping on a server given an id - uint32_t pingStart(const std::string& serverId); - // finish a ping on a server given an id - void pingFinish(uint32_t pingId, uint32_t playerCount); - - GameServerView getGameServer(const std::string& serverId); - std::vector getPingHistory(const std::string& serverId); - - std::unordered_map extractGameServers(); - - util::sync::AtomicBool pendingChanges; - -private: - struct InnerData { - std::unordered_map servers; - std::string central; // current central url - std::string game; // current game server ID - uint32_t activePingId; - }; - - util::sync::WrappingMutex _data; -}; \ No newline at end of file diff --git a/src/net/game_socket.cpp b/src/net/game_socket.cpp index 25c2c731..c7369720 100644 --- a/src/net/game_socket.cpp +++ b/src/net/game_socket.cpp @@ -1,4 +1,5 @@ #include "game_socket.hpp" + #include #include #include diff --git a/src/net/game_socket.hpp b/src/net/game_socket.hpp index 7a030d84..0ceb00cf 100644 --- a/src/net/game_socket.hpp +++ b/src/net/game_socket.hpp @@ -1,5 +1,6 @@ #pragma once #include "udp_socket.hpp" + #include #include diff --git a/src/net/network_manager.cpp b/src/net/network_manager.cpp index ba8dd60e..8966a8cf 100644 --- a/src/net/network_manager.cpp +++ b/src/net/network_manager.cpp @@ -34,7 +34,7 @@ NetworkManager::NetworkManager() { }); addBuiltinListener([this](auto packet) { - GlobedServerManager::get().pingFinishActive(packet->playerCount); + GameServerManager::get().finishKeepalive(packet->playerCount); }); addBuiltinListener([this](auto packet) { @@ -112,20 +112,22 @@ void NetworkManager::connect(const std::string& addr, unsigned short port, bool this->send(packet); } -void NetworkManager::connectWithView(const GameServerView& gsview) { +void NetworkManager::connectWithView(const GameServer& gsview) { try { this->connect(gsview.address.ip, gsview.address.port); - GlobedServerManager::get().setActiveGameServer(gsview.id); + GameServerManager::get().setActive(gsview.id); } catch (const std::exception& e) { this->disconnect(true); ErrorQueues::get().error(std::string("Connection failed: ") + e.what()); } } -void NetworkManager::connectStandalone(const std::string& addr, unsigned short port) { +void NetworkManager::connectStandalone() { + auto server = GameServerManager::get().getServer(GameServerManager::STANDALONE_ID); + try { - this->connect(addr, port); - GlobedServerManager::get().setActiveGameServer(GlobedServerManager::STANDALONE_SERVER_ID); + this->connect(server.address.ip, server.address.port, true); + GameServerManager::get().setActive(GameServerManager::STANDALONE_ID); } catch (const std::exception& e) { this->disconnect(true); ErrorQueues::get().error(std::string("Connection failed: ") + e.what()); @@ -149,7 +151,7 @@ void NetworkManager::disconnect(bool quiet) { gameSocket.disconnect(); gameSocket.cleanupBox(); - GlobedServerManager::get().setActiveGameServer(""); + GameServerManager::get().clearActive(); } void NetworkManager::send(std::shared_ptr packet) { @@ -187,14 +189,14 @@ void NetworkManager::threadMainFunc() { for (const auto& task : taskQueue.popAll()) { if (task == NetworkThreadTask::PingServers) { - auto& sm = GlobedServerManager::get(); - auto activeServer = sm.getActiveGameServer(); + auto& sm = GameServerManager::get(); + auto activeServer = sm.active(); - for (auto& [serverId, server] : sm.extractGameServers()) { + for (auto& [serverId, server] : sm.getAllServers()) { if (serverId == activeServer) continue; try { - auto pingId = sm.pingStart(serverId); + auto pingId = sm.startPing(serverId); gameSocket.sendPacketTo(PingPacket::create(pingId), server.address.ip, server.address.port); } catch (const std::exception& e) { ErrorQueues::get().warn(e.what()); @@ -264,7 +266,7 @@ void NetworkManager::threadRecvFunc() { void NetworkManager::handlePingResponse(std::shared_ptr packet) { if (PingResponsePacket* pingr = dynamic_cast(packet.get())) { - GlobedServerManager::get().pingFinish(pingr->id, pingr->playerCount); + GameServerManager::get().finishPing(pingr->id, pingr->playerCount); } } @@ -274,7 +276,7 @@ void NetworkManager::maybeSendKeepalive() { if ((now - lastKeepalive) > KEEPALIVE_INTERVAL) { lastKeepalive = now; this->send(KeepalivePacket::create()); - GlobedServerManager::get().pingStartActive(); + GameServerManager::get().startKeepalive(); } } } diff --git a/src/net/network_manager.hpp b/src/net/network_manager.hpp index abe0b390..0be75b18 100644 --- a/src/net/network_manager.hpp +++ b/src/net/network_manager.hpp @@ -1,15 +1,15 @@ #pragma once #include "game_socket.hpp" -#include - -#include -#include -#include #include #include #include +#include +#include +#include +#include + using namespace util::sync; enum class NetworkThreadTask { @@ -43,10 +43,11 @@ class NetworkManager { // Connect to a server void connect(const std::string& addr, unsigned short port, bool standalone = false); - // Safer version of `connect`, sets the active game server in `GlobedServerManager` doesn't throw an exception on error - void connectWithView(const GameServerView& gsview); - // Is similar to `connectWithView` (does not throw exceptions) and is made specifically for standalone servers. - void connectStandalone(const std::string& addr, unsigned short port); + // Safer version of `connect`, sets the active game server in `GameServerManager` on success, doesn't throw on exception on error + void connectWithView(const GameServer& gsview); + // Is similar to `connectWithView` (does not throw exceptions) but is made specifically for standalone servers. + // Grabs the address from the first server in `GameServerManager` + void connectStandalone(); // Disconnect from a server. Does nothing if not connected void disconnect(bool quiet = false); diff --git a/src/net/udp_socket.cpp b/src/net/udp_socket.cpp index da16e29a..cdf8eb14 100644 --- a/src/net/udp_socket.cpp +++ b/src/net/udp_socket.cpp @@ -1,5 +1,4 @@ #include "udp_socket.hpp" -#include #include UdpSocket::UdpSocket() : socket_(0) { diff --git a/src/ui/error_check_node.hpp b/src/ui/error_check_node.hpp index 67ceeb15..292a0a63 100644 --- a/src/ui/error_check_node.hpp +++ b/src/ui/error_check_node.hpp @@ -1,5 +1,5 @@ #pragma once -#include +#include class ErrorCheckNode : public cocos2d::CCNode { public: diff --git a/src/ui/hooks/menu_layer.hpp b/src/ui/hooks/menu_layer.hpp index bcd0c8f5..2b109fb7 100644 --- a/src/ui/hooks/menu_layer.hpp +++ b/src/ui/hooks/menu_layer.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include +#include #include #include diff --git a/src/ui/hooks/play_layer.hpp b/src/ui/hooks/play_layer.hpp index d332348a..3d392134 100644 --- a/src/ui/hooks/play_layer.hpp +++ b/src/ui/hooks/play_layer.hpp @@ -1,15 +1,12 @@ #pragma once -#include -#include - #include +#include #if GLOBED_HAS_KEYBINDS -#include +# include #endif // GLOBED_HAS_KEYBINDS #include