mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 15:39:20 +02:00
The protocol hung off whatever frame the renderer happened to be drawing, and the only thing standing in for synchronisation was the sum of every vehicle's coordinates with a random number added on top - a value that cannot be equal on two machines and therefore reported a desync on every single step. - Global.simulation_tick: the authority counts the steps, a client takes the number from the authoritative frame it is executing. FRAME_INFO and the command requests carry it, so the network timeline has one unambiguous order independent of frame rates - network/statehash: a versioned FNV digest of the gameplay critical state - vehicle position, speed, direction, brake pipe and cylinder pressure, plus the clock. Values are quantized so that float noise is not mistaken for drift, and vehicles are folded in under their own identity and summed, so the digest does not depend on the order the scenario created them in. Switches, memcells and the event queue are the next candidates; they go in when stage 7 pins down what a snapshot contains - a client compares its own digest against the authoritative one and reports the drift when it starts and then only now and then, rather than every step; the overlay says how long it has been out of step and at which tick - refusing to compare at all when the two builds disagree on the digest layout EU07_NETWORK_VERSION is now 4.
186 lines
4.5 KiB
C++
186 lines
4.5 KiB
C++
/*
|
|
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/simulation.h"
|
|
#include "utilities/Logs.h"
|
|
#include "utilities/Globals.h"
|
|
#include "network/statehash.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, uint64_t tick, uint64_t state_hash, 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.tick = tick;
|
|
msg.state_hash = state_hash;
|
|
msg.state_hash_version = STATE_HASH_VERSION;
|
|
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::server_manager::publish_vehicle_list()
|
|
{
|
|
if (Entities.empty())
|
|
return;
|
|
|
|
Entities.refresh();
|
|
|
|
// resend on every change, and once in a while regardless, so that a peer which has
|
|
// just finished catching up does not have to wait for somebody to move
|
|
bool const changed = (Entities.entries() != last_published_list);
|
|
if (!changed && --publish_countdown > 0)
|
|
return;
|
|
|
|
last_published_list = Entities.entries();
|
|
publish_countdown = PUBLISH_INTERVAL_FRAMES;
|
|
|
|
vehicle_list msg;
|
|
msg.vehicles = last_published_list;
|
|
|
|
for (auto srv : servers)
|
|
srv->push_message(msg);
|
|
}
|
|
|
|
void network::server_manager::update_crews()
|
|
{
|
|
Crews.reconcile();
|
|
|
|
if (--crew_publish_countdown <= 0) {
|
|
// peers that joined after somebody took a vehicle over have to learn about it too
|
|
crew_publish_countdown = PUBLISH_INTERVAL_FRAMES;
|
|
Crews.mark_all_pending();
|
|
}
|
|
|
|
auto const changed = Crews.take_pending_updates();
|
|
for (NetworkEntityId const id : changed) {
|
|
crew_update msg;
|
|
msg.entity_id = id;
|
|
msg.crew = Crews.crew_of(id);
|
|
|
|
for (auto srv : servers)
|
|
srv->push_message(msg);
|
|
}
|
|
}
|
|
|
|
void network::server_manager::apply_local_claim(NetworkEntityId entity_id)
|
|
{
|
|
auto const result = Crews.claim(PEER_HOST, entity_id);
|
|
|
|
if (result == claim_result::granted || result == claim_result::already_member) {
|
|
Global.network_lobby_message.clear();
|
|
Global.network_pending_vehicle = Entities.name_of(entity_id);
|
|
}
|
|
else {
|
|
WriteLog("net: refused local claim of vehicle " + std::to_string(entity_id) + ": " + describe(result), logtype::net);
|
|
Global.network_lobby_message = describe(result);
|
|
}
|
|
}
|
|
|
|
void network::server_manager::apply_local_leave(NetworkEntityId entity_id)
|
|
{
|
|
if (Crews.leave(PEER_HOST, entity_id))
|
|
Global.network_leave_pending = true;
|
|
}
|
|
|
|
void network::manager::request_claim(NetworkEntityId entity_id)
|
|
{
|
|
if (client) {
|
|
client->send_claim(entity_id);
|
|
return;
|
|
}
|
|
|
|
if (servers)
|
|
servers->apply_local_claim(entity_id);
|
|
}
|
|
|
|
void network::manager::request_leave(NetworkEntityId entity_id)
|
|
{
|
|
if (client) {
|
|
client->send_leave(entity_id);
|
|
// the server owns the roster, but stepping out of our own cab is a local matter
|
|
Global.network_leave_pending = true;
|
|
return;
|
|
}
|
|
|
|
if (servers)
|
|
servers->apply_local_leave(entity_id);
|
|
}
|
|
|
|
void network::manager::update()
|
|
{
|
|
for (auto &backend : backend_list())
|
|
backend.second->update();
|
|
|
|
if (servers && simulation::is_ready) {
|
|
servers->update_crews();
|
|
servers->publish_vehicle_list();
|
|
}
|
|
|
|
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;
|