mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 20:19:20 +02:00
Joining meant replaying backbuffer.bin from the very first frame of the session, so the longer a session ran, the longer it took to get in - and the whole history had to be kept for it. - network/snapshot: a chunked container with a version of its own. Every section carries its length, so a peer built before a section existed reads a newer snapshot and simply walks past what it does not know. Version 1 carries the session (tick, clock, weather, the state of the random engine), the vehicles (position on the track, speed, direction, controls, brake pressures, whether the AI is driving) and the crew roster. Switches, memcells, signals and the event queue are the next sections - joining is now: handshake -> load the scenario -> CLIENT_READY -> the server takes a snapshot on the spot and sends it -> the peer goes live. Because the snapshot is taken at the moment the peer becomes active there is no backlog to replay at all; the frame by frame catch-up is gone - the server refuses a peer that loaded a different scenario, with a readable reason instead of a silent desync - reconnecting into a session that is already loaded asks for a fresh snapshot straight away rather than waiting for a loader that will not run again - a vehicle is only put back on the rails when it really is somewhere else; the arithmetic that turns the recorded bogie offset back into the nose distance place_on_track() expects mirrors what that function itself does - the session log is still written out - it is useful when picking a desync apart - but nothing depends on replaying it any more
192 lines
4.6 KiB
C++
192 lines
4.6 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::notify_scenario_loaded()
|
|
{
|
|
if (client)
|
|
client->send_ready();
|
|
}
|
|
|
|
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;
|