diff --git a/.clangd b/.clangd index 3c9792ea..8847112d 100644 --- a/.clangd +++ b/.clangd @@ -6,6 +6,8 @@ Diagnostics: Includes: IgnoreHeader: - \.compat\.inc +CompileFlags: + CompilationDatabase: engine/ --- # Header-specific conditions. @@ -20,11 +22,4 @@ CompileFlags: - -Wno-unused-const-variable - -Wno-unused-function - -Wno-unused-variable ---- -# Suppress all third-party warnings. -If: - PathMatch: thirdparty/.* - -Diagnostics: - Suppress: "*" diff --git a/flatscreen-project/project.godot b/flatscreen-project/project.godot index 9acb939c..1b3cee73 100644 --- a/flatscreen-project/project.godot +++ b/flatscreen-project/project.godot @@ -9,6 +9,8 @@ config_version=5 [application] + config/name="you_done_it" +run/main_scene="uid://dosb4sb7pvss4" config/features=PackedStringArray("4.5", "Forward Plus") config/icon="res://icon.svg" diff --git a/justfile b/justfile index ce702248..3b1bc6f6 100644 --- a/justfile +++ b/justfile @@ -6,7 +6,7 @@ compiledb := if tree_hash == `cat .tree.hash` { "no" } else { "yes" } build: format # Compiling Editor - cd engine/ && scons target=editor symbols=yes optimization=debug dev_build=yes linker=mold use_llvm=yes compiledb={{compiledb}} custom_modules="../modules" + cd engine/ && scons disable_exceptions=false target=editor symbols=yes optimization=debug dev_build=yes linker=mold use_llvm=yes compiledb={{compiledb}} custom_modules="../modules" echo {{tree_hash}} > .tree.hash run: build @@ -15,7 +15,7 @@ run: build release-linux: build # Compiling Linux Release - cd engine/ && scons platform=linuxbsd target=template_release arch=x86_64 linker=mold use_llvm=yes custom_modules="../modules" + cd engine/ && scons disable_exceptions=false platform=linuxbsd target=template_release arch=x86_64 linker=mold use_llvm=yes custom_modules="../modules" # Preparing Build Environment sed -i "s!templatepath!{{`realpath engine/bin/godot.linuxbsd.template_release.x86_64.llvm`}}!" project/export_presets.cfg rm -rf build && mkdir build @@ -27,7 +27,7 @@ release-linux: build release-windows: build # Compiling Windows Release - cd engine/ && scons platform=windows target=template_release arch=x86_64 linker=mold custom_modules="../modules" + cd engine/ && scons disable_exceptions=false platform=windows target=template_release arch=x86_64 linker=mold custom_modules="../modules" # Preparing Build Environment sed -i "s!templatepath!{{`realpath engine/bin/godot.windows.template_release.x86_64`}}!" project/export_presets.cfg rm -rf build && mkdir build @@ -46,4 +46,5 @@ initialize-template projectname: format: # Formatting Custom Modules - clang-format -i $(find modules/ -iname '*.h' -o -iname '*.c' -o -iname '*.hpp' -o -iname '*.cpp' -o -iname '*.hxx' -o -iname '*.cxx') + clang-format -i modules/you_done_it/*.h modules/you_done_it/*.cpp + # $(find modules/ -iname '*.h' -o -iname '*.c' -o -iname '*.hpp' -o -iname '*.cpp' -o -iname '*.hxx' -o -iname '*.cxx') diff --git a/modules/you_done_it/SCsub b/modules/you_done_it/SCsub index 96fd4e0f..f127e1be 100644 --- a/modules/you_done_it/SCsub +++ b/modules/you_done_it/SCsub @@ -7,9 +7,9 @@ ydi_env = env.Clone() ydi_env.Append(CPPPATH=["libzmq/include", "cppzmq/"]) ydi_env.add_source_files(env.modules_sources, "*.cpp") -if not os.path.isdir(Dir('libzmq/build/lib').abspath): - call(["cmake", "-Slibzmq", "-B" + Dir('libzmq/build').abspath, "-DZMQ_BUILD_TESTS=OFF"]) +call(["cmake", "-Slibzmq", "-B" + Dir('libzmq/build').abspath, "-DZMQ_BUILD_TESTS=OFF", "-DZMQ_BUILD_STATIC=ON", "-DZMQ_BUILD_SHARED=OFF"]) call(["cmake", "--build", Dir('libzmq/build').abspath]) env.Append(LIBPATH=[Dir('libzmq/build/lib/').abspath]) -ydi_env.Append(LIBS=['libzmq']) +ydi_env.Append(LIBPATH=[Dir('libzmq/build/lib/').abspath]) +env.Append(LIBS=['libzmq']) diff --git a/modules/you_done_it/flatscreen_root.cpp b/modules/you_done_it/flatscreen_root.cpp deleted file mode 100644 index f6e9b81a..00000000 --- a/modules/you_done_it/flatscreen_root.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "flatscreen_root.h" - -void FlatscreenRoot::_bind_methods() {} - -void FlatscreenRoot::enter_tree() { - ydi::server::open(); -} - -void FlatscreenRoot::exit_tree() { - ydi::server::close(); -} diff --git a/modules/you_done_it/flatscreen_root.h b/modules/you_done_it/flatscreen_root.h deleted file mode 100644 index 51147974..00000000 --- a/modules/you_done_it/flatscreen_root.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "ydi_server.h" -#include - -class FlatscreenRoot : public Container { - GDCLASS(FlatscreenRoot, Container); - static void _bind_methods(); - void enter_tree(); - void exit_tree(); -protected: - void _notification(int what); -}; diff --git a/modules/you_done_it/register_types.cpp b/modules/you_done_it/register_types.cpp index 9aa0aef5..52b63ba2 100644 --- a/modules/you_done_it/register_types.cpp +++ b/modules/you_done_it/register_types.cpp @@ -1,12 +1,13 @@ #include "register_types.h" -#include "core/object/class_db.h" -#include "zmq.hpp" +#include "server_node.h" +#include void initialize_you_done_it_module(ModuleInitializationLevel p_level) { if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { return; } + ClassDB::register_class(); } void uninitialize_you_done_it_module(ModuleInitializationLevel p_level) { diff --git a/modules/you_done_it/ydi_networking.cpp b/modules/you_done_it/ydi_networking.cpp index 68bd115b..d3f9cdd1 100644 --- a/modules/you_done_it/ydi_networking.cpp +++ b/modules/you_done_it/ydi_networking.cpp @@ -1,39 +1,30 @@ #include "ydi_networking.h" -#include -#include namespace ydi { -MessageType to_message_type(std::string const &msg) { - int as_int{ std::stoi(msg) }; - if (as_int >= 0 && as_int < MESSAGE_TYPE_MAX) { +MessageType to_message_type(zmq::message_t const &msg) { + int as_int{ std::stoi(msg.str()) }; + if (as_int >= 0 && as_int < MESSAGE_TYPE_INVALID) { return (MessageType)as_int; } else { - return NONE; + return MESSAGE_TYPE_INVALID; } } -MessageType message_type(zmq::multipart_t const &msg, size_t *type_idx) { - MessageType type{ to_message_type(msg[0].to_string()) }; - size_t idx{ 0 }; - if (!type) { - ++idx; - type = to_message_type(msg[0].to_string()); +NOKReason to_nok_reason(zmq::message_t const &msg) { + int as_int{ std::stoi(msg.str()) }; + if (as_int >= 0 && as_int < NOK_REASON_INVALID) { + return (NOKReason)as_int; + } else { + return NOK_REASON_INVALID; } - if (!type) { - idx = std::numeric_limits::max(); - } - if (type_idx) { - *type_idx = idx; - } - return type; - } -bool decompose_connect(zmq::multipart_t &msg, int *code) { - size_t start{}; - MessageType actual_type{ message_type(msg, &start) }; - if (actual_type) { - +ClueID to_clue_id(zmq::message_t const &msg) { + int as_int{ std::stoi(msg.str()) }; + if (as_int >= 0 && as_int < CLUE_MAX) { + return (ClueID)as_int; + } else { + return CLUE_MAX; } } @@ -67,4 +58,4 @@ void extend_multipart(zmq::multipart_t &mpart, zmq::multipart_t const &right) { } void extend_multipart(zmq::multipart_t &mpart) {} -} +} //namespace ydi diff --git a/modules/you_done_it/ydi_networking.h b/modules/you_done_it/ydi_networking.h index 87874bac..48ec2299 100644 --- a/modules/you_done_it/ydi_networking.h +++ b/modules/you_done_it/ydi_networking.h @@ -1,30 +1,44 @@ #pragma once #include -#include -#include #include #include +#include +#include namespace ydi { enum MessageType { - NONE = 0u, + // connection management messages CONNECT = 1u, OK, NOK, - MESSAGE_TYPE_MAX + HEART, + BEAT, + // gameplay messages + REVEAL, + // end of messages + MESSAGE_TYPE_INVALID }; enum ClueID { FIRST_CLUE, + CLUE_MAX }; -MessageType to_message_type(std::string const &msg); -MessageType message_type(zmq::multipart_t const &msg, size_t *type_idx = nullptr); +enum ConnectionStatus { + DISCONNECTED, + CONNECTED, + AUTHENTICATED +}; -bool decompose_connect(zmq::multipart_t &msg, int *code); -bool decompose_nok(zmq::multipart_t &msg, MessageType &failed); -bool decompose_reveal_clue(zmq::multipart_t &msg, ClueID &id); +enum NOKReason { + CLIENT_UNAUTHENTICATED, + NOK_REASON_INVALID //!< this means the value could not be parsed as a NOK reason, not that the reason is an invalid message.INVALID +}; + +MessageType to_message_type(zmq::message_t const &msg); +NOKReason to_nok_reason(zmq::message_t const &msg); +ClueID to_clue_id(zmq::message_t const &msg); void extend_multipart(zmq::multipart_t &mpart, MessageType type); void extend_multipart(zmq::multipart_t &mpart, ClueID type); @@ -37,7 +51,7 @@ void extend_multipart(zmq::multipart_t &mpart, zmq::multipart_t const &right); void extend_multipart(zmq::multipart_t &mpart); template -void extend_multipart(zmq::multipart_t &mpart, TArg const &arg, TArgs const &... args) { +void extend_multipart(zmq::multipart_t &mpart, TArg const &arg, TArgs const &...args) { extend_multipart(mpart, arg); extend_multipart(mpart, args...); } @@ -50,9 +64,9 @@ zmq::multipart_t multipart(TArg const &arg) { } template -zmq::multipart_t multipart(TArg const &arg, TArgs const &... args) { +zmq::multipart_t multipart(TArg const &arg, TArgs const &...args) { zmq::multipart_t mpart{ multipart(arg) }; extend_multipart(mpart, args...); return mpart; } -} +} //namespace ydi diff --git a/modules/you_done_it/ydi_server.cpp b/modules/you_done_it/ydi_server.cpp index 882943a8..f6f1f1bb 100644 --- a/modules/you_done_it/ydi_server.cpp +++ b/modules/you_done_it/ydi_server.cpp @@ -2,39 +2,48 @@ #include "ydi_networking.h" #include #include +#include #include #include -#include -#include +#include #include #include #include #include namespace ydi::server { +struct Event { + MessageType type; + union { + ClueID clue; + }; +}; struct Service { std::optional context{ std::nullopt }; std::optional socket{ std::nullopt }; - std::unordered_set revealedClues{}; + std::unordered_set revealed_clues{}; std::recursive_mutex mtx{}; std::optional client{ std::nullopt }; + std::queue unhandled_events{}; double lastHeart{ 0.0 }; double lastBeat{ 0.0 }; std::atomic stop_threads{ false }; }; +std::optional receive_thread{ std::nullopt }; +std::optional ping_thread{ std::nullopt }; std::optional service{ std::nullopt }; -std::optional receiveThread{ std::nullopt }; -std::optional pingThread{ std::nullopt }; void handle_reveal_clue(zmq::multipart_t const &message) { + Event evt{ .type = REVEAL, .clue = to_clue_id(message.at(2)) }; + service->unhandled_events.push(evt); } -void handle_authorised_message(std::string_view const &sender, std::string_view const &type, zmq::multipart_t &message) { - if (type == "BEAT") { +void handle_authorised_message(std::string_view const &sender, MessageType type, zmq::multipart_t &message) { + if (type == BEAT) { service->lastBeat = Time::get_singleton()->get_unix_time_from_system(); - } else if(type == "REVEAL") { + } else if (type == REVEAL) { handle_reveal_clue(message); } else { multipart(sender, "NOK", "UNKOWN_COMMAND", message).send(*service->socket); @@ -43,18 +52,19 @@ void handle_authorised_message(std::string_view const &sender, std::string_view void handle_message(zmq::multipart_t &message) { std::string_view const sender{ message.at(0).to_string_view() }; - std::string_view const type{ message.at(1).to_string_view() }; + MessageType type{ to_message_type(message.at(1)) }; std::scoped_lock lock{ service->mtx }; if (service->client) { - if (sender != service->client) { - multipart(sender,"NOK", "UNAUTHORIZED_REQUEST").send(*service->socket); + if (sender == service->client) { + handle_authorised_message(sender, type, message); + } else { + multipart(sender, "NOK", "UNAUTHORIZED_REQUEST").send(*service->socket); } - handle_authorised_message(sender, type, message); - } else if (type == "CONNECT") { + } else if (type == CONNECT) { service->client.emplace(sender); - multipart(sender, "OK", message).send(*service->socket); + multipart(sender, OK, message).send(*service->socket); } else { - multipart(sender,"NOK", "UNAUTHORIZED_REQUEST", message).send(*service->socket); + multipart(sender, NOK, "UNAUTHORIZED_REQUEST", message).send(*service->socket); } } @@ -72,7 +82,8 @@ void receive_thread_entry() { void ping_thread_entry() { using namespace std::chrono_literals; - { std::scoped_lock lock{ service->mtx }; + { + std::scoped_lock lock{ service->mtx }; if (!service->client) { return; } @@ -87,45 +98,67 @@ void ping_thread_entry() { } void open() { + print_line("Server: Starting"); service.emplace(); try { service->context.emplace(1); } catch (...) { service.reset(); + print_line("Server: Failed to create context"); return; } + print_line("Server: Created zmq context"); try { service->socket.emplace(*service->context, zmq::socket_type::router); } catch (...) { service->context->close(); service->context.reset(); service.reset(); + print_line("Server: Failed to create socket"); return; } + print_line("Server: Created socket"); try { - service->socket->connect("tcp://*:6667"); + service->socket->bind("tcp://*:6667"); } catch (...) { service->socket->close(); service->socket.reset(); service->context->close(); service->context.reset(); service.reset(); + print_line("Server: Failed to bind socket"); + return; } - receiveThread.emplace(receive_thread_entry); + print_line("Server: Bound socket"); + receive_thread.emplace(receive_thread_entry); } void close() { if (service) { + print_line("Server: Shutting down..."); std::scoped_lock lock{ service->mtx }; + service->stop_threads = true; + print_line("Server: Stopping Threads..."); + if (receive_thread && receive_thread->joinable()) { + receive_thread->join(); + } + print_line("Server: Receive thread stopped"); + if (ping_thread && ping_thread->joinable()) { + ping_thread->join(); + } + print_line("Server: Ping thread stopped"); if (service->socket) { service->socket->close(); service->socket.reset(); } + print_line("Server: Socket closed"); if (service->context) { service->context->close(); service->context.reset(); } + print_line("Server: Context closed"); service.reset(); + print_line("Server: Shutdown complete!"); } } -} +} //namespace ydi::server diff --git a/modules/you_done_it/ydi_server.h b/modules/you_done_it/ydi_server.h index 23a2ca1a..072ff5bc 100644 --- a/modules/you_done_it/ydi_server.h +++ b/modules/you_done_it/ydi_server.h @@ -1,10 +1,10 @@ #pragma once -#include +#include "ydi_networking.h" #include #include #include -#include "ydi_networking.h" +#include namespace ydi::server { void open(); @@ -12,8 +12,8 @@ void close(); namespace receive { bool isRevealed(ClueID id); Ref clueImage(ClueID id); -} +} //namespace receive namespace send { void announceConclusion(ClueID method, ClueID motive, ClueID murderer); } -} +} //namespace ydi::server