mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 20:19:20 +02:00
Reversing on the host turned the whole set around on the client, and the locomotive ended up looking like something derailed making its way across the scenery. Both come from the same place: a routine correction was calling place_on_track(), which sets the axles from scratch and works from the vehicle's own idea of which way it faces. That is right for a peer joining with nothing to go on, and wrong for a train that is already running - the placement reorients it. A correction now slides the vehicle along the track it is already on, by the difference between the two bogie offsets, which is the same operation the physics performs on every step. When the two sides disagree about which piece of track the vehicle is on, it is left alone until they agree; guessing there is what put a locomotive beside the rails. place_on_track() is kept for the one case it suits, a peer joining the session. Also: - taking a seat works with the game paused. The whole thing sat inside the "has any time passed" branch, so with the world stopped the lobby button changed the roster and nothing else happened - the debug panel gets a Network section, shown only in a session: role, peer, tick, bytes and messages each way with a running rate, two charts of kB/s so the cost of a session can be read off, how far the worst vehicle is out of place, and whether the state digest agrees. The separate "network" overlay window is gone - everything it said lives there now, together with the lobby and refusal messages that had nowhere to be seen
242 lines
5.9 KiB
C++
242 lines
5.9 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"
|
|
#include "network/snapshot.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)
|
|
return;
|
|
|
|
// a client no longer replays these frame by frame, so an empty one is only worth
|
|
// sending now and then, to carry the tick and the digest
|
|
if (commands.empty() && (--heartbeat_countdown > 0))
|
|
return;
|
|
|
|
if (commands.empty())
|
|
heartbeat_countdown = HEARTBEAT_INTERVAL_FRAMES;
|
|
|
|
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.expire_absences();
|
|
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::server_manager::publish_state()
|
|
{
|
|
if (--state_countdown > 0)
|
|
return;
|
|
|
|
state_countdown = STATE_INTERVAL_FRAMES;
|
|
|
|
// most of the time only what changed goes out; every so often the whole thing does,
|
|
// so that anything a peer missed heals by itself
|
|
bool const full = ((state_updates++ % STATE_FULL_EVERY) == 0);
|
|
|
|
auto const blob = take_snapshot(full);
|
|
if (blob.empty())
|
|
return;
|
|
|
|
snapshot msg;
|
|
msg.tick = Global.simulation_tick;
|
|
msg.mode = 1; // correction on top of a running simulation
|
|
msg.blob = blob;
|
|
|
|
for (auto srv : servers)
|
|
srv->push_message(msg);
|
|
}
|
|
|
|
void network::manager::request_resync(uint64_t tick, uint64_t state_hash)
|
|
{
|
|
if (client)
|
|
client->send_resync_request(tick, state_hash);
|
|
}
|
|
|
|
void network::manager::update()
|
|
{
|
|
Traffic.update();
|
|
|
|
for (auto &backend : backend_list())
|
|
backend.second->update();
|
|
|
|
if (servers && Global.simulation_loaded) {
|
|
// note: not simulation::is_ready. that one waits for the local player's cab, and
|
|
// the cab of a claimed vehicle is built by update_crews() - waiting for it here
|
|
// would leave the two waiting on each other
|
|
servers->update_crews();
|
|
servers->publish_vehicle_list();
|
|
servers->publish_state();
|
|
}
|
|
|
|
if (client) {
|
|
client->update();
|
|
|
|
if (Global.simulation_loaded)
|
|
client->publish_state();
|
|
}
|
|
}
|
|
|
|
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;
|