//! Players known to a world. use crate::codec::{DecodeError, Reader, Writer}; use crate::inventory::Inventory; /// Stable identity of a player across sessions, chosen by the player's game. pub type PlayerKey = [u8; 16]; /// Index of a player in the world's player list. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct PlayerId(pub u8); /// Most players that can be online at the same time. pub const MAX_ONLINE_PLAYERS: usize = 4; /// Most players a world remembers. pub const MAX_KNOWN_PLAYERS: usize = 16; /// Longest stored player name, in bytes of UTF-8. pub const MAX_NAME_BYTES: usize = 32; /// Pickaxe tier every player starts with: stone. pub const STARTING_TOOL_TIER: u8 = 1; /// A player and everything that belongs to them personally. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Player { pub key: PlayerKey, pub name: String, pub online: bool, pub tool_tier: u8, /// First tick at which the player may act again. pub next_action_tick: u64, pub inventory: Inventory, } impl Player { /// Creates an online player with a stone pickaxe and an empty inventory. pub fn new(key: PlayerKey, name: &str) -> Self { Self { key, name: clamp_name(name), online: true, tool_tier: STARTING_TOOL_TIER, next_action_tick: 0, inventory: Inventory::new(), } } pub fn encode(&self, w: &mut Writer) { w.raw(&self.key); w.str(&self.name); w.bool(self.online); w.u8(self.tool_tier); w.u64(self.next_action_tick); self.inventory.encode(w); } pub fn decode(r: &mut Reader<'_>) -> Result { 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")); } let online = r.bool()?; let tool_tier = r.u8()?; let next_action_tick = r.u64()?; let inventory = Inventory::decode(r)?; Ok(Self { key, name, online, tool_tier, next_action_tick, inventory, }) } } /// Shortens `name` to at most [`MAX_NAME_BYTES`] bytes without splitting a /// character. pub fn clamp_name(name: &str) -> String { let mut end = name.len().min(MAX_NAME_BYTES); while !name.is_char_boundary(end) { end -= 1; } name[..end].to_owned() }