[Refactor] Implment structured network protocol.
This commit is contained in:
parent
3234ef7b3b
commit
9d770ef9b2
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#include "gfx/types.h"
|
#include "gfx/types.h"
|
||||||
#include "net/constants.h"
|
#include "net/constants.h"
|
||||||
|
#include "net/message_protocol.h"
|
||||||
|
#include "client_network.h"
|
||||||
#include "network_manager.h"
|
#include "network_manager.h"
|
||||||
#include "game_state.h"
|
#include "game_state.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
@ -20,9 +22,9 @@
|
|||||||
#include "debug/debug_overlay.h"
|
#include "debug/debug_overlay.h"
|
||||||
|
|
||||||
void GameState::_init_desktop(void) {
|
void GameState::_init_desktop(void) {
|
||||||
_desktop = std::make_unique<Desktop>(_screen_width, _screen_height, _network.get());
|
_desktop = std::make_unique<Desktop>(_screen_width, _screen_height, this);
|
||||||
|
|
||||||
auto term = std::make_unique<Terminal>(_network.get());
|
auto term = std::make_unique<Terminal>(this);
|
||||||
auto term_window = std::make_unique<UIWindow>("Terminal", 100, 100, 800, 500);
|
auto term_window = std::make_unique<UIWindow>("Terminal", 100, 100, 800, 500);
|
||||||
term_window->set_content(std::move(term));
|
term_window->set_content(std::move(term));
|
||||||
_desktop->add_window(std::move(term_window));
|
_desktop->add_window(std::move(term_window));
|
||||||
@ -149,11 +151,11 @@ void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts
|
|||||||
|
|
||||||
if(_login_screen->is_new_account_mode()) {
|
if(_login_screen->is_new_account_mode()) {
|
||||||
std::string hostname = _login_screen->get_hostname();
|
std::string hostname = _login_screen->get_hostname();
|
||||||
std::string msg = "C_ACC::" + username + "::" + password + "::" + hostname;
|
_network->send(net_protocol::build_message(net_protocol::Opcode::C2S_CREATE_ACCOUNT,
|
||||||
_network->send(msg);
|
{username, password, hostname}));
|
||||||
} else {
|
} else {
|
||||||
std::string msg = "LOGIN::" + username + "::" + password;
|
_network->send(net_protocol::build_message(net_protocol::Opcode::C2S_LOGIN,
|
||||||
_network->send(msg);
|
{username, password}));
|
||||||
}
|
}
|
||||||
_login_screen->clear_login_attempt(); /* Try to spam my server now b.tch! */
|
_login_screen->clear_login_attempt(); /* Try to spam my server now b.tch! */
|
||||||
}
|
}
|
||||||
@ -161,18 +163,36 @@ void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts
|
|||||||
/* Check for server response to our login attempt. */
|
/* Check for server response to our login attempt. */
|
||||||
std::string server_msg;
|
std::string server_msg;
|
||||||
while(_network->poll_message(server_msg)) {
|
while(_network->poll_message(server_msg)) {
|
||||||
if(server_msg == "LOGIN_SUCCESS" || server_msg == "C_ACC_SUCCESS") {
|
net_protocol::Opcode opcode;
|
||||||
|
std::vector<std::string> args;
|
||||||
|
net_protocol::parse_message(server_msg, opcode, args);
|
||||||
|
|
||||||
|
switch(opcode) {
|
||||||
|
case net_protocol::Opcode::S2C_LOGIN_SUCCESS:
|
||||||
|
case net_protocol::Opcode::S2C_CREATE_ACCOUNT_SUCCESS:
|
||||||
_current_screen = Screen::BOOTING;
|
_current_screen = Screen::BOOTING;
|
||||||
_login_screen.reset(); /* Free mem. */
|
_login_screen.reset(); /* Free mem. */
|
||||||
_boot_sequence = std::make_unique<BootSequence>();
|
_boot_sequence = std::make_unique<BootSequence>();
|
||||||
break;
|
break;
|
||||||
} else if(server_msg == "LOGIN_FAIL") {
|
case net_protocol::Opcode::S2C_LOGIN_FAIL:
|
||||||
_login_screen->set_error_message("Invalid username or password.");
|
_login_screen->set_error_message(args.empty() ? "Login failed." : args[0]);
|
||||||
} else if(server_msg == "C_ACC_FAIL") {
|
break;
|
||||||
_login_screen->set_error_message("Username already exists.");
|
case net_protocol::Opcode::S2C_CREATE_ACCOUNT_FAIL:
|
||||||
|
_login_screen->set_error_message(args.empty() ? "Account creation failed." :
|
||||||
|
args[0]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Recieved unexpected opcode %d during login.\n",
|
||||||
|
static_cast<int>(opcode));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* If we successfully changed screen, stop processing messages for this frame. */
|
||||||
|
if(_current_screen == Screen::BOOTING) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} break;
|
break;
|
||||||
|
}
|
||||||
case Screen::BOOTING: {
|
case Screen::BOOTING: {
|
||||||
if(!_boot_sequence) break; /* Shouldn't happen. */
|
if(!_boot_sequence) break; /* Shouldn't happen. */
|
||||||
if(_boot_sequence->is_finished()) {
|
if(_boot_sequence->is_finished()) {
|
||||||
@ -185,46 +205,52 @@ void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts
|
|||||||
case Screen::DESKTOP: {
|
case Screen::DESKTOP: {
|
||||||
std::string server_msg;
|
std::string server_msg;
|
||||||
while(_network->poll_message(server_msg)) {
|
while(_network->poll_message(server_msg)) {
|
||||||
/* Check for 'special', non-terminal messages first. */
|
net_protocol::Opcode opcode;
|
||||||
if(server_msg.rfind("FILEDATA::", 0) == 0) {
|
std::vector<std::string> args;
|
||||||
std::string payload = server_msg.substr(10);
|
net_protocol::parse_message(server_msg, opcode, args);
|
||||||
size_t separator_pos = payload.find("::");
|
|
||||||
if(separator_pos != std::string::npos) {
|
switch(opcode) {
|
||||||
std::string filepath = payload.substr(0, separator_pos);
|
case net_protocol::Opcode::S2C_FILE_DATA:
|
||||||
std::string content = payload.substr(separator_pos+2);
|
if(args.size() == 2) {
|
||||||
auto editor = std::make_unique<Editor>(filepath);
|
auto editor = std::make_unique<Editor>(args[0]);
|
||||||
editor->set_buffer_content(content);
|
editor->set_buffer_content(args[1]);
|
||||||
auto editor_window = std::make_unique<UIWindow>(filepath.c_str(),
|
auto editor_window = std::make_unique<UIWindow>(args[0].c_str(),
|
||||||
200, 200, 600, 400);
|
200, 200, 600, 400);
|
||||||
editor_window->set_content(std::move(editor));
|
editor_window->set_content(std::move(editor));
|
||||||
_desktop->add_window(std::move(editor_window));
|
_desktop->add_window(std::move(editor_window));
|
||||||
}
|
}
|
||||||
continue;
|
break;
|
||||||
}
|
case net_protocol::Opcode::S2C_DISCONNECT: {
|
||||||
if(server_msg == "__DISCONNECTED__") {
|
|
||||||
IWindowContent* content = _desktop->get_focused_window() ?
|
IWindowContent* content = _desktop->get_focused_window() ?
|
||||||
_desktop->get_focused_window()->get_content() : nullptr;
|
_desktop->get_focused_window()->get_content() : nullptr;
|
||||||
Terminal* terminal = dynamic_cast<Terminal*>(content);
|
Terminal* terminal = dynamic_cast<Terminal*>(content);
|
||||||
|
|
||||||
if(terminal) {
|
if(terminal) {
|
||||||
terminal->add_history("Connection closed.");
|
terminal->add_history("Connection closed.");
|
||||||
_network->send("");
|
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
/* If not 'special' message, assume it's for terminal. */
|
break;
|
||||||
|
case net_protocol::Opcode::S2C_CLOSE_WINDOW: {
|
||||||
|
if(_desktop) {
|
||||||
|
UIWindow* focused_window = _desktop->get_focused_window();
|
||||||
|
if(focused_window) { focused_window->close(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case net_protocol::Opcode::S2C_COMMAND_RESPONSE: {
|
||||||
|
if(!args.empty()) {
|
||||||
IWindowContent* content = _desktop->get_focused_window() ?
|
IWindowContent* content = _desktop->get_focused_window() ?
|
||||||
_desktop->get_focused_window()->get_content() : nullptr;
|
_desktop->get_focused_window()->get_content() : nullptr;
|
||||||
Terminal* terminal = dynamic_cast<Terminal*>(content);
|
Terminal* terminal = dynamic_cast<Terminal*>(content);
|
||||||
if(!terminal)
|
if(!terminal) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Server sends "output\nprompt", split them. */
|
/* Server sends "output\nprompt", split them. */
|
||||||
size_t last_newline = server_msg.find_last_of('\n');
|
size_t last_newline = args[0].find_last_of('\n');
|
||||||
if(last_newline != std::string::npos) {
|
if(last_newline != std::string::npos) {
|
||||||
std::string prompt = server_msg.substr(last_newline+1);
|
std::string prompt = args[0].substr(last_newline+1);
|
||||||
terminal->set_prompt(prompt);
|
terminal->set_prompt(prompt);
|
||||||
|
|
||||||
std::string output = server_msg.substr(0, last_newline);
|
std::string output = args[0].substr(0, last_newline);
|
||||||
if(!output.empty()) {
|
if(!output.empty()) {
|
||||||
/* Split multiline output and push each line to history. */
|
/* Split multiline output and push each line to history. */
|
||||||
std::stringstream ss(output);
|
std::stringstream ss(output);
|
||||||
@ -234,14 +260,14 @@ void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
terminal->add_history(server_msg);
|
terminal->add_history(args[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(_desktop) {
|
if(_desktop) {
|
||||||
/*
|
|
||||||
* TODO: These fuck'in window dimensions just need to be global at this point!!
|
|
||||||
* Pass GameState by reference ?
|
|
||||||
*/
|
|
||||||
_desktop->update(dt, _screen_width, _screen_height);
|
_desktop->update(dt, _screen_width, _screen_height);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -249,6 +275,13 @@ void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameState::send_network_command(const std::string& command) {
|
||||||
|
if(_network && _network->is_connected()) {
|
||||||
|
_network->send(net_protocol::build_message(net_protocol::Opcode::C2S_COMMAND,
|
||||||
|
{command}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GameState::render(const RenderContext& context) {
|
void GameState::render(const RenderContext& context) {
|
||||||
switch(_current_screen) {
|
switch(_current_screen) {
|
||||||
case Screen::MAIN_MENU:
|
case Screen::MAIN_MENU:
|
||||||
@ -277,3 +310,13 @@ void GameState::render(const RenderContext& context) {
|
|||||||
_debug_overlay->render(context.ui_renderer);
|
_debug_overlay->render(context.ui_renderer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameState::send_file_write_request(const std::string& path, const std::string& content) {
|
||||||
|
_network->send(net_protocol::build_message(net_protocol::Opcode::C2S_WRITE_FILE,
|
||||||
|
{path, content}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameState::send_file_read_request(const std::string& path) {
|
||||||
|
_network->send(net_protocol::build_message(net_protocol::Opcode::C2S_READ_FILE,
|
||||||
|
{path}));
|
||||||
|
}
|
||||||
|
|||||||
@ -32,6 +32,11 @@ public:
|
|||||||
void update(float dt, int draw_calls, int shape_verts, int text_verts);
|
void update(float dt, int draw_calls, int shape_verts, int text_verts);
|
||||||
void render(const RenderContext& context);
|
void render(const RenderContext& context);
|
||||||
|
|
||||||
|
/* Public network interface for UI components. */
|
||||||
|
void send_network_command(const std::string& command);
|
||||||
|
void send_file_write_request(const std::string& path, const std::string& content);
|
||||||
|
void send_file_read_request(const std::string& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<ClientNetwork> _network;
|
std::unique_ptr<ClientNetwork> _network;
|
||||||
std::unique_ptr<Desktop> _desktop;
|
std::unique_ptr<Desktop> _desktop;
|
||||||
@ -46,5 +51,6 @@ private:
|
|||||||
bool _is_single_player;
|
bool _is_single_player;
|
||||||
|
|
||||||
void _init_desktop(void);
|
void _init_desktop(void);
|
||||||
|
void _send_network_command(const std::string& command);
|
||||||
void _run_server(void);
|
void _run_server(void);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,12 +6,12 @@
|
|||||||
#include <SDL3/SDL_events.h>
|
#include <SDL3/SDL_events.h>
|
||||||
|
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "client_network.h"
|
#include "game_state.h"
|
||||||
#include "gfx/types.h"
|
#include "gfx/types.h"
|
||||||
#include "ui/window_action.h"
|
#include "ui/window_action.h"
|
||||||
|
|
||||||
Terminal::Terminal(ClientNetwork* network)
|
Terminal::Terminal(GameState* game_state)
|
||||||
: _network(network), _should_close(false), _scroll_offset(0),
|
: _game_state(game_state), _should_close(false), _scroll_offset(0),
|
||||||
_prompt(""), _pending_action({ActionType::NONE}) {
|
_prompt(""), _pending_action({ActionType::NONE}) {
|
||||||
_input_view = std::make_unique<TextView>(&_input_buffer, false);
|
_input_view = std::make_unique<TextView>(&_input_buffer, false);
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ void Terminal::_on_ret_press(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_history.push_back(_prompt + "> " + command);
|
_history.push_back(_prompt + "> " + command);
|
||||||
_network->send(command);
|
_game_state->send_network_command(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::handle_input(SDL_Event* event, int window_x, int window_y, int window_gl_y) {
|
void Terminal::handle_input(SDL_Event* event, int window_x, int window_y, int window_gl_y) {
|
||||||
|
|||||||
@ -4,16 +4,17 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include "client_network.h"
|
|
||||||
#include "gfx/types.h"
|
#include "gfx/types.h"
|
||||||
#include "ui/text_buffer.h"
|
#include "ui/text_buffer.h"
|
||||||
#include "ui/text_view.h"
|
#include "ui/text_view.h"
|
||||||
#include "ui/i_window_content.h"
|
#include "ui/i_window_content.h"
|
||||||
#include "ui/window_action.h"
|
#include "ui/window_action.h"
|
||||||
|
|
||||||
|
class GameState;
|
||||||
|
|
||||||
class Terminal : public IWindowContent {
|
class Terminal : public IWindowContent {
|
||||||
public:
|
public:
|
||||||
Terminal(ClientNetwork* network);
|
Terminal(GameState* game_state);
|
||||||
~Terminal(void);
|
~Terminal(void);
|
||||||
|
|
||||||
void update(void) override;
|
void update(void) override;
|
||||||
@ -33,7 +34,7 @@ private:
|
|||||||
std::vector<std::string> _history;
|
std::vector<std::string> _history;
|
||||||
int _scroll_offset;
|
int _scroll_offset;
|
||||||
std::string _prompt;
|
std::string _prompt;
|
||||||
ClientNetwork* _network;
|
GameState* _game_state;
|
||||||
TextBuffer _input_buffer;
|
TextBuffer _input_buffer;
|
||||||
WindowAction _pending_action;
|
WindowAction _pending_action;
|
||||||
std::unique_ptr<TextView> _input_view;
|
std::unique_ptr<TextView> _input_view;
|
||||||
|
|||||||
@ -8,14 +8,14 @@
|
|||||||
#include <SDL3/SDL_video.h>
|
#include <SDL3/SDL_video.h>
|
||||||
|
|
||||||
#include "gfx/types.h"
|
#include "gfx/types.h"
|
||||||
#include "ui/editor.h"
|
#include "game_state.h"
|
||||||
#include "desktop.h"
|
#include "desktop.h"
|
||||||
#include "client_network.h"
|
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "ui/i_window_content.h"
|
#include "ui/i_window_content.h"
|
||||||
#include "ui/launcher.h"
|
#include "ui/launcher.h"
|
||||||
#include "ui/taskbar.h"
|
#include "ui/taskbar.h"
|
||||||
#include "ui/ui_window.h"
|
#include "ui/ui_window.h"
|
||||||
|
#include "ui/editor.h"
|
||||||
#include "ui/window_action.h"
|
#include "ui/window_action.h"
|
||||||
|
|
||||||
static const std::string& get_random_snippet(const std::vector<std::string>& snippets) {
|
static const std::string& get_random_snippet(const std::vector<std::string>& snippets) {
|
||||||
@ -26,9 +26,9 @@ static const std::string& get_random_snippet(const std::vector<std::string>& sni
|
|||||||
return snippets[rand() % snippets.size()];
|
return snippets[rand() % snippets.size()];
|
||||||
}
|
}
|
||||||
|
|
||||||
Desktop::Desktop(int screen_width, int screen_height, ClientNetwork* network) {
|
Desktop::Desktop(int screen_width, int screen_height, GameState* game_state) {
|
||||||
_taskbar = std::make_unique<Taskbar>(screen_width, screen_height);
|
_taskbar = std::make_unique<Taskbar>(screen_width, screen_height);
|
||||||
_network = network;
|
_game_state= game_state;
|
||||||
_focused_window = nullptr;
|
_focused_window = nullptr;
|
||||||
_launcher_is_open = false;
|
_launcher_is_open = false;
|
||||||
_launcher = std::make_unique<Launcher>(5, 0, 200); /* Tmp y-coord. */
|
_launcher = std::make_unique<Launcher>(5, 0, 200); /* Tmp y-coord. */
|
||||||
@ -133,7 +133,7 @@ void Desktop::handle_event(SDL_Event* event, int screen_width, int screen_height
|
|||||||
} else if(_launcher_is_open) {
|
} else if(_launcher_is_open) {
|
||||||
std::string app_to_launch = _launcher->handle_event(event, screen_height);
|
std::string app_to_launch = _launcher->handle_event(event, screen_height);
|
||||||
if(app_to_launch == "Terminal") {
|
if(app_to_launch == "Terminal") {
|
||||||
auto term = std::make_unique<Terminal>(_network);
|
auto term = std::make_unique<Terminal>(_game_state);
|
||||||
auto term_window = std::make_unique<UIWindow>("Terminal", 150, 150, 800, 500);
|
auto term_window = std::make_unique<UIWindow>("Terminal", 150, 150, 800, 500);
|
||||||
term_window->set_content(std::move(term));
|
term_window->set_content(std::move(term));
|
||||||
add_window(std::move(term_window));
|
add_window(std::move(term_window));
|
||||||
@ -182,13 +182,11 @@ void Desktop::update(float dt, int screen_width, int screen_height) {
|
|||||||
WindowAction action = content->get_pending_action();
|
WindowAction action = content->get_pending_action();
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case ActionType::WRITE_FILE: {
|
case ActionType::WRITE_FILE: {
|
||||||
std::string message = "WRITEF::" + action.payload1 + "::" + action.payload2;
|
_game_state->send_file_write_request(action.payload1, action.payload2);
|
||||||
_network->send(message);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ActionType::READ_FILE: {
|
case ActionType::READ_FILE: {
|
||||||
std::string message = "READF::" + action.payload1;
|
_game_state->send_file_read_request(action.payload1);
|
||||||
_network->send(message);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -4,13 +4,14 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include "client_network.h"
|
|
||||||
#include "gfx/types.h"
|
#include "gfx/types.h"
|
||||||
#include "ui/ui_window.h"
|
#include "ui/ui_window.h"
|
||||||
#include "ui/taskbar.h"
|
#include "ui/taskbar.h"
|
||||||
#include "ui/launcher.h"
|
#include "ui/launcher.h"
|
||||||
#include "ui/ui_renderer.h"
|
#include "ui/ui_renderer.h"
|
||||||
|
|
||||||
|
class GameState;
|
||||||
|
|
||||||
/* Animated background stuff. */
|
/* Animated background stuff. */
|
||||||
struct ScrollingText {
|
struct ScrollingText {
|
||||||
std::string text;
|
std::string text;
|
||||||
@ -22,7 +23,7 @@ struct ScrollingText {
|
|||||||
|
|
||||||
class Desktop {
|
class Desktop {
|
||||||
public:
|
public:
|
||||||
Desktop(int screen_width, int screen_height, ClientNetwork* network);
|
Desktop(int screen_width, int screen_height, GameState* game_state);
|
||||||
~Desktop(void);
|
~Desktop(void);
|
||||||
|
|
||||||
void add_window(std::unique_ptr<UIWindow> window);
|
void add_window(std::unique_ptr<UIWindow> window);
|
||||||
@ -41,7 +42,7 @@ private:
|
|||||||
std::unique_ptr<Taskbar> _taskbar;
|
std::unique_ptr<Taskbar> _taskbar;
|
||||||
std::unique_ptr<Launcher> _launcher;
|
std::unique_ptr<Launcher> _launcher;
|
||||||
UIWindow* _focused_window;
|
UIWindow* _focused_window;
|
||||||
ClientNetwork* _network;
|
GameState* _game_state;
|
||||||
std::vector<ScrollingText> _background_text;
|
std::vector<ScrollingText> _background_text;
|
||||||
std::vector<std::string> _snippets;
|
std::vector<std::string> _snippets;
|
||||||
bool _launcher_is_open;
|
bool _launcher_is_open;
|
||||||
|
|||||||
@ -35,6 +35,7 @@ public:
|
|||||||
int taskbar_height);
|
int taskbar_height);
|
||||||
void minimize(void);
|
void minimize(void);
|
||||||
void restore(void);
|
void restore(void);
|
||||||
|
void close(void) { _should_close = true; }
|
||||||
bool is_minimized(void) const;
|
bool is_minimized(void) const;
|
||||||
bool should_close(void) const;
|
bool should_close(void) const;
|
||||||
void set_focused(bool focused);
|
void set_focused(bool focused);
|
||||||
|
|||||||
@ -97,7 +97,10 @@ std::string CommandProcessor::process_command(const std::string& command) {
|
|||||||
"ON T1.id = T2.parent_id WHERE T1.name = 'bin' AND T2.name = ? "
|
"ON T1.id = T2.parent_id WHERE T1.name = 'bin' AND T2.name = ? "
|
||||||
"AND t1.machine_id = ?;"
|
"AND t1.machine_id = ?;"
|
||||||
<< script_filename << _session_machine->id
|
<< script_filename << _session_machine->id
|
||||||
>> std::tie(script_id, script_content);
|
>> [&](long long id, std::string content) {
|
||||||
|
script_id = id;
|
||||||
|
script_content = content;
|
||||||
|
};
|
||||||
|
|
||||||
if(script_id > 0) {
|
if(script_id > 0) {
|
||||||
bool is_remote = (_session_machine != _home_machine);
|
bool is_remote = (_session_machine != _home_machine);
|
||||||
|
|||||||
37
common/src/net/message_protocol.cpp
Normal file
37
common/src/net/message_protocol.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include <sstream>
|
||||||
|
#include "message_protocol.h"
|
||||||
|
|
||||||
|
namespace net_protocol {
|
||||||
|
|
||||||
|
std::string build_message(Opcode opcode, const std::vector<std::string>& args) {
|
||||||
|
std::string payload;
|
||||||
|
payload += static_cast<char>(opcode);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < args.size(); ++i) {
|
||||||
|
payload.append(args[i]);
|
||||||
|
if(i < args.size() - 1) {
|
||||||
|
payload += '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_message(const std::string& payload, Opcode& out_opcode,
|
||||||
|
std::vector<std::string>& out_args) {
|
||||||
|
out_args.clear();
|
||||||
|
if(payload.empty()) {
|
||||||
|
return; /* Might handle this as an error.. */
|
||||||
|
}
|
||||||
|
|
||||||
|
out_opcode = static_cast<Opcode>(payload[0]);
|
||||||
|
|
||||||
|
if(payload.length() > 1) {
|
||||||
|
std::stringstream ss(payload.substr(1));
|
||||||
|
std::string segment;
|
||||||
|
while(std::getline(ss, segment, '\0')) {
|
||||||
|
out_args.push_back(segment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}; /* namespace net_protocol. */
|
||||||
53
common/src/net/message_protocol.h
Normal file
53
common/src/net/message_protocol.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace net_protocol {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Defines the opcodes for client-server communication.
|
||||||
|
*
|
||||||
|
* Each message payload begins with one of these single-byte opcodes to identify
|
||||||
|
* the purpose of the message.
|
||||||
|
*/
|
||||||
|
enum class Opcode : uint8_t {
|
||||||
|
/* Client -> Server messages. */
|
||||||
|
C2S_CREATE_ACCOUNT,
|
||||||
|
C2S_LOGIN,
|
||||||
|
C2S_COMMAND,
|
||||||
|
C2S_WRITE_FILE,
|
||||||
|
C2S_READ_FILE,
|
||||||
|
|
||||||
|
/* Server -> Client messages. */
|
||||||
|
S2C_CREATE_ACCOUNT_SUCCESS,
|
||||||
|
S2C_CREATE_ACCOUNT_FAIL,
|
||||||
|
S2C_LOGIN_SUCCESS,
|
||||||
|
S2C_LOGIN_FAIL,
|
||||||
|
S2C_COMMAND_RESPONSE,
|
||||||
|
S2C_FILE_DATA,
|
||||||
|
S2C_DISCONNECT,
|
||||||
|
S2C_CLOSE_WINDOW,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Builds a raw message payload from an opcode and arguments.
|
||||||
|
* @param opcode The message type.
|
||||||
|
* @param args Vector of string arguments to be included in the message.
|
||||||
|
* @return A payload string to be sent over the network.
|
||||||
|
*/
|
||||||
|
std::string build_message(Opcode opcode, const std::vector<std::string>& args = {});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parse a raw message payload into its opcode and arguments.
|
||||||
|
* @param payload The raw payload received from the netwrok.
|
||||||
|
* @param out_opcode The output variable for the parsed opcode.
|
||||||
|
* @param out_args the output vector for the parsed string arguments.
|
||||||
|
*
|
||||||
|
* The format is: [1-byte Opcode][arg1]\0[arg2]\0...
|
||||||
|
*/
|
||||||
|
void parse_message(const std::string& payload, Opcode& out_opcode,
|
||||||
|
std::vector<std::string>& out_args);
|
||||||
|
|
||||||
|
}; /* namespace net_protocol. */
|
||||||
@ -4,6 +4,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "network_manager.h"
|
#include "network_manager.h"
|
||||||
|
#include "net/message_protocol.h"
|
||||||
#include "db/database_manager.h"
|
#include "db/database_manager.h"
|
||||||
|
|
||||||
#include "asio/error_code.hpp"
|
#include "asio/error_code.hpp"
|
||||||
@ -95,10 +96,6 @@ void NetworkManager::start_accept(void) {
|
|||||||
},
|
},
|
||||||
[this, new_connection]() { this->on_disconnect(new_connection); });
|
[this, new_connection]() { this->on_disconnect(new_connection); });
|
||||||
|
|
||||||
/* Send initial prompt. */
|
|
||||||
std::string prompt = "\n" + get_full_path(new_player_ptr->cmd_processor->get_current_dir());
|
|
||||||
new_connection->send(prompt);
|
|
||||||
|
|
||||||
_connections.push_back(new_connection);
|
_connections.push_back(new_connection);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Accept error: %s\n", ec.message().c_str());
|
fprintf(stderr, "Accept error: %s\n", ec.message().c_str());
|
||||||
@ -108,17 +105,6 @@ void NetworkManager::start_accept(void) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<std::string> split_message(const std::string& s, const std::string& delimiter) {
|
|
||||||
std::vector<std::string> tokens;
|
|
||||||
size_t start = 0, end = 0;
|
|
||||||
while((end = s.find(delimiter, start)) != std::string::npos) {
|
|
||||||
tokens.push_back(s.substr(start, end-start));
|
|
||||||
start = end + delimiter.length();
|
|
||||||
}
|
|
||||||
tokens.push_back(s.substr(start));
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
|
void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
|
||||||
const std::string& message) {
|
const std::string& message) {
|
||||||
Player* player = _players[connection->get_id()].get();
|
Player* player = _players[connection->get_id()].get();
|
||||||
@ -127,94 +113,103 @@ void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
net_protocol::Opcode opcode;
|
||||||
|
std::vector<std::string> args;
|
||||||
|
net_protocol::parse_message(message, opcode, args);
|
||||||
|
|
||||||
if(player->state == PlayerState::AUTHENTICATING) {
|
if(player->state == PlayerState::AUTHENTICATING) {
|
||||||
if(message.rfind("C_ACC::", 0) == 0) {
|
switch(opcode) {
|
||||||
std::string payload = message.substr(7);
|
case net_protocol::Opcode::C2S_CREATE_ACCOUNT:
|
||||||
auto parts = split_message(payload, "::");
|
if(args.size() == 3) {
|
||||||
if(parts.size() == 3) {
|
if(_db_manager->create_player(args[0], args[1], args[2],
|
||||||
if(_db_manager->create_player(parts[0], parts[1], parts[2],
|
|
||||||
_machine_manager.get_vfs_template())) {
|
_machine_manager.get_vfs_template())) {
|
||||||
long long machine_id = _db_manager->players().get_home_machine_id(parts[0]);
|
long long machine_id = _db_manager->players().get_home_machine_id(args[0]);
|
||||||
Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get());
|
Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get());
|
||||||
delete player->cmd_processor; /* Delete old command processor. */
|
delete player->cmd_processor; /* Delete old command processor. */
|
||||||
player->cmd_processor = new CommandProcessor(home_machine, _world_machines,
|
player->cmd_processor = new CommandProcessor(home_machine, _world_machines,
|
||||||
_db_manager.get(),
|
_db_manager.get(),
|
||||||
&_machine_manager);
|
&_machine_manager);
|
||||||
player->state = PlayerState::ACTIVE;
|
player->state = PlayerState::ACTIVE;
|
||||||
connection->send("C_ACC_SUCCESS");
|
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CREATE_ACCOUNT_SUCCESS));
|
||||||
/* send initial prompt. */
|
/* send initial prompt. */
|
||||||
std::string prompt = "\n" + get_full_path(player->cmd_processor->get_current_dir());
|
std::string prompt = "\n" + get_full_path(player->cmd_processor->get_current_dir());
|
||||||
connection->send(prompt);
|
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_COMMAND_RESPONSE,
|
||||||
|
{prompt}));
|
||||||
} else {
|
} else {
|
||||||
connection->send("C_ACC_FAIL");
|
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CREATE_ACCOUNT_FAIL,
|
||||||
|
{"Username already exists."}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(message.rfind("LOGIN::", 0) == 0) {
|
break;
|
||||||
std::string payload = message.substr(7);
|
case net_protocol::Opcode::C2S_LOGIN:
|
||||||
auto parts = split_message(payload, "::");
|
if(args.size() == 2) {
|
||||||
if(parts.size() == 2) {
|
if(_db_manager->players().authenticate(args[0], args[1])) {
|
||||||
if(_db_manager->players().authenticate(parts[0], parts[1])) {
|
long long machine_id = _db_manager->players().get_home_machine_id(args[0]);
|
||||||
long long machine_id = _db_manager->players().get_home_machine_id(parts[0]);
|
printf("DEBUG: Loading machine %lld for player %s\n", machine_id, args[0].c_str());
|
||||||
printf("DEBUG: Loading machine %lld for player %s\n", machine_id, parts[0].c_str());
|
|
||||||
Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get());
|
Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get());
|
||||||
delete player->cmd_processor; /* Delete old command processor. */
|
delete player->cmd_processor; /* Delete old command processor. */
|
||||||
player->cmd_processor = new CommandProcessor(home_machine, _world_machines,
|
player->cmd_processor = new CommandProcessor(home_machine, _world_machines,
|
||||||
_db_manager.get(), &_machine_manager);
|
_db_manager.get(), &_machine_manager);
|
||||||
|
|
||||||
player->state = PlayerState::ACTIVE;
|
player->state = PlayerState::ACTIVE;
|
||||||
connection->send("LOGIN_SUCCESS");
|
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_LOGIN_SUCCESS));
|
||||||
/* Send initial prompt. */
|
/* Send initial prompt. */
|
||||||
std::string prompt = "\n" + get_full_path(player->cmd_processor->get_current_dir());
|
std::string prompt = "\n" + get_full_path(player->cmd_processor->get_current_dir());
|
||||||
connection->send(prompt);
|
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_COMMAND_RESPONSE,
|
||||||
|
{prompt}));
|
||||||
} else {
|
} else {
|
||||||
connection->send("LOGIN_FAIL");
|
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_LOGIN_FAIL,
|
||||||
|
{"Invalid username or password."}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
break;
|
||||||
/* Ignore all other messages while authing. */
|
default:
|
||||||
connection->send("ERR: Not authenticated.\n");
|
/* ignore all other messages while authing. */
|
||||||
|
fprintf(stderr, "Received invalid opcode %d during auth.\n", static_cast<int>(opcode));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* === PLAYER BECOMES ACTIVE HERE === */
|
/* === PLAYER BECOMES ACTIVE HERE === */
|
||||||
|
|
||||||
/* Check for "special" message prefixes. */
|
switch(opcode) {
|
||||||
if(message.rfind("WRITEF::", 0) == 0) {
|
case net_protocol::Opcode::C2S_WRITE_FILE:
|
||||||
/* Message format: WRITEF::/path/to/file::content. */
|
if(args.size() == 2) {
|
||||||
std::string payload = message.substr(8);
|
fprintf(stderr, "[Player %u] Write file: '%s'\n", player->id, args[0].c_str());
|
||||||
size_t separator_pos = payload.find("::");
|
player->cmd_processor->write_file(args[0], args[1]);
|
||||||
if(separator_pos != std::string::npos) {
|
|
||||||
std::string filepath = payload.substr(0, separator_pos);
|
|
||||||
std::string content = payload.substr(separator_pos+2);
|
|
||||||
|
|
||||||
fprintf(stderr, "[Player %u] Write file: \'%s\'\n", player->id, filepath.c_str());
|
|
||||||
player->cmd_processor->write_file(filepath, content);
|
|
||||||
/* Response not required for a file write. */
|
/* Response not required for a file write. */
|
||||||
}
|
}
|
||||||
return;
|
break;
|
||||||
}
|
case net_protocol::Opcode::C2S_READ_FILE:
|
||||||
if(message.rfind("READF::", 0) == 0) {
|
if(!args.empty()) {
|
||||||
std::string filepath = message.substr(7);
|
fprintf(stderr, "[Player %u] Read file: '%s'\n", player->id, args[0].c_str());
|
||||||
fprintf(stderr, "[Player %u] Read file: '%s'\n", player->id, filepath.c_str());
|
std::string content = player->cmd_processor->read_file(args[0]);
|
||||||
std::string content = player->cmd_processor->read_file(filepath);
|
|
||||||
/* Send the content back to the client. */
|
/* Send the content back to the client. */
|
||||||
connection->send("FILEDATA::" + filepath + "::" + content);
|
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_FILE_DATA,
|
||||||
return;
|
{args[0], content}));
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
/* If no prefix, treat as normal terminal command. */
|
case net_protocol::Opcode::C2S_COMMAND:
|
||||||
fprintf(stderr, "[Player %u] Command: '%s'\n", player->id, message.c_str());
|
if(!args.empty()) {
|
||||||
std::string response = player->cmd_processor->process_command(message);
|
fprintf(stderr, "[Player %u] Command: '%s'\n", player->id, args[0].c_str());
|
||||||
|
std::string response = player->cmd_processor->process_command(args[0]);
|
||||||
|
|
||||||
if(response == "__CLOSE_CONNECTION__") {
|
if(response == "__CLOSE_CONNECTION__") {
|
||||||
connection->send(response);
|
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CLOSE_WINDOW));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string new_prompt = get_full_path(player->cmd_processor->get_current_dir());
|
std::string new_prompt = get_full_path(player->cmd_processor->get_current_dir());
|
||||||
response += "\n" + new_prompt;
|
response += "\n" + new_prompt;
|
||||||
|
|
||||||
connection->send(response);
|
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_COMMAND_RESPONSE,
|
||||||
|
{response}));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "Received invalid opcode %d from active player.\n",
|
||||||
|
static_cast<int>(opcode));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkManager::on_disconnect(std::shared_ptr<net::TcpConnection> connection) {
|
void NetworkManager::on_disconnect(std::shared_ptr<net::TcpConnection> connection) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user