From b2a9388fab49856e4d460986104fa8733bd1a428 Mon Sep 17 00:00:00 2001 From: milek7 Date: Mon, 14 Jan 2019 00:20:14 +0100 Subject: [PATCH] work --- Camera.cpp | 2 +- Globals.cpp | 5 ++ Globals.h | 3 +- Train.cpp | 2 +- application.cpp | 112 +++++++++++++++++++++++++++++--------------- command.cpp | 63 +++++++------------------ command.h | 59 +++++++++++------------ material.cpp | 1 - network/manager.h | 5 +- network/network.cpp | 14 ++++++ network/network.h | 2 + simulation.cpp | 2 +- 12 files changed, 150 insertions(+), 120 deletions(-) diff --git a/Camera.cpp b/Camera.cpp index 5ebc65ae..9dbc9dae 100644 --- a/Camera.cpp +++ b/Camera.cpp @@ -140,7 +140,7 @@ void TCamera::Update() command_data command; // NOTE: currently we're only storing commands for local entity and there's no id system in place, // so we're supplying 'default' entity id of 0 - while( simulation::Commands->pop( command, static_cast( command_target::entity ) | 0 ) ) { + while( simulation::Commands.pop( command, static_cast( command_target::entity ) | 0 ) ) { OnCommand( command ); } diff --git a/Globals.cpp b/Globals.cpp index fd8a3b60..638e47d1 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -788,6 +788,11 @@ global_settings::ConfigParse(cParser &Parser) { Parser.getTokens(1); Parser >> network_conf.is_server; } + else if (token == "network.client") + { + Parser.getTokens(1); + Parser >> network_conf.is_client; + } } while ((token != "") && (token != "endconfig")); //(!Parser->EndOfFile) // na koniec trochę zależności if (!bLoadTraction) // wczytywanie drutów i słupów diff --git a/Globals.h b/Globals.h index a5007719..4844ec38 100644 --- a/Globals.h +++ b/Globals.h @@ -198,7 +198,8 @@ struct global_settings { struct network_conf_t { bool enabled = false; - bool is_server; + bool is_server = false; + bool is_client = false; } network_conf; // methods diff --git a/Train.cpp b/Train.cpp index 2d0f5b7c..3956d5c9 100644 --- a/Train.cpp +++ b/Train.cpp @@ -4908,7 +4908,7 @@ bool TTrain::Update( double const Deltatime ) // eventually commands are going to be retrieved directly by the vehicle, filtered through active control stand // and ultimately executed, provided the stand allows it. command_data commanddata; - while( simulation::Commands->pop( commanddata, (uint32_t)command_target::vehicle | id() )) { + while( simulation::Commands.pop( commanddata, (uint32_t)command_target::vehicle | id() )) { auto lookup = m_commandhandlers.find( commanddata.command ); if( lookup != m_commandhandlers.end() ) { diff --git a/application.cpp b/application.cpp index b97dbb0f..3878caeb 100644 --- a/application.cpp +++ b/application.cpp @@ -188,48 +188,89 @@ eu07_application::run() { Timer::subsystem.mainloop_total.start(); glfwPollEvents(); - if (!m_network) + // ------------------------------------------------------------------- + // multiplayer command relaying logic can seem a bit complex + // + // we are simultaneously: + // => master (not client) OR slave (client) + // => server OR not + // + // trivia: being client and server is possible + + bool nextloop = true; + while (nextloop) { + command_queue::commands_map commands_to_exec; + command_queue::commands_map local_commands = simulation::Commands.pop_intercept_queue(); + double slave_sync; + + // if we're the server + if (m_network && m_network->server) + { + // fetch from network layer command requests received from clients + command_queue::commands_map remote_commands = m_network->server->pop_commands(); + + // push these into local queue + add_to_dequemap(local_commands, remote_commands); + } + + // if we're slave + if (m_network && m_network->client) + { + // fetch frame info from network layer, + auto frame_info = m_network->client->get_next_delta(); + + // use delta and commands received from master + double delta = std::get<0>(frame_info); + Timer::set_delta_override(delta); + slave_sync = std::get<1>(frame_info); + add_to_dequemap(commands_to_exec, std::get<2>(frame_info)); + + // and send our local commands to master + m_network->client->send_commands(local_commands); + + if (delta == 0.0) + nextloop = false; + } + // if we're master + else { + // just push local commands to execution + add_to_dequemap(commands_to_exec, local_commands); + + nextloop = false; + } + + // send commands to command queue + simulation::Commands.push_commands(commands_to_exec); + + // do actual frame processing if (!m_modes[ m_modestack.top() ]->update()) - break; - simulation::Commands->update(); - } - else if (!Global.network_conf.is_server) { - command_queue_client *queue = dynamic_cast(simulation::Commands.get()); - double delta; - do { - auto tup = m_network->get_next_delta(); - delta = std::get<0>(tup); - Timer::set_delta_override(delta); - queue->push_server_commands(std::get<2>(tup)); + goto die; - m_network->send_commands(queue->pop_queued_commands()); + // update continuous commands + simulation::Commands.update(); - if (!m_modes[ m_modestack.top() ]->update()) - break; + double sync = generate_sync(); - queue->update(); - - double sync = generate_sync(); - if (sync != std::get<1>(tup)) { + // if we're slave + if (m_network && m_network->client) + { + // verify sync + if (sync != slave_sync) { WriteLog("net: DESYNC!", logtype::net); } } - while (delta != 0.0); + + // if we're the server + if (m_network && m_network->server) + { + // send delta, sync, and commands we just executed to clients + double delta = Timer::GetDeltaTime(); + m_network->server->push_delta(delta, sync, commands_to_exec); + } } - else { - command_queue_server *queue = dynamic_cast(simulation::Commands.get()); - queue->push_client_commands(m_network->pop_commands()); - auto commands = queue->pop_queued_commands(); - if (!m_modes[ m_modestack.top() ]->update()) - break; - - queue->update(); - - double delta = Timer::GetDeltaTime(); - m_network->push_delta(delta, generate_sync(), commands); - } + // ------------------------------------------------------------------- m_taskqueue.update(); opengl_texture::reset_unit_cache(); @@ -254,6 +295,7 @@ eu07_application::run() { if (m_network) m_network->poll(); } + die: return 0; } @@ -671,15 +713,11 @@ bool eu07_application::init_network() { m_network.emplace(); if (Global.network_conf.is_server) { m_network->create_server(); - simulation::Commands = std::make_unique(); } - else { + if (Global.network_conf.is_client) { m_network->connect(); - simulation::Commands = std::make_unique(); } } - else - simulation::Commands = std::make_unique(); return true; } diff --git a/command.cpp b/command.cpp index c9c07487..86c1c7f7 100644 --- a/command.cpp +++ b/command.cpp @@ -18,7 +18,7 @@ http://mozilla.org/MPL/2.0/. namespace simulation { -std::unique_ptr Commands; +command_queue Commands; commanddescription_sequence Commands_descriptions = { { "aidriverenable", command_target::vehicle, command_mode::oneoff }, @@ -231,9 +231,6 @@ commanddescription_sequence Commands_descriptions = { } // simulation -// -------------------- -// command_queue - void command_queue::update() { double delta = Timer::GetDeltaTime(); @@ -249,6 +246,15 @@ void command_queue::update() // posts specified command for specified recipient void command_queue::push( command_data const &Command, uint32_t const Recipient ) { + if (is_network_target(Recipient)) { + auto lookup = m_intercept_queue.emplace(Recipient, commanddata_sequence()); + lookup.first->second.emplace_back(Command); + } else { + push_direct(Command, Recipient); + } +} + +void command_queue::push_direct(const command_data &Command, const uint32_t Recipient) { auto const &desc = simulation::Commands_descriptions[ static_cast( Command.command ) ]; if (desc.mode == command_mode::continuous) { @@ -295,55 +301,18 @@ bool command_queue::is_network_target(uint32_t const Recipient) { return true; } -// -------------------- -// command_queue_server - -void command_queue_server::push(const command_data &Command, const uint32_t Recipient) { - if (is_network_target(Recipient)) { - auto lookup = network_queue.emplace(Recipient, commanddata_sequence()); - lookup.first->second.emplace_back(Command); - } - command_queue::push(Command, Recipient); -} - -command_queue_server::commands_map command_queue_server::pop_queued_commands() { - commands_map map(network_queue); - network_queue.clear(); +command_queue::commands_map command_queue::pop_intercept_queue() { + commands_map map(m_intercept_queue); + m_intercept_queue.clear(); return map; } -void command_queue_server::push_client_commands(const commands_map &commands) { +void command_queue::push_commands(const commands_map &commands) { for (auto const &kv : commands) for (command_data const &data : kv.second) - push(data, kv.first); + push_direct(data, kv.first); } -// -------------------- -// command_queue_client - -void command_queue_client::push(const command_data &Command, const uint32_t Recipient) { - if (is_network_target(Recipient)) { - auto lookup = network_queue.emplace(Recipient, commanddata_sequence()); - lookup.first->second.emplace_back(Command); - } - else - command_queue::push(Command, Recipient); -} - -command_queue_client::commands_map command_queue_client::pop_queued_commands() { - commands_map map(network_queue); - network_queue.clear(); - return map; -} - -void command_queue_client::push_server_commands(const commands_map &commands) { - for (auto const &kv : commands) - for (command_data const &data : kv.second) - command_queue::push(data, kv.first); -} - -// -------------------- - void command_relay::post( user_command const Command, double const Param1, double const Param2, int const Action, uint16_t Recipient) const { @@ -368,5 +337,5 @@ command_relay::post( user_command const Command, double const Param1, double con uint32_t combined_recipient = static_cast( command.target ) | Recipient; command_data commanddata({Command, Action, Param1, Param2, Timer::GetDeltaTime(), FreeFlyModeFlag, Global.pCamera.Pos }); - simulation::Commands->push(commanddata, combined_recipient); + simulation::Commands.push(commanddata, combined_recipient); } diff --git a/command.h b/command.h index 85cfbc12..53723dc5 100644 --- a/command.h +++ b/command.h @@ -275,20 +275,36 @@ public: typedef std::unordered_map commands_map; // methods - // posts specified command for specified recipient - virtual void + // posts specified command for specified recipient into m_intercept_queue + void push( command_data const &Command, uint32_t const Recipient ); // retrieves oldest posted command for specified recipient, if any. returns: true on retrieval, false if there's nothing to retrieve bool pop( command_data &Command, uint32_t const Recipient ); - void update(); - bool is_network_target(const uint32_t Recipient); + // generates active continuous commands + void + update(); + + // checks if given command must be scheduled on server + bool + is_network_target(const uint32_t Recipient); + + // pops commands from intercept queue + commands_map pop_intercept_queue(); + + // pushes commands into main queue + void push_commands(const commands_map &commands); private: // members - // main command queue + // contains command ready to execution commands_map m_commands; + // contains intercepted commands to be read by application layer + commands_map m_intercept_queue; + + void push_direct( command_data const &Command, uint32_t const Recipient ); + // hash operator for m_active_continuous struct command_set_hash { uint64_t operator() (const std::pair &pair) const { @@ -300,29 +316,14 @@ private: std::unordered_set, command_set_hash> m_active_continuous; }; -class command_queue_server : public command_queue { -public: - void push( command_data const &Command, uint32_t const Recipient ) override; - - commands_map pop_queued_commands(); - void push_client_commands(const commands_map &commands); - -private: - // contains copies of commands to be sent to clients - commands_map network_queue; -}; - -class command_queue_client : public command_queue { -public: - void push( command_data const &Command, uint32_t const Recipient ) override; - - commands_map pop_queued_commands(); - void push_server_commands(const commands_map &commands); - -private: - // contains intercepted commands to be sent for execution to server - commands_map network_queue; -}; +template +void add_to_dequemap(std::unordered_map> &lhs, const std::unordered_map> &rhs) { + for (auto const &kv : rhs) { + auto lookup = lhs.emplace(kv.first, std::deque()); + for (B const &data : kv.second) + lookup.first->second.emplace_back(data); + } +} // NOTE: simulation should be a (light) wrapper rather than namespace so we could potentially instance it, // but realistically it's not like we're going to run more than one simulation at a time @@ -330,7 +331,7 @@ namespace simulation { typedef std::vector commanddescription_sequence; -extern std::unique_ptr Commands; +extern command_queue Commands; // TODO: add name to command map, and wrap these two into helper object extern commanddescription_sequence Commands_descriptions; diff --git a/material.cpp b/material.cpp index 6cfe8b7b..8b413d38 100644 --- a/material.cpp +++ b/material.cpp @@ -380,7 +380,6 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) { materialhandle = m_materials.size(); m_materialmappings.emplace( material.name, materialhandle ); m_materials.emplace_back( std::move(material) ); - std::cout << m_materials.size() << std::endl; } else { // otherwise record our failure to process the resource, to speed up subsequent attempts diff --git a/network/manager.h b/network/manager.h index 76562332..3127556a 100644 --- a/network/manager.h +++ b/network/manager.h @@ -9,12 +9,13 @@ namespace network class manager { asio::io_context io_context; - std::shared_ptr server; - std::shared_ptr client; public: manager(); + std::shared_ptr server; + std::shared_ptr client; + void create_server(); void connect(); void poll(); diff --git a/network/network.cpp b/network/network.cpp index 76b6bae0..fc946e82 100644 --- a/network/network.cpp +++ b/network/network.cpp @@ -118,6 +118,11 @@ void network::connection::send_message(std::shared_ptr msg) send_data(buf); } +network::server::server() +{ + recorder.open("recorder.bin", std::ios::trunc | std::ios::out | std::ios::binary); +} + void network::server::push_delta(double dt, double sync, command_queue::commands_map commands) { std::shared_ptr msg = std::make_shared(); @@ -125,6 +130,12 @@ void network::server::push_delta(double dt, double sync, command_queue::commands msg->sync = sync; msg->commands = commands; + sn_utils::ls_uint32(recorder, 0x37305545); + sn_utils::ls_uint32(recorder, msg->get_size()); + sn_utils::ls_uint16(recorder, (uint16_t)msg->type); + msg->serialize(recorder); + recorder.flush(); + for (auto c : clients) c->send_message(msg); } @@ -159,6 +170,9 @@ std::tuple network::client::get_nex void network::client::send_commands(command_queue::commands_map commands) { + if (commands.empty()) + return; + std::shared_ptr msg = std::make_shared(); msg->commands = commands; diff --git a/network/network.h b/network/network.h index 855802bd..bac8de22 100644 --- a/network/network.h +++ b/network/network.h @@ -41,8 +41,10 @@ namespace network { protected: std::vector> clients; + std::fstream recorder; public: + server(); void push_delta(double dt, double sync, command_queue::commands_map commands); command_queue::commands_map pop_commands(); void notify_train(std::string name); diff --git a/simulation.cpp b/simulation.cpp index 47734958..518ef152 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -77,7 +77,7 @@ state_manager::update( double const Deltatime, int Iterationcount ) { void state_manager::process_commands() { command_data commanddata; - while( Commands->pop( commanddata, (uint32_t)command_target::simulation )) { + while( Commands.pop( commanddata, (uint32_t)command_target::simulation )) { if (commanddata.action == GLFW_RELEASE) continue;