7f904b5e1d
Ein Spieler hat eine dauerhafte Kennung, einen Namen, eine Werkzeugstufe und ein Inventar. Befehle (Beitreten, Verlassen, Graben) sind der einzige Weg, die Welt zu ändern, und lassen sich kodieren. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
87 lines
2.5 KiB
Rust
87 lines
2.5 KiB
Rust
//! 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<Self, DecodeError> {
|
|
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()
|
|
}
|