diff --git a/modules/you_done_it/client_node.cpp b/modules/you_done_it/client_node.cpp new file mode 100644 index 00000000..57dc03ba --- /dev/null +++ b/modules/you_done_it/client_node.cpp @@ -0,0 +1,27 @@ +#include "client_node.h" +#include "ydi_client.h" + +void ClientNode::_bind_methods() { + ClassDB::bind_method(D_METHOD("connect_to_server"), &self_type::connect_to_server); +} + +void ClientNode::exit_tree() { + ydi::client::disconnect(); +} + +void ClientNode::_notification(int what) { + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + switch (what) { + case NOTIFICATION_EXIT_TREE: + exit_tree(); + return; + default: + return; + } +} + +void ClientNode::connect_to_server(String const &server) { + ydi::client::connect(server); +} diff --git a/modules/you_done_it/client_node.h b/modules/you_done_it/client_node.h new file mode 100644 index 00000000..67ae2abf --- /dev/null +++ b/modules/you_done_it/client_node.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +class ClientNode : Node { + GDCLASS(ClientNode, Node); + static void _bind_methods(); + void exit_tree(); + +protected: + void _notification(int what); + +public: + void connect_to_server(String const &url); +}; diff --git a/modules/you_done_it/register_types.cpp b/modules/you_done_it/register_types.cpp index 52b63ba2..74139d10 100644 --- a/modules/you_done_it/register_types.cpp +++ b/modules/you_done_it/register_types.cpp @@ -1,6 +1,8 @@ #include "register_types.h" +#include "client_node.h" #include "server_node.h" +#include "ydi_networking.h" #include void initialize_you_done_it_module(ModuleInitializationLevel p_level) { @@ -8,6 +10,8 @@ void initialize_you_done_it_module(ModuleInitializationLevel p_level) { return; } ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_abstract_class(); } void uninitialize_you_done_it_module(ModuleInitializationLevel p_level) { diff --git a/modules/you_done_it/server_node.cpp b/modules/you_done_it/server_node.cpp index 6866318c..ae3383b7 100644 --- a/modules/you_done_it/server_node.cpp +++ b/modules/you_done_it/server_node.cpp @@ -2,12 +2,22 @@ #include "ydi_server.h" #include -void ServerNode::_bind_methods() {} - +void ServerNode::_bind_methods() { + ADD_SIGNAL(MethodInfo("new_clue", PropertyInfo(Variant::INT, "id"))); +} void ServerNode::enter_tree() { ydi::server::open(); } +void ServerNode::process(double delta) { + Vector new_clues{}; + if (ydi::server::receive::new_clues(new_clues)) { + for (NetworkData::ClueID clue : new_clues) { + emit_signal("new_clue", clue); + } + } +} + void ServerNode::exit_tree() { ydi::server::close(); } @@ -20,6 +30,9 @@ void ServerNode::_notification(int what) { case NOTIFICATION_ENTER_TREE: enter_tree(); return; + case NOTIFICATION_PROCESS: + process(get_process_delta_time()); + return; case NOTIFICATION_EXIT_TREE: exit_tree(); return; diff --git a/modules/you_done_it/server_node.h b/modules/you_done_it/server_node.h index 4f53de93..cd717870 100644 --- a/modules/you_done_it/server_node.h +++ b/modules/you_done_it/server_node.h @@ -6,6 +6,7 @@ class ServerNode : public Node { GDCLASS(ServerNode, Node); static void _bind_methods(); void enter_tree(); + void process(double delta); void exit_tree(); protected: diff --git a/modules/you_done_it/ydi_client.cpp b/modules/you_done_it/ydi_client.cpp index c4b287d9..ece99c3c 100644 --- a/modules/you_done_it/ydi_client.cpp +++ b/modules/you_done_it/ydi_client.cpp @@ -14,7 +14,7 @@ struct Connection { std::optional context{ std::nullopt }; std::optional socket{ std::nullopt }; std::recursive_mutex mtx; - std::atomic status; + std::atomic status; std::atomic stop_threads; }; @@ -23,22 +23,25 @@ std::optional connection{ std::nullopt }; std::optional receive_thread{ std::nullopt }; void handle_ok(zmq::multipart_t const &message) { - MessageType type{ to_message_type(message[1]) }; + NetworkData::MessageType type{ to_message_type(message[1]) }; switch (type) { default: // no need to handle every OK, just some relevant ones return; - case CONNECT: - connection->status = AUTHENTICATED; + case NetworkData::MSG_CONNECT: + connection->status = NetworkData::CONNECTION_AUTHENTICATED; + return; } } void handle_message(zmq::multipart_t const &message) { - MessageType type{ to_message_type(message[0]) }; + NetworkData::MessageType type{ to_message_type(message[0]) }; switch (type) { - case OK: + case NetworkData::MSG_OK: handle_ok(message); + case NetworkData::MSG_HEART: + multipart(NetworkData::MSG_BEAT).send(*connection->socket); default: - print_error(vformat("Client: Unhandled message type received: ", type, message[0].to_string().c_str())); + print_error(vformat("Client: Received unhandled message: ", type, message[0].to_string().c_str())); return; } } diff --git a/modules/you_done_it/ydi_client.h b/modules/you_done_it/ydi_client.h index f39dd92c..af7a37b0 100644 --- a/modules/you_done_it/ydi_client.h +++ b/modules/you_done_it/ydi_client.h @@ -7,6 +7,6 @@ namespace ydi::client { void connect(String const &url); void disconnect(); namespace send { -void reveal_clue(ClueID id); +void reveal_clue(NetworkData::ClueID id); } //namespace send } //namespace ydi::client diff --git a/modules/you_done_it/ydi_networking.cpp b/modules/you_done_it/ydi_networking.cpp index d3f9cdd1..4664a40d 100644 --- a/modules/you_done_it/ydi_networking.cpp +++ b/modules/you_done_it/ydi_networking.cpp @@ -1,38 +1,48 @@ #include "ydi_networking.h" +#include +#include + +MAKE_TYPE_INFO(NetworkData::ClueID, Variant::INT); + +void NetworkData::_bind_methods() { + BIND_ENUM_CONSTANT(CLUE_FIRST); + BIND_ENUM_CONSTANT(CLUE_SECOND); + BIND_ENUM_CONSTANT(CLUE_MAX); +} namespace ydi { -MessageType to_message_type(zmq::message_t const &msg) { +NetworkData::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; + if (as_int >= 0 && as_int < NetworkData::MSG_INVALID) { + return (NetworkData::MessageType)as_int; } else { - return MESSAGE_TYPE_INVALID; + return NetworkData::MSG_INVALID; } } -NOKReason to_nok_reason(zmq::message_t const &msg) { +NetworkData::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; + if (as_int >= 0 && as_int < NetworkData::NOK_REASON_INVALID) { + return (NetworkData::NOKReason)as_int; } else { - return NOK_REASON_INVALID; + return NetworkData::NOK_REASON_INVALID; } } -ClueID to_clue_id(zmq::message_t const &msg) { +NetworkData::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; + if (as_int >= 0 && as_int < NetworkData::CLUE_MAX) { + return (NetworkData::ClueID)as_int; } else { - return CLUE_MAX; + return NetworkData::CLUE_MAX; } } -void extend_multipart(zmq::multipart_t &mpart, MessageType type) { +void extend_multipart(zmq::multipart_t &mpart, NetworkData::MessageType type) { mpart.addstr(std::to_string(type)); } -void extend_multipart(zmq::multipart_t &mpart, ClueID id) { +void extend_multipart(zmq::multipart_t &mpart, NetworkData::ClueID id) { mpart.addstr(std::to_string(id)); } diff --git a/modules/you_done_it/ydi_networking.h b/modules/you_done_it/ydi_networking.h index 48ec2299..b88e852e 100644 --- a/modules/you_done_it/ydi_networking.h +++ b/modules/you_done_it/ydi_networking.h @@ -1,47 +1,57 @@ #pragma once +#include +#include #include #include #include #include #include +class NetworkData : Object { + GDCLASS(NetworkData, Object); + static void _bind_methods(); + +public: + enum MessageType { + // connection management messages + MSG_CONNECT = 1u, + MSG_OK, + MSG_NOK, + MSG_HEART, + MSG_BEAT, + // gameplay messages + MSG_REVEAL, + // end of messages + MSG_INVALID + }; + + enum ClueID { + CLUE_FIRST, + CLUE_SECOND, + CLUE_MAX + }; + + enum ConnectionStatus { + CONNECTION_DISCONNECTED, + CONNECTION_CONNECTED, + CONNECTION_AUTHENTICATED + }; + + enum NOKReason { + NOK_UNAUTHENTICATED, + NOK_UNKNOWN_MSG, + NOK_REASON_INVALID //!< this means the value could not be parsed as a NOK reason, not that the reason is an invalid message.INVALID + }; +}; + namespace ydi { -enum MessageType { - // connection management messages - CONNECT = 1u, - OK, - NOK, - HEART, - BEAT, - // gameplay messages - REVEAL, - // end of messages - MESSAGE_TYPE_INVALID -}; +NetworkData::MessageType to_message_type(zmq::message_t const &msg); +NetworkData::NOKReason to_nok_reason(zmq::message_t const &msg); +NetworkData::ClueID to_clue_id(zmq::message_t const &msg); -enum ClueID { - FIRST_CLUE, - CLUE_MAX -}; - -enum ConnectionStatus { - DISCONNECTED, - CONNECTED, - AUTHENTICATED -}; - -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); +void extend_multipart(zmq::multipart_t &mpart, NetworkData::MessageType type); +void extend_multipart(zmq::multipart_t &mpart, NetworkData::ClueID type); void extend_multipart(zmq::multipart_t &mpart, std::string const &string); void extend_multipart(zmq::multipart_t &mpart, std::string_view const &strv); diff --git a/modules/you_done_it/ydi_server.cpp b/modules/you_done_it/ydi_server.cpp index f6f1f1bb..3f8d4110 100644 --- a/modules/you_done_it/ydi_server.cpp +++ b/modules/you_done_it/ydi_server.cpp @@ -5,26 +5,19 @@ #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 revealed_clues{}; + std::unordered_set revealed_clues{}; std::recursive_mutex mtx{}; std::optional client{ std::nullopt }; - std::queue unhandled_events{}; + Vector new_clues{}; double lastHeart{ 0.0 }; double lastBeat{ 0.0 }; @@ -36,23 +29,22 @@ std::optional ping_thread{ std::nullopt }; std::optional service{ 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); + service->new_clues.push_back(to_clue_id(message.at(2))); } -void handle_authorised_message(std::string_view const &sender, MessageType type, zmq::multipart_t &message) { - if (type == BEAT) { +void handle_authorised_message(std::string_view const &sender, NetworkData::MessageType type, zmq::multipart_t &message) { + if (type == NetworkData::MSG_BEAT) { service->lastBeat = Time::get_singleton()->get_unix_time_from_system(); - } else if (type == REVEAL) { + } else if (type == NetworkData::MSG_REVEAL) { handle_reveal_clue(message); } else { - multipart(sender, "NOK", "UNKOWN_COMMAND", message).send(*service->socket); + multipart(sender, NetworkData::MSG_NOK, NetworkData::NOK_UNKNOWN_MSG, message).send(*service->socket); } } void handle_message(zmq::multipart_t &message) { std::string_view const sender{ message.at(0).to_string_view() }; - MessageType type{ to_message_type(message.at(1)) }; + NetworkData::MessageType type{ to_message_type(message.at(1)) }; std::scoped_lock lock{ service->mtx }; if (service->client) { if (sender == service->client) { @@ -60,11 +52,11 @@ void handle_message(zmq::multipart_t &message) { } else { multipart(sender, "NOK", "UNAUTHORIZED_REQUEST").send(*service->socket); } - } else if (type == CONNECT) { + } else if (type == NetworkData::MSG_CONNECT) { service->client.emplace(sender); - multipart(sender, OK, message).send(*service->socket); + multipart(sender, NetworkData::MSG_OK, message).send(*service->socket); } else { - multipart(sender, NOK, "UNAUTHORIZED_REQUEST", message).send(*service->socket); + multipart(sender, NetworkData::MSG_NOK, "UNAUTHORIZED_REQUEST", message).send(*service->socket); } } @@ -161,4 +153,16 @@ void close() { print_line("Server: Shutdown complete!"); } } + +namespace receive { +bool new_clues(Vector &out) { + std::scoped_lock lock{ service->mtx }; + bool has_new{ !service->new_clues.is_empty() }; + if (has_new) { + out.append_array(service->new_clues); + service->new_clues.clear(); + } + return has_new; +} +} //namespace receive } //namespace ydi::server diff --git a/modules/you_done_it/ydi_server.h b/modules/you_done_it/ydi_server.h index 072ff5bc..77acad2e 100644 --- a/modules/you_done_it/ydi_server.h +++ b/modules/you_done_it/ydi_server.h @@ -2,6 +2,7 @@ #include "ydi_networking.h" #include +#include #include #include #include @@ -9,11 +10,8 @@ namespace ydi::server { void open(); void close(); + namespace receive { -bool isRevealed(ClueID id); -Ref clueImage(ClueID id); +bool new_clues(Vector &out); } //namespace receive -namespace send { -void announceConclusion(ClueID method, ClueID motive, ClueID murderer); -} } //namespace ydi::server