17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-03 07:20:22 +01:00

multiplayer stage 7: versioned snapshots instead of replaying the session

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
This commit is contained in:
2026-09-06 19:37:28 +00:00
parent b255c8120a
commit 54d8b112b8
14 changed files with 558 additions and 44 deletions

View File

@@ -18,17 +18,13 @@ namespace network
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;
// exists only to keep the send buffer alive until asio is done with it
void send_complete(std::shared_ptr<std::string> buf);
void catch_up();
public:
std::function<void(const message &msg)> message_handler;
@@ -44,7 +40,9 @@ namespace network
enum peer_state {
AWAITING_HELLO,
CATCHING_UP,
// handshake done, the peer is loading the scenario; it gets a snapshot and
// goes live the moment it says it is ready
AWAITING_READY,
ACTIVE,
DEAD
};
@@ -57,6 +55,8 @@ namespace network
class server
{
private:
// the session log is still written out, it is handy when picking a desync apart,
// but joining no longer means replaying it
std::shared_ptr<std::istream> backbuffer;
protected:
@@ -122,6 +122,8 @@ namespace network
void update();
frame_delta get_next_delta(int counter);
void send_commands(command_queue::commands_map commands);
// tells the server the scenario is loaded and a snapshot can be applied
void send_ready();
// lobby requests; the server is the one that decides
void send_claim(NetworkEntityId entity_id);
void send_leave(NetworkEntityId entity_id);