Read player data
This page shows how a plugin finds players, reads their data, tells their ids apart and changes them. In Paper a Player is an object with methods. In mcrs a player is an entity inside the world of a dimension, and its state is a set of components, so you write a query where Paper code would hold on to the object.
The page is short because the set of components grows with the project. The examples come from repository files, and we did not run them outside the repository tests.
Find players with a query
Section titled “Find players with a query”Every player entity has the marker component Player. A system in a dimension world sees the players of that dimension only, as Dimensions explains. The test harness finds a player with this kind of query:
pub fn player_in(&mut self, dim: Entity, anchor: Entity) -> Option<Entity> { let session = self.session_of(anchor)?; let world = self.world_in(dim); world .query_filtered::<(Entity, &Owner), With<Player>>() .iter(world) .find(|(_, owner)| owner.0 == session) .map(|(player, _)| player)}crates/mcrs_minecraft_server/tests/it/host_app/mod.rs:374-382
In a plugin you write the query as a system parameter, for example Query<(&GameProfile, &PlayerGameMode), With<Player>>. That line is an illustration and was not compiled. Its type names come from the files in the next section.
Components you can read
Section titled “Components you can read”GameProfile is the profile that the client logged in with:
#[derive(Debug, Clone, Component)]pub struct GameProfile { pub id: uuid::Uuid, pub username: String, pub properties: Vec<Property<String>>,}crates/mcrs_minecraft_engine/src/game_profile.rs:8-13
The game mode, the flight state and the operator level are in one file:
#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Deref, DerefMut)]pub struct PlayerGameMode(pub GameMode);
impl Default for PlayerGameMode { fn default() -> Self { Self(GameMode::Survival) }}
/// Whether the player flies, as its client reports it and its game mode allows.#[derive(Component, Debug, Clone, Copy, PartialEq, Eq, Deref, DerefMut, Default)]pub struct PlayerFlying(pub bool);crates/mcrs_minecraft_engine/src/ability.rs:12-23
PlayerOpLevel in the same file holds the operator level, which goes from 0 for a player who is not an operator up to 4. The test harness reads a game mode like this:
pub fn game_mode(&mut self, anchor: Entity) -> GameMode { let player = self.player(anchor); self.world().get::<PlayerGameMode>(player).unwrap().0}crates/mcrs_minecraft_server/tests/it/host_app/mod.rs:389-392
One player has three ids
Section titled “One player has three ids”You cannot use one of these ids in place of another.
| Id | Where | What it is |
|---|---|---|
GameProfile::id |
component of the player | the UUID of the profile, as in Paper |
PlayerSession |
component Owner of the player in a dimension world; also on the host |
the name of one connection. The host never hands out a value twice, and a change of dimension keeps it |
Entity |
the entity of the player inside one dimension world | a Bevy id that exists in one world only. The host also has an anchor entity for the same player, and it is a different entity |
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]pub struct PlayerSession(pub u64);crates/mcrs_minecraft_engine_world/src/session/sessions.rs:12-13
Use the session to address a player across worlds and to send packets. Use the UUID for anything that must survive a reconnect.
Change a player inside its own world
Section titled “Change a player inside its own world”Inside the world of a player you can insert or edit its components. Do not change a player from a system that runs in a different world. A change that crosses worlds is a message or an effect. The vanilla commands gamemode and teleport do this with command effects, see Add a command.
Send a message to a player
Section titled “Send a message to a player”mcrs has no player.sendMessage(). The low-level way is to write a packet for a session. The chat plugin does this for a player who has chat hidden:
if info.is_some_and(|info| info.chat_mode == ChatMode::Hidden) { let disabled: Text = Text::translate("chat.disabled.options", vec![]).color(Color::RED); if let Ok(disabled) = RawText::from_text(&disabled) { packet_writer.write(bus::to(owner.0, system_chat(disabled))); } return;}crates/mcrs_minecraft_chat_server/src/chat_plugin.rs:56-62
A friendlier layer for this does not exist yet. We know of no issue for it, so we promise nothing.
Saving player data is not done either.2 The Status page shows the state of saving.
Next: Test a plugin without a client, where a test joins a fake player and reads these components.
Footnotes
Section titled “Footnotes”Sources and verification
Verified: 26.4-snapshot-7, 51d45c26a, 2026-11-09
- crates/mcrs_minecraft_engine/src/ability.rs
- crates/mcrs_minecraft_engine/src/game_profile.rs
- crates/mcrs_minecraft_engine_world/src/entity/player/mod.rs
- crates/mcrs_minecraft_engine_world/src/session/sessions.rs
- crates/mcrs_minecraft_server/tests/it/host_app/mod.rs
- issue #158
Tracking: #158