Sitelet https://github.com/cycle-five/runecast-protocol
Skip to content

Latest commit

 

History

150 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

runecast-protocol

Wire protocol types for RuneCast WebSocket communication.

Overview

This crate defines the message types that flow between client and server. It's the contract - both sides agree on these shapes.

┌────────────┐                           ┌────────────┐
│   Client   │ ◄─── ServerMessage ────── │   Server   │
│  (Browser) │ ──── ClientMessage ─────► │   (Rust)   │
└────────────┘                           └────────────┘

Design Goals

  1. Single source of truth - Message types defined once, used everywhere
  2. Backward compatible - Envelope is opt-in, legacy messages still work
  3. Type-safe - Strongly typed error codes, game states, positions
  4. Serialization-ready - All types derive Serialize/Deserialize

Module Structure

src/protocol/
├── mod.rs              # Re-exports, constants, compat module
├── envelope.rs         # Envelope<T>, MaybeEnveloped<T>
├── types.rs            # Shared types (Position, Grid, PlayerInfo, etc.)
├── client_messages.rs  # ClientMessage enum (26 variants)
└── server_messages.rs  # ServerMessage enum (40+ variants)

Key Types

Envelope (Optional Wrapper)

// New format with envelope
{
  "seq": 42,
  "ack": 41,
  "timestamp": 1699900000000,
  "payload": { "type": "heartbeat" }
}

// Legacy format (still supported)
{ "type": "heartbeat" }

The MaybeEnveloped<T> type accepts both formats, enabling gradual migration.

Client Messages

pub enum ClientMessage {
    // Connection
    Identify { token: String },
    Heartbeat,
    Ack { seq: u64 },
    
    // Lobby
    JoinChannelLobby { channel_id: String, guild_id: Option<String> },
    CreateCustomLobby,
    JoinCustomLobby { lobby_code: String },
    LeaveLobby,
    ToggleReady,
    
    // Game
    CreateGame,
    StartGame,
    SubmitWord { word: String, positions: Vec<Position> },
    PassTurn,
    ShuffleBoard,
    SwapTile { row: u8, col: u8, new_letter: char },
    
    // ... and more
}

Server Messages

pub enum ServerMessage {
    // Connection
    Hello { session_id: String, server_time: u64 },
    Ready { player: PlayerInfo },
    HeartbeatAck { server_time: u64 },
    
    // Game Events
    GameStarted { game_id: String, grid: Grid, players: Vec<GamePlayerInfo> },
    WordScored { player_id: String, word: String, score: i32, path: Vec<Position> },
    TurnChanged { player_id: String, round: u8 },
    GameOver { final_scores: Vec<ScoreInfo>, winner_id: Option<String> },
    
    // Errors
    Error { code: ErrorCode, message: String, details: Option<Value> },
    
    // ... and more
}

Error Codes

pub enum ErrorCode {
    NotAuthenticated,
    LobbyNotFound,
    LobbyFull,
    NotYourTurn,
    InvalidPath,
    WordNotInDictionary,
    WordAlreadyUsed,
    InsufficientGems,
    // ... 20+ typed errors
}

Compatibility Layer

The compat module helps with migration:

use runecast_protocol::compat;

// Parse incoming (handles both legacy and enveloped)
let (msg, seq, ack) = compat::parse_client_message(&json_text)?;

// Serialize outgoing (envelope optional)
let json = compat::serialize_server_message(&response, Some(seq), Some(ack))?;

// Convert between legacy and new formats
let snapshot = compat::legacy_game_state_to_snapshot(old_value)?;

Usage

Add to your Cargo.toml:

[dependencies]
runecast-protocol = { path = "../runecast-protocol" }
use runecast_protocol::{
    ClientMessage, ServerMessage, ErrorCode,
    Position, Grid, PlayerInfo,
};

// Deserialize client message
let msg: ClientMessage = serde_json::from_str(&text)?;

// Create server response
let response = ServerMessage::Error {
    code: ErrorCode::NotYourTurn,
    message: "It's not your turn".to_string(),
    details: None,
};

Constants

pub const HEARTBEAT_INTERVAL_MS: u64 = 30_000;
pub const HEARTBEAT_TIMEOUT_MS: u64 = 45_000;
pub const RECONNECT_GRACE_MS: u64 = 60_000;
pub const MAX_MESSAGE_SIZE: usize = 65_536;
pub const PROTOCOL_VERSION: &str = "1.0.0";

Documentation

Full API documentation is automatically generated and published to GitHub Pages:

The documentation is rebuilt on every push to master via GitHub Actions.

Documentation Deployment

This repository uses GitHub Actions to automatically build and publish API documentation to GitHub Pages.

How It Works

The docs.yml workflow:

  1. Triggers on every push to master branch (or manual workflow dispatch)
  2. Builds the Rust documentation using cargo doc --no-deps
  3. Prepares the docs folder with:
    • All generated documentation files
    • A redirect index.html that points to the main crate docs
    • A .nojekyll file to ensure GitHub Pages doesn't ignore files starting with _
  4. Commits the docs/ folder back to the repository
  5. Publishes to GitHub Pages (served from the docs folder)

Configuring GitHub Pages

To enable GitHub Pages for this repository:

  1. Go to Settings → Pages
  2. Under Source, select:
    • Branch: master (or your main branch)
    • Folder: /docs
  3. Click Save

GitHub Pages will then be available at: https://cycle-five.github.io/runecast-protocol/

Local Documentation

To view the documentation locally:

cargo doc --no-deps --open

This will build and open the documentation in your default browser.

Documentation Standards

All public API items should have documentation comments:

  • Modules: Describe the purpose and contents
  • Structs/Enums: Explain what they represent
  • Fields: Document important fields (especially public ones)
  • Functions: Explain parameters, return values, and behavior
  • Examples: Include usage examples where helpful

The codebase follows these standards and cargo doc produces no warnings.

Why a Separate Crate?

  1. Shared between server and potential Rust client - If you ever build a Rust client or CLI tool, it can use these same types
  2. Forces clean boundaries - Protocol types can't accidentally depend on server internals
  3. Version independently - Protocol changes are explicit and trackable
  4. Documentation - Acts as API documentation for frontend developers

Part of RuneCast. This is published for transparency and not intended as a general-purpose library.

About

RuneCast's communication protocol messages and types.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages