Networking Protocol Specification
This document defines the protocol used for networking in our MTG Commander game engine. It outlines the message formats, synchronization mechanisms, and handling of game-specific concepts.
Table of Contents
- Message Format
- Connection Handshake
- Authentication
- Game State Synchronization
- Player Actions
- Card Interactions
- MTG-Specific Handling
- Error Handling
Message Format
All networked messages follow a standardized format using bevy_replicon's structure:
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct NetworkMessage<T> { /// Message type identifier pub message_type: MessageType, /// Payload data pub payload: T, /// Sequence number for ordering pub sequence: u64, /// Timestamp when the message was created pub timestamp: f64, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub enum MessageType { /// Game state synchronization GameState, /// Player action Action, /// System message System, /// Chat message Chat, /// Error message Error, } }
Connection Handshake
The connection handshake establishes a client-server relationship and verifies compatibility:
- Client Request: Client sends connection request with version information
- Server Validation: Server validates compatibility and available slots
- Connection Accept/Reject: Server sends acceptance or rejection
- Game State Sync: Server sends initial game state if accepted
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ConnectionRequest { /// Client version pub version: String, /// Player name/identifier pub player_name: String, /// Unique client identifier pub client_id: Option<u64>, /// Reconnection token for game in progress (if any) pub reconnect_token: Option<String>, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ConnectionResponse { /// Whether the connection was accepted pub accepted: bool, /// Reason for rejection if applicable pub rejection_reason: Option<String>, /// Server-assigned client ID pub assigned_client_id: Option<u64>, /// Session identifier pub session_id: Option<String>, } }
Authentication
For secure multiplayer, we implement a simple authentication system:
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct AuthRequest { /// Username or identifier pub username: String, /// Password or token pub password_hash: String, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct AuthResponse { /// Whether authentication was successful pub success: bool, /// Authentication token for future use pub token: Option<String>, /// Expiration time for the token pub expiration: Option<f64>, } }
Game State Synchronization
Game state synchronization happens at multiple levels:
- Full State Sync: Complete game state sent on connection or major changes
- Delta Updates: Only changed components sent during normal gameplay
- Targeted Updates: Some updates only sent to specific clients (for hidden information)
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct GameStateSync { /// Whether this is a full sync or delta pub is_full_sync: bool, /// Current game turn pub turn: u32, /// Current game phase pub phase: Phase, /// Active player entity ID pub active_player: u64, /// Priority player entity ID pub priority_player: Option<u64>, /// Entity changes (components) pub entity_changes: Vec<EntityChange>, /// Game events that occurred pub events: Vec<GameEvent>, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct EntityChange { /// Entity ID pub entity_id: u64, /// Component changes pub components: Vec<ComponentChange>, /// Whether the entity was created pub is_new: bool, /// Whether the entity was destroyed pub is_removed: bool, } }
Player Actions
Players send action requests to the server, which validates and processes them:
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct PlayerActionRequest { /// Client ID pub client_id: u64, /// Action type pub action_type: NetworkedActionType, /// Target entities pub targets: Vec<u64>, /// Action parameters pub parameters: Option<ActionParameters>, } #[derive(Serialize, Deserialize, Clone, Debug)] pub enum NetworkedActionType { /// Play a land card PlayLand, /// Cast a spell CastSpell, /// Activate an ability ActivateAbility, /// Declare attackers DeclareAttackers, /// Declare blockers DeclareBlockers, /// Pass priority PassPriority, /// Make a mulligan decision Mulligan, /// Choose to put a commander in the command zone CommanderZoneChoice, /// Stack response RespondToStack, /// Choose targets ChooseTargets, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ActionParameters { /// Mana payment information pub mana_payment: Option<Mana>, /// Ability index (for cards with multiple abilities) pub ability_index: Option<usize>, /// X value for spells with X in cost pub x_value: Option<u32>, /// Selected mode for modal spells pub selected_mode: Option<u32>, /// Additional costs paid pub additional_costs: Option<Vec<AdditionalCost>>, } }
Card Interactions
Card interactions require special handling for targeting, stack effects, and zone changes:
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct TargetSelection { /// Source card/ability pub source: u64, /// Selected targets pub targets: Vec<Target>, /// Whether all required targets have been selected pub is_complete: bool, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Target { /// Target entity ID pub entity_id: u64, /// Target type pub target_type: TargetType, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub enum TargetType { /// Player Player, /// Creature Creature, /// Permanent Permanent, /// Spell on stack Spell, /// Zone Zone, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct StackEffect { /// Effect ID pub id: u64, /// Source entity pub source: u64, /// Controller pub controller: u64, /// Targets pub targets: Vec<Target>, /// Effect details pub effect: EffectDetails, } }
MTG-Specific Handling
MTG has unique concepts that require special handling:
Hidden Information
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct HiddenInformation { /// Zone type containing hidden information pub zone: ZoneType, /// Owner of the zone pub owner: u64, /// Hidden card IDs pub card_ids: Vec<u64>, /// Whether zone contents were reordered pub reordered: bool, } #[derive(Serialize, Deserialize, Clone, Debug)] pub struct RevealedCard { /// Card entity ID pub card_id: u64, /// Zone before reveal pub from_zone: ZoneType, /// Players who can see the card pub visible_to: Vec<u64>, /// Reveal source (card/effect that caused reveal) pub reveal_source: Option<u64>, } }
Randomization
To prevent cheating, all randomization happens on the server:
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct RandomizationRequest { /// Type of randomization pub randomization_type: RandomizationType, /// Entities involved pub entities: Vec<u64>, /// Additional parameters pub parameters: Option<RandomizationParameters>, } #[derive(Serialize, Deserialize, Clone, Debug)] pub enum RandomizationType { /// Shuffle a library ShuffleLibrary, /// Flip a coin CoinFlip, /// Roll a die DieRoll, /// Select random targets RandomTargets, /// Random card discard RandomDiscard, } }
Priority System
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct PriorityUpdate { /// Player with priority pub priority_player: u64, /// Time left to respond (in milliseconds) pub time_remaining: Option<u64>, /// What's currently on the stack pub stack_size: usize, /// Current phase/step pub current_phase: Phase, } }
Error Handling
#![allow(unused)] fn main() { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct NetworkError { /// Error code pub code: u32, /// Error message pub message: String, /// Related entity (if applicable) pub related_entity: Option<u64>, /// Related action (if applicable) pub related_action: Option<NetworkedActionType>, } #[derive(Serialize, Deserialize, Clone, Debug)] pub enum ErrorCode { /// Invalid action InvalidAction = 1000, /// Invalid target InvalidTarget = 1001, /// Not your turn NotYourTurn = 1002, /// Not your priority NotYourPriority = 1003, /// Invalid mana payment InvalidManaPayment = 1004, /// Game rule violation GameRuleViolation = 1005, /// Connection error ConnectionError = 2000, /// Synchronization error SyncError = 2001, /// Internal server error InternalError = 9000, } }
This protocol specification provides a comprehensive framework for networked gameplay in our MTG Commander engine. It balances efficiency, security, and game rule enforcement while handling the unique requirements of Magic: The Gathering, such as hidden information, complex targeting, and state-based effects.