//! Everything a player can ask the world to do. use crate::codec::{DecodeError, Reader, Writer}; use crate::map::Pos; use crate::player::{clamp_name, PlayerId, PlayerKey, MAX_NAME_BYTES}; /// A request that changes the world. Commands are the only way to change it. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Command { /// A player enters the world, new or returning. Join { key: PlayerKey, name: String }, /// A player leaves the world. Their inventory stays. Leave { player: PlayerId }, /// A player hits the tile at `pos` with their pickaxe. Dig { player: PlayerId, pos: Pos }, } const TAG_JOIN: u8 = 1; const TAG_LEAVE: u8 = 2; const TAG_DIG: u8 = 3; impl Command { pub fn encode(&self, w: &mut Writer) { match self { Command::Join { key, name } => { w.u8(TAG_JOIN); w.raw(key); w.str(&clamp_name(name)); } Command::Leave { player } => { w.u8(TAG_LEAVE); w.u8(player.0); } Command::Dig { player, pos } => { w.u8(TAG_DIG); w.u8(player.0); w.i32(pos.x); w.i32(pos.y); } } } pub fn decode(r: &mut Reader<'_>) -> Result { match r.u8()? { TAG_JOIN => { let mut key = [0u8; 16]; key.copy_from_slice(r.raw(16)?); let name = r.string()?; if name.len() > MAX_NAME_BYTES { return Err(DecodeError::InvalidValue("player name")); } Ok(Command::Join { key, name }) } TAG_LEAVE => Ok(Command::Leave { player: PlayerId(r.u8()?), }), TAG_DIG => { let player = PlayerId(r.u8()?); let x = r.i32()?; let y = r.i32()?; Ok(Command::Dig { player, pos: Pos::new(x, y), }) } _ => Err(DecodeError::InvalidValue("command tag")), } } }