mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-17 23:39:18 +02:00
milek7/sim branch network code import
This commit is contained in:
244
network/backend/asio.cpp
Normal file
244
network/backend/asio.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#include "stdafx.h"
|
||||
#include "network/backend/asio.h"
|
||||
|
||||
#include "sn_utils.h"
|
||||
#include "Logs.h"
|
||||
|
||||
network::tcp::connection::connection(asio::io_context &io_ctx, bool client, size_t counter)
|
||||
: network::connection(client, counter),
|
||||
m_socket(io_ctx)
|
||||
{
|
||||
m_header_buffer.resize(8);
|
||||
}
|
||||
|
||||
network::tcp::connection::~connection()
|
||||
{
|
||||
m_socket.shutdown(m_socket.shutdown_both);
|
||||
m_socket.close();
|
||||
}
|
||||
|
||||
void network::tcp::connection::disconnect()
|
||||
{
|
||||
network::connection::disconnect();
|
||||
}
|
||||
|
||||
void network::tcp::connection::send_data(std::shared_ptr<std::string> buffer)
|
||||
{
|
||||
asio::async_write(m_socket, asio::buffer(*buffer.get()), std::bind(&connection::send_complete, this, buffer));
|
||||
}
|
||||
|
||||
void network::tcp::connection::connected()
|
||||
{
|
||||
network::connection::connected();
|
||||
read_header();
|
||||
}
|
||||
|
||||
void network::tcp::connection::read_header()
|
||||
{
|
||||
asio::async_read(m_socket, asio::buffer(m_header_buffer),
|
||||
std::bind(&connection::handle_header, this,
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void network::tcp::connection::handle_header(const asio::error_code &err, size_t bytes_transferred)
|
||||
{
|
||||
if (err) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
std::istringstream header(m_header_buffer);
|
||||
if (m_header_buffer.size() != bytes_transferred) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t sig = sn_utils::ld_uint32(header);
|
||||
if (sig != NETWORK_MAGIC) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t len = sn_utils::ld_uint32(header);
|
||||
m_body_buffer.resize(len);
|
||||
if (len > MAX_MSG_SIZE) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
asio::async_read(m_socket, asio::buffer(m_body_buffer),
|
||||
std::bind(&connection::handle_data, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void network::tcp::connection::handle_data(const asio::error_code &err, size_t bytes_transferred)
|
||||
{
|
||||
if (err) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_body_buffer.size() != bytes_transferred) {
|
||||
disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
std::istringstream stream(m_body_buffer);
|
||||
std::shared_ptr<message> msg = deserialize_message(stream);
|
||||
if (message_handler)
|
||||
message_handler(*msg);
|
||||
|
||||
read_header();
|
||||
}
|
||||
|
||||
void network::tcp::connection::write_message(const message &msg, std::ostream &stream)
|
||||
{
|
||||
size_t beg = (size_t)stream.tellp();
|
||||
|
||||
sn_utils::ls_uint32(stream, NETWORK_MAGIC);
|
||||
sn_utils::ls_uint32(stream, 0);
|
||||
|
||||
serialize_message(msg, stream);
|
||||
|
||||
size_t size = (size_t)stream.tellp() - beg - 8;
|
||||
if (size > MAX_MSG_SIZE) {
|
||||
ErrorLog("net: message too big", logtype::net);
|
||||
return;
|
||||
}
|
||||
stream.seekp(beg + 4, std::ios_base::beg);
|
||||
sn_utils::ls_uint32(stream, size);
|
||||
|
||||
stream.seekp(0, std::ios_base::end);
|
||||
}
|
||||
|
||||
void network::tcp::connection::send_messages(const std::vector<std::shared_ptr<message> > &messages)
|
||||
{
|
||||
if (messages.size() == 0)
|
||||
return;
|
||||
|
||||
std::ostringstream stream;
|
||||
for (auto const &msg : messages)
|
||||
write_message(*msg.get(), stream);
|
||||
|
||||
stream.flush();
|
||||
send_data(std::make_shared<std::string>(stream.str()));
|
||||
}
|
||||
|
||||
void network::tcp::connection::send_message(const message &msg)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
write_message(msg, stream);
|
||||
|
||||
stream.flush();
|
||||
send_data(std::make_shared<std::string>(stream.str()));
|
||||
}
|
||||
|
||||
// -----------------
|
||||
|
||||
network::tcp::server::server(std::shared_ptr<std::istream> buf, asio::io_context &io_ctx, const std::string &host, uint32_t port)
|
||||
: network::server(buf), m_acceptor(io_ctx)
|
||||
{
|
||||
auto endpoint = asio::ip::tcp::endpoint(asio::ip::address::from_string(host), port);
|
||||
m_acceptor.open(endpoint.protocol());
|
||||
m_acceptor.set_option(asio::socket_base::reuse_address(true));
|
||||
m_acceptor.set_option(asio::ip::tcp::no_delay(true));
|
||||
m_acceptor.bind(endpoint);
|
||||
m_acceptor.listen(10);
|
||||
|
||||
accept_conn();
|
||||
}
|
||||
|
||||
void network::tcp::server::accept_conn()
|
||||
{
|
||||
std::shared_ptr<connection> conn = std::make_shared<connection>(m_acceptor.get_executor().context());
|
||||
conn->set_handler(std::bind(&server::handle_message, this, conn, std::placeholders::_1));
|
||||
|
||||
m_acceptor.async_accept(conn->m_socket, std::bind(&server::handle_accept, this, conn, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void network::tcp::server::handle_accept(std::shared_ptr<connection> conn, const asio::error_code &err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
clients.emplace_back(conn);
|
||||
conn->connected();
|
||||
}
|
||||
else
|
||||
{
|
||||
conn->state = connection::DEAD;
|
||||
WriteLog(std::string("net: failed to accept client: " + err.message()), logtype::net);
|
||||
}
|
||||
|
||||
accept_conn();
|
||||
}
|
||||
|
||||
// ------------------
|
||||
|
||||
network::tcp::client::client(asio::io_context &io_ctxx, const std::string &hostarg, uint32_t portarg)
|
||||
: host(hostarg), port(portarg), io_ctx(io_ctxx)
|
||||
{
|
||||
}
|
||||
|
||||
void network::tcp::client::connect()
|
||||
{
|
||||
if (this->conn)
|
||||
return;
|
||||
|
||||
std::shared_ptr<connection> conn = std::make_shared<connection>(io_ctx, true, resume_frame_counter);
|
||||
conn->set_handler(std::bind(&client::handle_message, this, conn, std::placeholders::_1));
|
||||
|
||||
asio::ip::tcp::endpoint endpoint(
|
||||
asio::ip::address::from_string(host), port);
|
||||
conn->m_socket.open(endpoint.protocol());
|
||||
conn->m_socket.set_option(asio::ip::tcp::no_delay(true));
|
||||
conn->m_socket.async_connect(endpoint,
|
||||
std::bind(&client::handle_accept, this, std::placeholders::_1));
|
||||
|
||||
this->conn = conn;
|
||||
|
||||
}
|
||||
|
||||
void network::tcp::client::handle_accept(const asio::error_code &err)
|
||||
{
|
||||
if (!err)
|
||||
{
|
||||
conn->connected();
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog(std::string("net: failed to connect: " + err.message()), logtype::net);
|
||||
conn->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
network::tcp::asio_manager::asio_manager() {
|
||||
backend_list().emplace("tcp", this);
|
||||
}
|
||||
|
||||
std::shared_ptr<network::server> network::tcp::asio_manager::create_server(std::shared_ptr<std::fstream> backbuffer, const std::string &conf) {
|
||||
std::istringstream stream(conf);
|
||||
|
||||
std::string host;
|
||||
std::getline(stream, host, ':');
|
||||
|
||||
int port;
|
||||
stream >> port;
|
||||
|
||||
return std::make_shared<tcp::server>(backbuffer, io_context, host, port);
|
||||
}
|
||||
|
||||
std::shared_ptr<network::client> network::tcp::asio_manager::create_client(const std::string &conf) {
|
||||
std::istringstream stream(conf);
|
||||
|
||||
std::string host;
|
||||
std::getline(stream, host, ':');
|
||||
|
||||
int port;
|
||||
stream >> port;
|
||||
|
||||
return std::make_shared<tcp::client>(io_context, host, port);
|
||||
}
|
||||
|
||||
void network::tcp::asio_manager::update() {
|
||||
io_context.restart();
|
||||
io_context.poll();
|
||||
}
|
||||
82
network/backend/asio.h
Normal file
82
network/backend/asio.h
Normal file
@@ -0,0 +1,82 @@
|
||||
#define ASIO_STANDALONE
|
||||
|
||||
#ifdef DBG_NEW
|
||||
#undef new
|
||||
#endif
|
||||
|
||||
#include <asio.hpp>
|
||||
|
||||
#include "network/network.h"
|
||||
|
||||
namespace network::tcp
|
||||
{
|
||||
const uint32_t NETWORK_MAGIC = 0x37305545;
|
||||
const uint32_t MAX_MSG_SIZE = 100000;
|
||||
|
||||
class connection : public network::connection
|
||||
{
|
||||
friend class server;
|
||||
friend class client;
|
||||
|
||||
public:
|
||||
connection(asio::io_context &io_ctx, bool client = false, size_t counter = 0);
|
||||
~connection();
|
||||
|
||||
virtual void connected() override;
|
||||
virtual void disconnect() override;
|
||||
virtual void send_messages(const std::vector<std::shared_ptr<message> > &messages) override;
|
||||
virtual void send_message(const message &msg) override;
|
||||
|
||||
asio::ip::tcp::socket m_socket;
|
||||
|
||||
private:
|
||||
std::string m_header_buffer;
|
||||
std::string m_body_buffer;
|
||||
|
||||
void write_message(const message &msg, std::ostream &stream);
|
||||
void send_data(std::shared_ptr<std::string> buffer);
|
||||
void read_header();
|
||||
void handle_header(const asio::error_code &err, size_t bytes_transferred);
|
||||
void handle_data(const asio::error_code &err, size_t bytes_transferred);
|
||||
};
|
||||
|
||||
class server : public network::server
|
||||
{
|
||||
private:
|
||||
void accept_conn();
|
||||
void handle_accept(std::shared_ptr<connection> conn, const asio::error_code &err);
|
||||
|
||||
asio::ip::tcp::acceptor m_acceptor;
|
||||
|
||||
public:
|
||||
server(std::shared_ptr<std::istream> buf, asio::io_context &io_ctx, const std::string &host, uint32_t port);
|
||||
};
|
||||
|
||||
class client : public network::client
|
||||
{
|
||||
private:
|
||||
void handle_accept(const asio::error_code &err);
|
||||
asio::io_context &io_ctx;
|
||||
std::string host;
|
||||
uint32_t port;
|
||||
|
||||
protected:
|
||||
virtual void connect() override;
|
||||
|
||||
public:
|
||||
client(asio::io_context &io_ctx, const std::string &host, uint32_t port);
|
||||
};
|
||||
|
||||
class asio_manager : public network::backend_manager {
|
||||
asio::io_context io_context;
|
||||
|
||||
public:
|
||||
asio_manager();
|
||||
|
||||
virtual std::shared_ptr<network::server> create_server(std::shared_ptr<std::fstream>, const std::string &conf) override;
|
||||
virtual std::shared_ptr<network::client> create_client(const std::string &conf) override;
|
||||
virtual void update() override;
|
||||
};
|
||||
|
||||
asio_manager manager;
|
||||
}
|
||||
89
network/manager.cpp
Normal file
89
network/manager.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "network/manager.h"
|
||||
|
||||
#include "simulation.h"
|
||||
#include "Logs.h"
|
||||
|
||||
network::server_manager::server_manager()
|
||||
{
|
||||
backbuffer = std::make_shared<std::fstream>("backbuffer.bin", std::ios::out | std::ios::in | std::ios::trunc | std::ios::binary);
|
||||
}
|
||||
|
||||
command_queue::commands_map network::server_manager::pop_commands()
|
||||
{
|
||||
command_queue::commands_map map;
|
||||
|
||||
for (auto srv : servers)
|
||||
add_to_dequemap(map, srv->pop_commands());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
void network::server_manager::push_delta(double render_dt, double dt, double sync, const command_queue::commands_map &commands)
|
||||
{
|
||||
if (dt == 0.0 && commands.empty())
|
||||
return;
|
||||
|
||||
frame_info msg;
|
||||
msg.render_dt = render_dt;
|
||||
msg.dt = dt;
|
||||
msg.sync = sync;
|
||||
msg.commands = commands;
|
||||
|
||||
for (auto srv : servers)
|
||||
srv->push_delta(msg);
|
||||
|
||||
serialize_message(msg, *backbuffer.get());
|
||||
}
|
||||
|
||||
void network::server_manager::create_server(const std::string &backend, const std::string &conf)
|
||||
{
|
||||
auto it = backend_list().find(backend);
|
||||
if (it == backend_list().end()) {
|
||||
ErrorLog("net: unknown backend: " + backend);
|
||||
return;
|
||||
}
|
||||
|
||||
servers.emplace_back(it->second->create_server(backbuffer, conf));
|
||||
}
|
||||
|
||||
network::manager::manager()
|
||||
{
|
||||
}
|
||||
|
||||
void network::manager::update()
|
||||
{
|
||||
for (auto &backend : backend_list())
|
||||
backend.second->update();
|
||||
|
||||
if (client)
|
||||
client->update();
|
||||
}
|
||||
|
||||
void network::manager::create_server(const std::string &backend, const std::string &conf)
|
||||
{
|
||||
servers.emplace();
|
||||
servers->create_server(backend, conf);
|
||||
}
|
||||
|
||||
void network::manager::connect(const std::string &backend, const std::string &conf)
|
||||
{
|
||||
auto it = backend_list().find(backend);
|
||||
if (it == backend_list().end()) {
|
||||
ErrorLog("net: unknown backend: " + backend);
|
||||
return;
|
||||
}
|
||||
|
||||
client = it->second->create_client(conf);
|
||||
}
|
||||
|
||||
//std::unordered_map<std::string, std::shared_ptr<network::backend_manager>> network::backend_list;
|
||||
34
network/manager.h
Normal file
34
network/manager.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "network/network.h"
|
||||
#include "command.h"
|
||||
|
||||
namespace network
|
||||
{
|
||||
class server_manager
|
||||
{
|
||||
private:
|
||||
std::vector<std::shared_ptr<server>> servers;
|
||||
std::shared_ptr<std::fstream> backbuffer;
|
||||
|
||||
public:
|
||||
server_manager();
|
||||
|
||||
void push_delta(double render_dt, double dt, double sync, const command_queue::commands_map &commands);
|
||||
command_queue::commands_map pop_commands();
|
||||
void create_server(const std::string &backend, const std::string &conf);
|
||||
};
|
||||
|
||||
class manager
|
||||
{
|
||||
public:
|
||||
manager();
|
||||
|
||||
std::optional<server_manager> servers;
|
||||
std::shared_ptr<network::client> client;
|
||||
|
||||
void create_server(const std::string &backend, const std::string &conf);
|
||||
void connect(const std::string &backend, const std::string &conf);
|
||||
void update();
|
||||
};
|
||||
}
|
||||
124
network/message.cpp
Normal file
124
network/message.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include "stdafx.h"
|
||||
#include "network/message.h"
|
||||
#include "sn_utils.h"
|
||||
|
||||
void network::client_hello::serialize(std::ostream &stream) const
|
||||
{
|
||||
sn_utils::ls_int32(stream, version);
|
||||
sn_utils::ls_uint32(stream, start_packet);
|
||||
}
|
||||
|
||||
void network::client_hello::deserialize(std::istream &stream)
|
||||
{
|
||||
version = sn_utils::ld_int32(stream);
|
||||
start_packet = sn_utils::ld_uint32(stream);
|
||||
}
|
||||
|
||||
void network::server_hello::serialize(std::ostream &stream) const
|
||||
{
|
||||
sn_utils::ls_uint32(stream, seed);
|
||||
sn_utils::ls_int64(stream, timestamp);
|
||||
}
|
||||
|
||||
void network::server_hello::deserialize(std::istream &stream)
|
||||
{
|
||||
seed = sn_utils::ld_uint32(stream);
|
||||
timestamp = sn_utils::ld_int64(stream);
|
||||
}
|
||||
|
||||
void ::network::request_command::serialize(std::ostream &stream) const
|
||||
{
|
||||
sn_utils::ls_uint32(stream, commands.size());
|
||||
for (auto const &kv : commands)
|
||||
{
|
||||
sn_utils::ls_uint32(stream, kv.first);
|
||||
sn_utils::ls_uint32(stream, kv.second.size());
|
||||
for (command_data const &data : kv.second)
|
||||
{
|
||||
sn_utils::ls_uint32(stream, (uint32_t)data.command);
|
||||
sn_utils::ls_int32(stream, data.action);
|
||||
sn_utils::ls_float64(stream, data.param1);
|
||||
sn_utils::ls_float64(stream, data.param2);
|
||||
sn_utils::ls_float64(stream, data.time_delta);
|
||||
|
||||
sn_utils::s_bool(stream, data.freefly);
|
||||
sn_utils::s_vec3(stream, data.location);
|
||||
|
||||
sn_utils::s_str(stream, data.payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void network::request_command::deserialize(std::istream &stream)
|
||||
{
|
||||
uint32_t commands_size = sn_utils::ld_uint32(stream);
|
||||
for (uint32_t i = 0; i < commands_size; i++)
|
||||
{
|
||||
uint32_t recipient = sn_utils::ld_uint32(stream);
|
||||
uint32_t sequence_size = sn_utils::ld_uint32(stream);
|
||||
|
||||
command_queue::commanddata_sequence sequence;
|
||||
for (uint32_t i = 0; i < sequence_size; i++)
|
||||
{
|
||||
command_data data;
|
||||
data.command = (user_command)sn_utils::ld_uint32(stream);
|
||||
data.action = sn_utils::ld_int32(stream);
|
||||
data.param1 = sn_utils::ld_float64(stream);
|
||||
data.param2 = sn_utils::ld_float64(stream);
|
||||
data.time_delta = sn_utils::ld_float64(stream);
|
||||
|
||||
data.freefly = sn_utils::d_bool(stream);
|
||||
data.location = sn_utils::d_vec3(stream);
|
||||
|
||||
data.payload = sn_utils::d_str(stream);
|
||||
|
||||
sequence.emplace_back(data);
|
||||
}
|
||||
|
||||
commands.emplace(recipient, sequence);
|
||||
}
|
||||
}
|
||||
|
||||
void network::frame_info::serialize(std::ostream &stream) const
|
||||
{
|
||||
sn_utils::ls_float64(stream, render_dt);
|
||||
sn_utils::ls_float64(stream, dt);
|
||||
sn_utils::ls_float64(stream, sync);
|
||||
|
||||
request_command::serialize(stream);
|
||||
}
|
||||
|
||||
void network::frame_info::deserialize(std::istream &stream)
|
||||
{
|
||||
render_dt = sn_utils::ld_float64(stream);
|
||||
dt = sn_utils::ld_float64(stream);
|
||||
sync = sn_utils::ld_float64(stream);
|
||||
|
||||
request_command::deserialize(stream);
|
||||
}
|
||||
|
||||
std::shared_ptr<network::message> network::deserialize_message(std::istream &stream)
|
||||
{
|
||||
message::type_e type = (message::type_e)sn_utils::ld_uint16(stream);
|
||||
|
||||
std::shared_ptr<message> msg;
|
||||
|
||||
if (type == message::CLIENT_HELLO)
|
||||
msg = std::make_shared<client_hello>();
|
||||
else if (type == message::SERVER_HELLO)
|
||||
msg = std::make_shared<server_hello>();
|
||||
else if (type == message::FRAME_INFO)
|
||||
msg = std::make_shared<frame_info>();
|
||||
else if (type == message::REQUEST_COMMAND)
|
||||
msg = std::make_shared<request_command>();
|
||||
|
||||
msg->deserialize(stream);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
void network::serialize_message(const message &msg, std::ostream &stream)
|
||||
{
|
||||
sn_utils::ls_uint16(stream, (uint16_t)msg.type);
|
||||
msg.serialize(stream);
|
||||
}
|
||||
73
network/message.h
Normal file
73
network/message.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include "network/message.h"
|
||||
#include "command.h"
|
||||
#include <queue>
|
||||
|
||||
namespace network
|
||||
{
|
||||
struct message
|
||||
{
|
||||
enum type_e
|
||||
{
|
||||
CLIENT_HELLO = 0,
|
||||
SERVER_HELLO,
|
||||
FRAME_INFO,
|
||||
REQUEST_COMMAND,
|
||||
TYPE_MAX
|
||||
};
|
||||
|
||||
type_e type;
|
||||
|
||||
message(type_e t) : type(t) {}
|
||||
virtual void serialize(std::ostream &stream) const {}
|
||||
virtual void deserialize(std::istream &stream) {}
|
||||
};
|
||||
|
||||
struct client_hello : public message
|
||||
{
|
||||
client_hello() : message(CLIENT_HELLO) {}
|
||||
|
||||
virtual void serialize(std::ostream &stream) const override;
|
||||
virtual void deserialize(std::istream &stream) override;
|
||||
|
||||
int32_t version;
|
||||
uint32_t start_packet;
|
||||
};
|
||||
|
||||
struct server_hello : public message
|
||||
{
|
||||
server_hello() : message(SERVER_HELLO) {}
|
||||
|
||||
uint32_t seed;
|
||||
int64_t timestamp;
|
||||
|
||||
virtual void serialize(std::ostream &stream) const override;
|
||||
virtual void deserialize(std::istream &stream) override;
|
||||
};
|
||||
|
||||
struct request_command : public message
|
||||
{
|
||||
request_command(type_e type) : message(type) {}
|
||||
request_command() : message(REQUEST_COMMAND) {}
|
||||
|
||||
command_queue::commands_map commands;
|
||||
|
||||
virtual void serialize(std::ostream &stream) const override;
|
||||
virtual void deserialize(std::istream &stream) override;
|
||||
};
|
||||
|
||||
struct frame_info : public request_command
|
||||
{
|
||||
frame_info() : request_command(FRAME_INFO) {}
|
||||
|
||||
double render_dt;
|
||||
double dt;
|
||||
double sync;
|
||||
|
||||
virtual void serialize(std::ostream &stream) const override;
|
||||
virtual void deserialize(std::istream &stream) override;
|
||||
};
|
||||
|
||||
std::shared_ptr<message> deserialize_message(std::istream &stream);
|
||||
void serialize_message(const message &msg, std::ostream &stream);
|
||||
} // namespace network
|
||||
269
network/network.cpp
Normal file
269
network/network.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include "stdafx.h"
|
||||
#include "network/network.h"
|
||||
#include "network/message.h"
|
||||
#include "Logs.h"
|
||||
#include "sn_utils.h"
|
||||
#include "Timer.h"
|
||||
#include "application.h"
|
||||
#include "Globals.h"
|
||||
|
||||
namespace network {
|
||||
|
||||
backend_list_t& backend_list() {
|
||||
// HACK: static initialization order fiasco fix
|
||||
// NOTE: potential static deinitialization order fiasco
|
||||
static backend_list_t backend_list;
|
||||
return backend_list;
|
||||
}
|
||||
|
||||
}
|
||||
// connection
|
||||
|
||||
void network::connection::disconnect() {
|
||||
WriteLog("net: peer dropped", logtype::net);
|
||||
state = DEAD;
|
||||
}
|
||||
|
||||
void network::connection::set_handler(std::function<void (const message &)> handler) {
|
||||
message_handler = handler;
|
||||
}
|
||||
|
||||
network::connection::connection(bool client, size_t counter) {
|
||||
packet_counter = counter;
|
||||
is_client = client;
|
||||
state = AWAITING_HELLO;
|
||||
}
|
||||
|
||||
void network::connection::connected()
|
||||
{
|
||||
WriteLog("net: socket connected", logtype::net);
|
||||
|
||||
if (is_client) {
|
||||
client_hello msg;
|
||||
msg.version = 1;
|
||||
msg.start_packet = packet_counter;
|
||||
send_message(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void network::connection::catch_up()
|
||||
{
|
||||
backbuffer->seekg(backbuffer_pos);
|
||||
|
||||
std::vector<std::shared_ptr<message>> messages;
|
||||
|
||||
for (int i = 0; i < CATCHUP_PACKETS; i++) {
|
||||
if (backbuffer->peek() == EOF) {
|
||||
send_messages(messages);
|
||||
state = ACTIVE;
|
||||
backbuffer->seekg(0, std::ios_base::end);
|
||||
return;
|
||||
}
|
||||
|
||||
auto msg = deserialize_message(*backbuffer.get());
|
||||
|
||||
if (packet_counter) {
|
||||
packet_counter--;
|
||||
i--; // TODO: it would be better to skip frames in chunks
|
||||
continue;
|
||||
}
|
||||
|
||||
messages.push_back(msg);
|
||||
}
|
||||
|
||||
backbuffer_pos = backbuffer->tellg();
|
||||
backbuffer->seekg(0, std::ios_base::end);
|
||||
|
||||
send_messages(messages);
|
||||
}
|
||||
|
||||
void network::connection::send_complete(std::shared_ptr<std::string> buf)
|
||||
{
|
||||
if (!is_client && state == CATCHING_UP) {
|
||||
catch_up();
|
||||
}
|
||||
}
|
||||
|
||||
// --------------
|
||||
|
||||
// server
|
||||
network::server::server(std::shared_ptr<std::istream> buf) : backbuffer(buf)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void network::server::push_delta(const frame_info &msg)
|
||||
{
|
||||
for (auto it = clients.begin(); it != clients.end(); ) {
|
||||
if ((*it)->state == connection::DEAD) {
|
||||
it = clients.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((*it)->state == connection::ACTIVE)
|
||||
(*it)->send_message(msg);
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
command_queue::commands_map network::server::pop_commands()
|
||||
{
|
||||
command_queue::commands_map map(client_commands_queue);
|
||||
client_commands_queue.clear();
|
||||
return map;
|
||||
}
|
||||
|
||||
void network::server::handle_message(std::shared_ptr<connection> conn, const message &msg)
|
||||
{
|
||||
if (msg.type == message::TYPE_MAX)
|
||||
{
|
||||
conn->disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.type == message::CLIENT_HELLO) {
|
||||
auto cmd = dynamic_cast<const client_hello&>(msg);
|
||||
|
||||
if (cmd.version != 1 // wrong version
|
||||
|| !Global.ready_to_load) { // not ready yet
|
||||
conn->disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
server_hello reply;
|
||||
reply.seed = Global.random_seed;
|
||||
reply.timestamp = Global.starting_timestamp;
|
||||
conn->state = connection::CATCHING_UP;
|
||||
conn->backbuffer = backbuffer;
|
||||
conn->backbuffer_pos = 0;
|
||||
conn->packet_counter = cmd.start_packet;
|
||||
|
||||
conn->send_message(reply);
|
||||
|
||||
WriteLog("net: client accepted", logtype::net);
|
||||
}
|
||||
else if (msg.type == message::REQUEST_COMMAND) {
|
||||
auto cmd = dynamic_cast<const request_command&>(msg);
|
||||
|
||||
for (auto const &kv : cmd.commands)
|
||||
client_commands_queue.emplace(kv);
|
||||
}
|
||||
}
|
||||
|
||||
// ------------
|
||||
|
||||
void network::client::update()
|
||||
{
|
||||
if (conn && conn->state == connection::DEAD) {
|
||||
conn.reset();
|
||||
}
|
||||
|
||||
if (!conn) {
|
||||
if (!reconnect_delay) {
|
||||
connect();
|
||||
reconnect_delay = RECONNECT_DELAY_FRAMES;
|
||||
}
|
||||
reconnect_delay--;
|
||||
}
|
||||
}
|
||||
|
||||
// client
|
||||
std::tuple<double, double, command_queue::commands_map> network::client::get_next_delta(int counter)
|
||||
{
|
||||
auto now = std::chrono::high_resolution_clock::now();
|
||||
if (counter == 1) {
|
||||
frame_time = now - last_frame;
|
||||
last_frame = now;
|
||||
}
|
||||
|
||||
if (delta_queue.empty()) {
|
||||
// buffer underflow
|
||||
return std::tuple<double, double,
|
||||
command_queue::commands_map>(0.0, 0.0, command_queue::commands_map());
|
||||
}
|
||||
|
||||
|
||||
float size = delta_queue.size() - consume_counter;
|
||||
auto entry = delta_queue.front();
|
||||
float mult = entry.render_dt / std::chrono::duration_cast<std::chrono::duration<float>>(frame_time).count();
|
||||
|
||||
if (counter == 1 && size < MAX_BUFFER_SIZE * 2.0f) {
|
||||
last_target = last_target * TARGET_MIX +
|
||||
(std::min(TARGET_MIN + jitteriness * JITTERINESS_MULTIPIER, MAX_BUFFER_SIZE)) * (1.0f - TARGET_MIX);
|
||||
float diff = size - last_target;
|
||||
jitteriness = std::max(jitteriness * JITTERINESS_MIX, std::abs(diff));
|
||||
|
||||
float speed = 1.0f + diff * CONSUME_MULTIPIER;
|
||||
|
||||
consume_counter += speed;
|
||||
}
|
||||
|
||||
float last_rcv_diff = std::chrono::duration_cast<std::chrono::duration<float>>(now - last_rcv).count();
|
||||
|
||||
if (size > MAX_BUFFER_SIZE || consume_counter > mult || last_rcv_diff > 1.0f) {
|
||||
if (consume_counter > mult) {
|
||||
consume_counter = std::clamp(consume_counter - mult, -MAX_BUFFER_SIZE, MAX_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
delta_queue.pop();
|
||||
|
||||
return std::make_tuple(entry.dt, entry.sync, entry.commands);
|
||||
} else {
|
||||
// nothing to push
|
||||
return std::tuple<double, double,
|
||||
command_queue::commands_map>(0.0, 0.0, command_queue::commands_map());
|
||||
}
|
||||
}
|
||||
|
||||
void network::client::send_commands(command_queue::commands_map commands)
|
||||
{
|
||||
if (!conn || conn->state == connection::DEAD || commands.empty())
|
||||
return;
|
||||
// eh, maybe queue lost messages
|
||||
|
||||
request_command msg;
|
||||
msg.commands = commands;
|
||||
|
||||
conn->send_message(msg);
|
||||
}
|
||||
|
||||
void network::client::handle_message(std::shared_ptr<connection> conn, const message &msg)
|
||||
{
|
||||
if (msg.type >= message::TYPE_MAX)
|
||||
{
|
||||
conn->disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.type == message::SERVER_HELLO) {
|
||||
auto cmd = dynamic_cast<const server_hello&>(msg);
|
||||
conn->state = connection::ACTIVE;
|
||||
|
||||
if (!Global.ready_to_load) {
|
||||
Global.random_seed = cmd.seed;
|
||||
Global.random_engine.seed(Global.random_seed);
|
||||
Global.starting_timestamp = cmd.timestamp;
|
||||
Global.ready_to_load = true;
|
||||
} else if (Global.random_seed != cmd.seed) {
|
||||
ErrorLog("net: seed mismatch", logtype::net);
|
||||
conn->disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
WriteLog("net: accept received", logtype::net);
|
||||
}
|
||||
|
||||
if (conn->state != connection::ACTIVE)
|
||||
return;
|
||||
|
||||
if (msg.type == message::FRAME_INFO) {
|
||||
resume_frame_counter++;
|
||||
|
||||
auto delta = dynamic_cast<const frame_info&>(msg);
|
||||
delta_queue.push(delta);
|
||||
last_rcv = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
// --------------
|
||||
122
network/network.h
Normal file
122
network/network.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <chrono>
|
||||
#include "network/message.h"
|
||||
#include "command.h"
|
||||
|
||||
namespace network
|
||||
{
|
||||
//m7todo: separate client/server connection class?
|
||||
class connection
|
||||
{
|
||||
friend class server;
|
||||
friend class client;
|
||||
|
||||
private:
|
||||
const int CATCHUP_PACKETS = 300;
|
||||
|
||||
bool is_client;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<std::istream> backbuffer;
|
||||
size_t backbuffer_pos;
|
||||
size_t packet_counter;
|
||||
|
||||
void send_complete(std::shared_ptr<std::string> buf);
|
||||
void catch_up();
|
||||
|
||||
public:
|
||||
std::function<void(const message &msg)> message_handler;
|
||||
|
||||
virtual void connected() = 0;
|
||||
virtual void send_message(const message &msg) = 0;
|
||||
virtual void send_messages(const std::vector<std::shared_ptr<message>> &messages) = 0;
|
||||
|
||||
connection(bool client = false, size_t counter = 0);
|
||||
void set_handler(std::function<void(const message &msg)> handler);
|
||||
|
||||
virtual void disconnect() = 0;
|
||||
|
||||
enum peer_state {
|
||||
AWAITING_HELLO,
|
||||
CATCHING_UP,
|
||||
ACTIVE,
|
||||
DEAD
|
||||
};
|
||||
peer_state state;
|
||||
};
|
||||
|
||||
class server
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<std::istream> backbuffer;
|
||||
|
||||
protected:
|
||||
void handle_message(std::shared_ptr<connection> conn, const message &msg);
|
||||
|
||||
std::vector<std::shared_ptr<connection>> clients;
|
||||
|
||||
command_queue::commands_map client_commands_queue;
|
||||
|
||||
public:
|
||||
server(std::shared_ptr<std::istream> buf);
|
||||
void push_delta(const frame_info &msg);
|
||||
command_queue::commands_map pop_commands();
|
||||
};
|
||||
|
||||
class client
|
||||
{
|
||||
protected:
|
||||
virtual void connect() = 0;
|
||||
void handle_message(std::shared_ptr<connection> conn, const message &msg);
|
||||
std::shared_ptr<connection> conn;
|
||||
size_t resume_frame_counter = 0;
|
||||
size_t reconnect_delay = 0;
|
||||
|
||||
const size_t RECONNECT_DELAY_FRAMES = 60;
|
||||
const float MAX_BUFFER_SIZE = 60.0f;
|
||||
const float JITTERINESS_MIX = 0.998f;
|
||||
const float TARGET_MIN = 2.0f;
|
||||
const float TARGET_MIX = 0.98f;
|
||||
const float JITTERINESS_MULTIPIER = 2.0f;
|
||||
const float CONSUME_MULTIPIER = 0.05f;
|
||||
|
||||
std::queue<frame_info> delta_queue;
|
||||
|
||||
float last_target = 20.0f;
|
||||
float jitteriness = 1.0f;
|
||||
float consume_counter = 0.0f;
|
||||
|
||||
std::chrono::high_resolution_clock::time_point last_rcv;
|
||||
std::chrono::high_resolution_clock::time_point last_frame;
|
||||
std::chrono::high_resolution_clock::duration frame_time;
|
||||
|
||||
public:
|
||||
void update();
|
||||
std::tuple<double, double, command_queue::commands_map> get_next_delta(int counter);
|
||||
void send_commands(command_queue::commands_map commands);
|
||||
int get_frame_counter() {
|
||||
return resume_frame_counter;
|
||||
}
|
||||
int get_awaiting_frames() {
|
||||
return delta_queue.size();
|
||||
}
|
||||
};
|
||||
|
||||
class backend_manager
|
||||
{
|
||||
public:
|
||||
virtual std::shared_ptr<server> create_server(std::shared_ptr<std::fstream>, const std::string &conf) = 0;
|
||||
virtual std::shared_ptr<client> create_client(const std::string &conf) = 0;
|
||||
virtual void update() = 0;
|
||||
};
|
||||
|
||||
// HACK: static initialization order fiasco fix
|
||||
// extern std::unordered_map<std::string, backend_manager*> backend_list;
|
||||
using backend_list_t = std::unordered_map<std::string, backend_manager*>;
|
||||
backend_list_t& backend_list();
|
||||
}
|
||||
Reference in New Issue
Block a user