game_protocol source

← game_protocol · API reference · version 0.1.7

2 file(s), 142 lines. This is the source the published version ships, and it is loft — the library is written in the same language you call it from.

Public items

A worked example is a call site the author chose as the thing to read first. Call sites are every line naming the item, found mechanically over the source with comments excluded — so an item with no worked example is not an item nobody uses.

ItemDefinedWorked exampleCall sites
MsgType enum7—24 47 58 69 80 91 … (13 total)
WsMessage struct23—35 47 58 69 80 91 … (7 total)
GameEnvelope struct30—41 42 52 53 63 64 … (12 total)
msg_ping fn41—3 7
msg_pong fn52—3 11
msg_chat fn63—3 16
msg_input fn74—3 23
msg_state fn85—3 29
msg_error fn96—3 35

src/game_protocol.loft

// Copyright (c) 2026 Jurjen Stellingwerff
// SPDX-License-Identifier: LGPL-3.0-or-later
// --- Game Protocol ---
// Shared message types for client-server game communication.

// Message types for the game protocol envelope.
pub enum MsgType {
  StateFullSync,
  StateDelta,
  PlayerInput,
  Ping,
  Pong,
  ChatMessage,
  LobbyJoin,
  LobbyLeave,
  LobbyList,
  MatchStart,
  MatchEnd,
  Error
}

// A WebSocket message wrapper for game communication.
pub struct WsMessage {
  pub msg_type: MsgType,
  pub payload: text
}

// The outer envelope for all game messages.
// Contains routing info + the actual message.
pub struct GameEnvelope {
  pub sender: text,
  pub recipient: text,
  pub sequence: integer,
  pub timestamp: integer,
  pub message: WsMessage
}

// --- Message constructors ---

// Create a ping message.
pub fn msg_ping(seq: integer) -> GameEnvelope {
  GameEnvelope {
    sender: "",
    recipient: "",
    sequence: seq,
    timestamp: now(),
    message: WsMessage { msg_type: MsgType.Ping, payload: "" }
  }
}

// Create a pong response.
pub fn msg_pong(seq: integer) -> GameEnvelope {
  GameEnvelope {
    sender: "",
    recipient: "",
    sequence: seq,
    timestamp: now(),
    message: WsMessage { msg_type: MsgType.Pong, payload: "" }
  }
}

// Create a chat message.
pub fn msg_chat(sender: text, content: text) -> GameEnvelope {
  GameEnvelope {
    sender: sender,
    recipient: "",
    sequence: 0,
    timestamp: now(),
    message: WsMessage { msg_type: MsgType.ChatMessage, payload: content }
  }
}

// Create a player input message.
pub fn msg_input(sender: text, input_data: text) -> GameEnvelope {
  GameEnvelope {
    sender: sender,
    recipient: "",
    sequence: 0,
    timestamp: now(),
    message: WsMessage { msg_type: MsgType.PlayerInput, payload: input_data }
  }
}

// Create a state sync message.
pub fn msg_state(state_data: text) -> GameEnvelope {
  GameEnvelope {
    sender: "",
    recipient: "",
    sequence: 0,
    timestamp: now(),
    message: WsMessage { msg_type: MsgType.StateFullSync, payload: state_data }
  }
}

// Create an error message.
pub fn msg_error(error_text: text) -> GameEnvelope {
  GameEnvelope {
    sender: "",
    recipient: "",
    sequence: 0,
    timestamp: now(),
    message: WsMessage { msg_type: MsgType.Error, payload: error_text }
  }
}

tests/protocol.loft

// Copyright (c) 2026 Jurjen Stellingwerff
// SPDX-License-Identifier: LGPL-3.0-or-later
use game_protocol::(msg_ping, msg_pong, msg_chat, msg_input, msg_state, msg_error);
use game_protocol::MsgType;

fn test_ping_pong() {
  ping = msg_ping(1);
  assert(ping.message.msg_type == MsgType.Ping, "ping type");
  assert(ping.sequence == 1, "ping seq");

  pong = msg_pong(1);
  assert(pong.message.msg_type == MsgType.Pong, "pong type");
}

fn test_chat() {
  msg = msg_chat("alice", "hello");
  assert(msg.sender == "alice", "chat sender");
  assert(msg.message.payload == "hello", "chat payload");
  assert(msg.message.msg_type == MsgType.ChatMessage, "chat type");
}

fn test_input() {
  msg = msg_input("player1", "move:left");
  assert(msg.sender == "player1", "input sender");
  assert(msg.message.payload == "move:left", "input payload");
}

fn test_state() {
  msg = msg_state("score:42");
  assert(msg.message.msg_type == MsgType.StateFullSync, "state type");
  assert(msg.message.payload == "score:42", "state payload");
}

fn test_error() {
  msg = msg_error("invalid move");
  assert(msg.message.msg_type == MsgType.Error, "error type");
  assert(msg.message.payload == "invalid move", "error payload");
}