After all the preparation for the multiplayer I finally implemented a very basic version.
I added a trait for a server:
pub trait IsServer<E> {
fn get_arrived(&self, collector: &mut dyn Collector<E>);
fn queue_send(&self, event: E);
}
where one can either queue events to be sent to the server or check for newly arrived ones.
I reworked the ClientGame
to instead of applying events to the CoreGame
directly, they are now being sent to the server:
fn process_event_queue(&mut self) {
...
for event in self.event_queue.drain(..) {
match event {
CE::Game(x) => self.server.queue_send(x),
CE::SetCursor(x) => self.cursor_item = x,
...
}
}
}
the ClientGame
then also checks for events that arrived from the server and then applies those to its core:
fn process_server_events(&mut self) {
let mut events = Vec::new();
self.server.get_arrived(&mut events);
for event in events {
self.core.enqueue(event);
}
}
for the server and the networking part of the client (implementation of IsServer
) I used tokio combined with serde to (de)serialize the events.
The server collects all arriving events and then sends them back to every connected client.
This implementation is rather simple and has major flaws:
- Clients don’t have their simulation ticks synchronized and will have different states after some time
- Clients that connect late won’t receive past events. It will always seem to them like they joined a new game
Below a video of the first multiplayer implementation: