Refactored client-side rendering code to use a Color struct rather than raw float arrays.
134 lines
4.3 KiB
C++
134 lines
4.3 KiB
C++
#include <cstdio>
|
|
#include <exception>
|
|
#include <functional>
|
|
#include <memory>
|
|
|
|
#include "network_manager.h"
|
|
|
|
#include "asio/error_code.hpp"
|
|
#include "asio/ip/tcp.hpp"
|
|
#include "command_processor.h"
|
|
#include "player.h"
|
|
#include "machine.h"
|
|
#include "net/tcp_connection.h"
|
|
#include "vfs.h"
|
|
|
|
NetworkManager::NetworkManager(void) : _acceptor(_io_context) {
|
|
/* World setup. */
|
|
_world_machines["8.8.8.8"] = _machine_manager.create_machine(1000, "dns.google", "npc");
|
|
_world_machines["10.0.2.15"] = _machine_manager.create_machine(1001, "corp.internal", "npc");
|
|
|
|
/* Specific npc services. */
|
|
_world_machines["8.8.8.8"]->services[80] = "HTTPD v2.4";
|
|
_world_machines["10.0.2.15"]->services[21] = "FTPd v3.0";
|
|
|
|
fprintf(stderr, "Created world with %zu networks\n", _world_machines.size());
|
|
}
|
|
|
|
NetworkManager::~NetworkManager(void) {
|
|
for(auto const& [ip, machine] : _world_machines) {
|
|
delete machine;
|
|
}
|
|
stop();
|
|
}
|
|
|
|
void NetworkManager::stop(void) {
|
|
_io_context.stop();
|
|
if(_context_thread.joinable()) {
|
|
_context_thread.join();
|
|
}
|
|
}
|
|
|
|
void NetworkManager::start(uint16_t port) {
|
|
try {
|
|
asio::ip::tcp::endpoint endpoint(asio::ip::tcp::v4(), port);
|
|
_acceptor.open(endpoint.protocol());
|
|
_acceptor.set_option(asio::ip::tcp::acceptor::reuse_address(true));
|
|
_acceptor.bind(endpoint);
|
|
_acceptor.listen();
|
|
|
|
fprintf(stderr, "BettolaServer started on port %d\n", port);
|
|
|
|
start_accept();
|
|
|
|
/* Run io_context in its own thread so it doesn't block main. */
|
|
_context_thread = std::thread([this]() { _io_context.run(); });
|
|
} catch (const std::exception& e) {
|
|
fprintf(stderr, "BettolaServer exception: %s\n", e.what());
|
|
}
|
|
}
|
|
|
|
void NetworkManager::start_accept(void) {
|
|
_acceptor.async_accept(
|
|
[this](const asio::error_code& ec, asio::ip::tcp::socket socket) {
|
|
if(!ec) {
|
|
fprintf(stderr, "New connection from: %s\n",
|
|
socket.remote_endpoint().address().to_string().c_str());
|
|
|
|
auto new_connection =
|
|
std::make_shared<net::TcpConnection>(_io_context, std::move(socket));
|
|
|
|
/* Create a new player for this connection. */
|
|
uint32_t player_id = _next_player_id++;
|
|
Machine* player_machine = _machine_manager.create_machine(player_id, "player.home",
|
|
"player");
|
|
auto new_player = std::make_unique<Player>(player_id, player_machine, _world_machines);
|
|
Player* new_player_ptr = new_player.get();
|
|
_players[player_id] = std::move(new_player);
|
|
new_connection->set_id(player_id);
|
|
|
|
/* Callback for new connection. Capture new_connection's shared_ptr
|
|
* to keep it alive and ident it.
|
|
*/
|
|
new_connection->start(
|
|
[this, new_connection](const std::string& msg) {
|
|
this->on_message(new_connection, msg);
|
|
},
|
|
[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);
|
|
} else {
|
|
fprintf(stderr, "Accept error: %s\n", ec.message().c_str());
|
|
}
|
|
/* Continue listening for the next connection. */
|
|
start_accept();
|
|
});
|
|
}
|
|
|
|
void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
|
|
const std::string& message) {
|
|
Player* player = _players[connection->get_id()].get();
|
|
if(!player) {
|
|
fprintf(stderr, "Error: Receiving message from unknown player.\n");
|
|
return;
|
|
}
|
|
|
|
fprintf(stderr, "[Player %u] Command: '%s'\n", player->id, message.c_str());
|
|
|
|
std::string response = player->cmd_processor->process_command(message);
|
|
|
|
if(response == "__CLOSE_CONNECTION__") {
|
|
connection->send(response);
|
|
/* Just let me close the f.cking terminal? */
|
|
return;
|
|
}
|
|
|
|
std::string new_prompt = get_full_path(player->cmd_processor->get_current_dir());
|
|
response += "\n" + new_prompt;
|
|
|
|
connection->send(response);
|
|
}
|
|
|
|
void NetworkManager::on_disconnect(std::shared_ptr<net::TcpConnection> connection) {
|
|
uint32_t player_id = connection->get_id();
|
|
fprintf(stderr, "[Player %u] Disconnected.\n", player_id);
|
|
_connections.erase(
|
|
std::remove(_connections.begin(), _connections.end(), connection), _connections.end());
|
|
|
|
_players.erase(player_id);
|
|
}
|