Skip to main content

yog_player/
lib.rs

1//! Player access for Yog mods.
2//!
3//! [`Player`] is a thin wrapper over [`yog_entity::Entity`]: it delegates all
4//! entity-level operations (teleport, position, health …) to that layer and
5//! adds the player-specific extras (inventory, networking) on top.
6
7use yog_core::Server;
8use yog_entity::Entity;
9
10/// A handle to one player, bound to a [`Server`].
11///
12/// Construct with [`Player::new`] when you only have the player name (most
13/// event callbacks), or with [`Player::with_uuid`] when you also have the UUID
14/// (e.g. from [`yog_command::CommandContext`]) — the latter unlocks the full
15/// entity-layer API.
16pub struct Player<'a> {
17    server: &'a dyn Server,
18    name: String,
19    uuid: Option<String>,
20}
21
22impl<'a> Player<'a> {
23    /// Bind to the player called `name`. Entity-level ops that require a UUID
24    /// will return `None`/`false`; use [`Player::with_uuid`] to unlock them.
25    pub fn new(server: &'a dyn Server, name: impl Into<String>) -> Self {
26        Self { server, name: name.into(), uuid: None }
27    }
28
29    /// Bind to the player with both `name` and `uuid` (full functionality).
30    pub fn with_uuid(
31        server: &'a dyn Server,
32        name: impl Into<String>,
33        uuid: impl Into<String>,
34    ) -> Self {
35        Self { server, name: name.into(), uuid: Some(uuid.into()) }
36    }
37
38    pub fn name(&self) -> &str {
39        &self.name
40    }
41
42    pub fn uuid(&self) -> Option<&str> {
43        self.uuid.as_deref()
44    }
45
46    /// The underlying [`Entity`] handle, available when a UUID was provided.
47    pub fn entity(&self) -> Option<Entity<'_>> {
48        self.uuid.as_deref().map(|u| Entity::new(self.server, u))
49    }
50
51    // ── player-specific ops ─────────────────────────────────────────────────
52
53    /// Give `count` of `item_id` (e.g. `"minecraft:diamond"`).
54    pub fn give(&self, item_id: &str, count: u32) -> bool {
55        self.server.give_item(&self.name, item_id, count)
56    }
57
58    /// Send a raw-byte packet to this player on `channel` (server → client).
59    pub fn send_packet(&self, channel: &str, payload: &[u8]) -> bool {
60        self.server.send_to_player(&self.name, channel, payload)
61    }
62
63    // ── entity-level ops (delegated) ────────────────────────────────────────
64
65    /// Teleport to `(x, y, z)`. Uses the entity layer when a UUID is known;
66    /// falls back to the player-name primitive otherwise.
67    pub fn teleport(&self, x: f64, y: f64, z: f64) -> bool {
68        match &self.uuid {
69            Some(u) => self.server.entity_teleport(u, x, y, z),
70            None => self.server.teleport(&self.name, x, y, z),
71        }
72    }
73
74    /// Current position, or `None` if the entity isn't loaded / UUID unknown.
75    pub fn position(&self) -> Option<(f64, f64, f64)> {
76        self.entity()?.position()
77    }
78
79    /// Health, or `None` if UUID unknown.
80    pub fn health(&self) -> Option<f32> {
81        self.entity()?.health()
82    }
83
84    /// Set health; returns `false` if UUID unknown.
85    pub fn set_health(&self, health: f32) -> bool {
86        self.entity().map_or(false, |e: Entity<'_>| e.set_health(health))
87    }
88
89    /// Kill/remove this player entity; returns `false` if UUID unknown.
90    pub fn kill(&self) -> bool {
91        self.entity().map_or(false, |e: Entity<'_>| e.kill())
92    }
93
94    /// Send a title+subtitle screen to this player.
95    pub fn send_title(
96        &self,
97        title: &str,
98        subtitle: &str,
99        fadein: i32,
100        stay: i32,
101        fadeout: i32,
102    ) -> bool {
103        self.server.send_title(&self.name, title, subtitle, fadein, stay, fadeout)
104    }
105
106    /// Send a message to the action-bar (above hotbar).
107    pub fn send_actionbar(&self, message: &str) -> bool {
108        self.server.send_actionbar(&self.name, message)
109    }
110
111    /// Disconnect this player with a reason message.
112    pub fn kick(&self, reason: &str) -> bool {
113        self.server.kick_player(&self.name, reason)
114    }
115
116    /// Change this player's game mode (`"survival"`, `"creative"`, `"adventure"`, `"spectator"`).
117    pub fn set_gamemode(&self, gamemode: &str) -> bool {
118        self.server.set_gamemode(&self.name, gamemode)
119    }
120
121    /// Play a sound at this player's position (audible to nearby players too).
122    pub fn play_sound(&self, sound_id: &str, volume: f32, pitch: f32) -> bool {
123        self.server.play_sound_to_player(&self.name, sound_id, volume, pitch)
124    }
125
126    pub fn add_effect(
127        &self,
128        effect_id: &str,
129        duration_ticks: i32,
130        amplifier: u8,
131        show_particles: bool,
132    ) -> bool {
133        self.entity().map_or(false, |e: Entity<'_>| {
134            e.add_effect(effect_id, duration_ticks, amplifier, show_particles)
135        })
136    }
137
138    pub fn remove_effect(&self, effect_id: &str) -> bool {
139        self.entity().map_or(false, |e: Entity<'_>| e.remove_effect(effect_id))
140    }
141
142    pub fn clear_effects(&self) -> bool {
143        self.entity().map_or(false, |e: Entity<'_>| e.clear_effects())
144    }
145
146    // ── inventory ────────────────────────────────────────────────────────────
147
148    /// All occupied slots: `(slot_index, item_id, count)`.
149    pub fn inventory(&self) -> Vec<(u32, String, u32)> {
150        self.server.player_inventory(&self.name)
151    }
152
153    /// Set or clear (count==0) a specific inventory slot.
154    pub fn set_slot(&self, slot: u32, item_id: &str, count: u32) -> bool {
155        self.server.player_set_slot(&self.name, slot, item_id, count)
156    }
157
158    // ── cross-dimension teleport ─────────────────────────────────────────────
159
160    /// Teleport to `(x, y, z)` in `dimension` (may be a different dimension).
161    pub fn teleport_to_dim(&self, dimension: &str, x: f64, y: f64, z: f64) -> bool {
162        self.server.teleport_to_dim(&self.name, dimension, x, y, z)
163    }
164}