feat: added client node and registered clue id
This commit is contained in:
parent
38081a7fd0
commit
acb7351ea7
11 changed files with 167 additions and 82 deletions
27
modules/you_done_it/client_node.cpp
Normal file
27
modules/you_done_it/client_node.cpp
Normal file
|
|
@ -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);
|
||||
}
|
||||
15
modules/you_done_it/client_node.h
Normal file
15
modules/you_done_it/client_node.h
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <scene/main/node.h>
|
||||
|
||||
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);
|
||||
};
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
#include "register_types.h"
|
||||
|
||||
#include "client_node.h"
|
||||
#include "server_node.h"
|
||||
#include "ydi_networking.h"
|
||||
#include <core/object/class_db.h>
|
||||
|
||||
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<ServerNode>();
|
||||
ClassDB::register_class<ClientNode>();
|
||||
ClassDB::register_abstract_class<NetworkData>();
|
||||
}
|
||||
|
||||
void uninitialize_you_done_it_module(ModuleInitializationLevel p_level) {
|
||||
|
|
|
|||
|
|
@ -2,12 +2,22 @@
|
|||
#include "ydi_server.h"
|
||||
#include <core/config/engine.h>
|
||||
|
||||
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<NetworkData::ClueID> 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;
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ struct Connection {
|
|||
std::optional<zmq::context_t> context{ std::nullopt };
|
||||
std::optional<zmq::socket_t> socket{ std::nullopt };
|
||||
std::recursive_mutex mtx;
|
||||
std::atomic<ConnectionStatus> status;
|
||||
std::atomic<NetworkData::ConnectionStatus> status;
|
||||
std::atomic<bool> stop_threads;
|
||||
};
|
||||
|
||||
|
|
@ -23,22 +23,25 @@ std::optional<Connection> connection{ std::nullopt };
|
|||
std::optional<std::thread> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,38 +1,48 @@
|
|||
#include "ydi_networking.h"
|
||||
#include <core/core_bind.h>
|
||||
#include <core/object/class_db.h>
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,47 +1,57 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/object/class_db.h>
|
||||
#include <core/object/object.h>
|
||||
#include <zmq.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <zmq.hpp>
|
||||
#include <zmq_addon.hpp>
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -5,26 +5,19 @@
|
|||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
#include <unordered_set>
|
||||
#include <zmq.hpp>
|
||||
#include <zmq_addon.hpp>
|
||||
|
||||
namespace ydi::server {
|
||||
struct Event {
|
||||
MessageType type;
|
||||
union {
|
||||
ClueID clue;
|
||||
};
|
||||
};
|
||||
struct Service {
|
||||
std::optional<zmq::context_t> context{ std::nullopt };
|
||||
std::optional<zmq::socket_t> socket{ std::nullopt };
|
||||
std::unordered_set<ClueID> revealed_clues{};
|
||||
std::unordered_set<NetworkData::ClueID> revealed_clues{};
|
||||
std::recursive_mutex mtx{};
|
||||
std::optional<std::string> client{ std::nullopt };
|
||||
std::queue<Event> unhandled_events{};
|
||||
Vector<NetworkData::ClueID> new_clues{};
|
||||
double lastHeart{ 0.0 };
|
||||
double lastBeat{ 0.0 };
|
||||
|
||||
|
|
@ -36,23 +29,22 @@ std::optional<std::thread> ping_thread{ std::nullopt };
|
|||
std::optional<Service> 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<NetworkData::ClueID> &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
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "ydi_networking.h"
|
||||
#include <core/io/image.h>
|
||||
#include <core/object/ref_counted.h>
|
||||
#include <core/string/ustring.h>
|
||||
#include <core/templates/vector.h>
|
||||
#include <cassert>
|
||||
|
|
@ -9,11 +10,8 @@
|
|||
namespace ydi::server {
|
||||
void open();
|
||||
void close();
|
||||
|
||||
namespace receive {
|
||||
bool isRevealed(ClueID id);
|
||||
Ref<Image> clueImage(ClueID id);
|
||||
bool new_clues(Vector<NetworkData::ClueID> &out);
|
||||
} //namespace receive
|
||||
namespace send {
|
||||
void announceConclusion(ClueID method, ClueID motive, ClueID murderer);
|
||||
}
|
||||
} //namespace ydi::server
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue