1 stable release

1.0.2 Jan 7, 2022

#179 in WebSocket

GPL-3.0 license

18KB
152 lines

cg-game-server

About

This is a library that consists mostly of traits that are meant to speed up and standardize the development of CodeGame game servers.

It's the server's job to keep the players connected to each other and prevent them from cheating using minimal server-side state. All decisions are made by the client but always verified by the server.

Installation

Add the following dependencies to your Cargo.toml:

# this lib
cg-game-server = "0.1.0"
# CodeGame uses actix as its web framework, the traits in this project are built for actix
actix = "0.10"
actix-web = {version = "3", features = ["openssl"]}
actix-web-actors = "3"
# the project uses serde and serde_json to communicate with the clients
serde = {version = "1.0.133", features = ["derive"]}
serde_json = "1.0.74"

How to use

TL;DR: For some examples, please see the official games.

Apart from the main.rs module that starts the webserver, each CodeGame game server consists of three parts:

Here's a closer look at each:

Events

Events are structs that represent an action. Events flow between sockets and them and their clients, carrying data with them.

There are two types of events ReceiveableEvents (R in generics), which are sent to the server by a client (received by a socket), and SendableEvents (S in generics), which are sent between sockets.

When creating your own events it is recommended that you create two enums. Be sure to include the standard events in your enums too. It should look something like this:

use cg_game_server::events::{Error, Join, Leave};
use actix::prelude::Message;
use serde::{Deserialize, Serialize};
use std::result::Result;

/// Deserialization `Result` for deserializing `ReceiveableEvents`
pub type ReceiveableEventSerde = Result<ReceiveableEvents, serde_json::Error>;
/// Events that the client may send to the server
#[derive(Serialize, Deserialize, Debug, Message, Clone)]
#[rtype(result = "()")]
#[serde(tag = "event_name")]
pub enum ReceiveableEvents {
    #[serde(rename = "join")]
    Join(Join),
    #[serde(rename = "leave")]
    Leave(Leave),
}

/// Events that the server may send to the client
#[derive(Serialize, Debug, Message, Clone)]
#[rtype(result = "()")]
#[serde(tag = "event_name")]
pub enum SendableEvents {
    #[serde(rename = "error")]
    Error(Error),
    #[serde(rename = "join")]
    Joined(Join),
    #[serde(rename = "leave")]
    Left(Leave),
}

Socket

The Socket (C in generics) is what client connects to. It contains the game logic and is responsible for interpreting events sent by the client. Furthermore it sends all events that it reiceives from other sockets to the client.

A socket is required to implement the following:

  • actix::Actor -> required by actix, can send and receive messages
  • actix::StreamHandler -> required by actix, handles WebSocket communication
  • actix::Handler<EventWrapper<S>> required by CodeGame, handles SendableEvents sent to a socket
  • actix::GameServerCommunications<G, R> required by CodeGame, makes sure that the basic game server communication functions are available for every implementation of Socket

Server

The GameServer (G in generics) is the middle man. Its actix::Addr is passed to each new socket. Sockets can send events to the game server which it either processes or relays to other sockets.

A server is required to implement the following:

  • actix::Actor -> required by actix, can send and receive messages
  • actix::Handler<EventWrapper<S>> required by CodeGame, handles SendableEvents sent to sockets
  • actix::Handler<EventWrapper<Connect<C>>> required by CodeGame, handles Connect events sent to the server
  • actix::Handler<EventWrapper<Disconnect>> required by CodeGame, handles Disconnect events sent to the server
  • actix::Handler<EventWrapper<Join>> required by CodeGame, handles Join events sent to the server
  • actix::Handler<EventWrapper<Leave>> required by CodeGame, handles Leave events sent to the server

Dependencies

~28MB
~596K SLoC