Files
Documentation/content/world/entities/player-api.en.md
2026-01-20 20:33:59 +01:00

905 lines
24 KiB
Markdown

---
title: Player API
type: docs
weight: 4
---
The Player class provides extensive APIs for player management, including communication, health, inventory, permissions, and more.
## API Overview
{{< cards cols="3" >}}
{{< card link="#identity--connection" title="Identity & Connection" subtitle="Name, UUID, network" >}}
{{< card link="#communication" title="Communication" subtitle="Messages, titles, sounds" >}}
{{< card link="#health--combat" title="Health & Combat" subtitle="HP, damage, effects" >}}
{{< card link="#inventory-api" title="Inventory" subtitle="Items, slots, armor" >}}
{{< card link="#permissions" title="Permissions" subtitle="Access control" >}}
{{< card link="#movement--teleportation" title="Movement" subtitle="Teleport, position" >}}
{{< /cards >}}
---
## Identity & Connection
### Basic Information
{{< tabs items="Methods,Example" >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `getUuid()` | `UUID` | Unique player identifier |
| `getDisplayName()` | `String` | Display name (from PlayerRef username) |
| `getClientViewRadius()` | `int` | Player's view radius in chunks |
| `getViewRadius()` | `int` | Effective view radius (min of client and server) |
| `hasPermission(String)` | `boolean` | Check permission |
| `sendMessage(Message)` | `void` | Send message to player |
| `getInventory()` | `Inventory` | Player's inventory |
| `getGameMode()` | `GameMode` | Current game mode |
{{< /tab >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Access Player component via ref.getStore()
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
UUID uuid = player.getUuid();
String displayName = player.getDisplayName();
int viewRadius = player.getViewRadius();
GameMode mode = player.getGameMode();
// Check permission
if (player.hasPermission("myplugin.admin")) {
player.sendMessage(Message.raw("You are an admin!"));
}
// Get PlayerRef for more info
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
String username = playerRef.getUsername();
String language = playerRef.getLanguage();
```
{{< /tab >}}
{{< /tabs >}}
### PlayerRef (Thread-Safe Reference)
{{< callout type="info" >}}
**Important:** `PlayerRef` is a component that provides thread-safe access to player data. It's stored in the entity component system and manages the player's connection, position, and various managers.
{{< /callout >}}
{{< tabs items="Methods,Entity Reference,Connection & Messaging,Server Transfer" >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `getUuid()` | `UUID` | Player's unique identifier |
| `getUsername()` | `String` | Player's username |
| `getLanguage()` | `String` | Player's language setting |
| `setLanguage(String)` | `void` | Update language |
| `getReference()` | `Ref<EntityStore>` | Entity reference (null if not in world) |
| `getHolder()` | `Holder<EntityStore>` | Entity holder (when not in store) |
| `isValid()` | `boolean` | Whether reference or holder exists |
| `getTransform()` | `Transform` | Current position and rotation |
| `getHeadRotation()` | `Vector3f` | Head rotation |
| `getPacketHandler()` | `PacketHandler` | Network handler |
| `getChunkTracker()` | `ChunkTracker` | Chunk loading tracker |
| `getHiddenPlayersManager()` | `HiddenPlayersManager` | Player visibility manager |
{{< /tab >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Get PlayerRef via ref.getStore()
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Check if player is valid
if (playerRef != null && playerRef.isValid()) {
UUID uuid = playerRef.getUuid();
String username = playerRef.getUsername();
// Get entity reference
Ref<EntityStore> entityRef = playerRef.getReference();
if (entityRef != null) {
// Player is in a world - access via entityRef.getStore()
Player player = entityRef.getStore().getComponent(entityRef, Player.getComponentType());
}
}
// Get component from PlayerRef (convenience method)
Player playerComponent = playerRef.getComponent(Player.getComponentType());
```
{{< /tab >}}
{{< tab >}}
```java
// Send message to player
playerRef.sendMessage(Message.translation("welcome.message"));
// Get packet handler for more advanced operations
PacketHandler handler = playerRef.getPacketHandler();
// Get player position info
Transform transform = playerRef.getTransform();
Vector3d position = transform.getPosition();
Vector3f rotation = transform.getRotation();
Vector3f headRotation = playerRef.getHeadRotation();
// Update position (called internally by systems)
playerRef.updatePosition(world, transform, headRotation);
```
{{< /tab >}}
{{< tab >}}
```java
// Transfer player to another server (for server networks)
playerRef.referToServer("play.example.com", 25565);
// With custom data payload
byte[] referralData = serializePlayerData(playerRef);
playerRef.referToServer("play.example.com", 25565, referralData);
// Note: Max referral data size is 4096 bytes
```
{{< /tab >}}
{{< /tabs >}}
---
## Communication
### Sending Messages
{{< tabs items="Chat Messages,Translation Messages,Notifications" >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Send chat message via PlayerRef
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Simple message
playerRef.sendMessage(Message.raw("Hello, World!"));
// Raw message
playerRef.sendMessage(Message.raw("Plain text message"));
// Using Message interface (IMessageReceiver)
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
player.sendMessage(Message.raw("Message via Player interface"));
```
{{< /tab >}}
{{< tab >}}
```java
// Using translation keys
playerRef.sendMessage(Message.translation("welcome.message"));
// With parameters
playerRef.sendMessage(
Message.translation("greeting.message")
.param("name", playerRef.getUsername())
);
// With color
playerRef.sendMessage(
Message.translation("server.warning")
.color("#ff5555")
);
```
{{< /tab >}}
{{< tab >}}
```java
// Hytale uses Notifications instead of titles/action bars
PacketHandler handler = playerRef.getPacketHandler();
// Simple notification
NotificationUtil.sendNotification(handler, "Welcome to the server!");
// Notification with style
NotificationUtil.sendNotification(handler,
Message.raw("Achievement Unlocked!"),
NotificationStyle.Default
);
// With secondary message
NotificationUtil.sendNotification(handler,
Message.raw("Quest Complete"),
Message.raw("You earned 100 XP"),
NotificationStyle.Default
);
// With icon
NotificationUtil.sendNotification(handler,
Message.raw("New Item"),
"icon_name",
NotificationStyle.Default
);
// With item display
NotificationUtil.sendNotification(handler,
Message.raw("You received:"),
Message.raw("Diamond x10"),
itemWithMetadata,
NotificationStyle.Default
);
// Send to all players in universe
NotificationUtil.sendNotificationToUniverse(
Message.raw("Server restarting in 5 minutes!")
);
```
{{< /tab >}}
{{< /tabs >}}
### Playing Sounds
```java
// Play 3D sound to player
SoundUtil.playSoundEvent3dToPlayer(
playerRef,
TempAssetIdUtil.getSoundEventIndex("SFX_UI_Notification"),
SoundCategory.UI,
position,
componentAccessor
);
// Play 2D sound (no position)
SoundUtil.playSoundEvent2d(
ref,
TempAssetIdUtil.getSoundEventIndex("SFX_Player_Pickup_Item"),
SoundCategory.UI,
componentAccessor
);
// Play sound via world
world.playSound(soundEvent, position, volume, pitch);
```
---
## Health & Stats
{{< callout type="info" >}}
Hytale uses an **EntityStats system** for managing health and other stats. Stats are managed via `EntityStatComponent` and include Health, Oxygen, Stamina, Mana, SignatureEnergy, and Ammo.
{{< /callout >}}
### Entity Stats System
{{< tabs items="Available Stats,Accessing Stats,Modifying Stats" >}}
{{< tab >}}
| Stat | Description |
|------|-------------|
| `Health` | Entity health points |
| `Oxygen` | Breath/oxygen level |
| `Stamina` | Physical stamina |
| `Mana` | Magical energy |
| `SignatureEnergy` | Special ability energy |
| `Ammo` | Ammunition count |
These stats are defined via asset configuration and indexed at runtime.
```java
// Get stat indices (from DefaultEntityStatTypes)
int healthIndex = DefaultEntityStatTypes.getHealth();
int staminaIndex = DefaultEntityStatTypes.getStamina();
int manaIndex = DefaultEntityStatTypes.getMana();
```
{{< /tab >}}
{{< tab >}}
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Get EntityStatComponent from entity via ref.getStore()
EntityStatComponent statsComponent = ref.getStore().getComponent(
ref,
EntityStatComponent.getComponentType()
);
// Get specific stat value
int healthIndex = DefaultEntityStatTypes.getHealth();
float currentHealth = statsComponent.getValue(healthIndex);
float maxHealth = statsComponent.getMaxValue(healthIndex);
// Check percentage
float healthPercent = currentHealth / maxHealth;
```
{{< /tab >}}
{{< tab >}}
```java
// Modify stat values through the stats system
// Stats are typically modified through the EntityStatsModule
// and related systems (DamageSystems, HealSystems, etc.)
// Example: Apply damage via component
// The DamageSystems handle damage application
ref.getStore().putComponent(ref, Damage.getComponentType(),
new Damage(damageAmount, damageSource));
```
{{< /tab >}}
{{< /tabs >}}
### Invulnerability
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Make player invulnerable (adds Invulnerable component)
ref.getStore().putComponent(ref, Invulnerable.getComponentType(), Invulnerable.INSTANCE);
// Remove invulnerability
ref.getStore().tryRemoveComponent(ref, Invulnerable.getComponentType());
// Check invulnerability
boolean isInvulnerable = ref.getStore().getArchetype(ref)
.contains(Invulnerable.getComponentType());
// Note: Creative mode automatically sets Invulnerable
```
### Spawn Protection
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Players have temporary invulnerability after spawning
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
// Check if player has spawn protection (3 seconds after spawn)
if (player.hasSpawnProtection()) {
// Player is temporarily invulnerable
}
// Check if waiting for client ready
if (player.isWaitingForClientReady()) {
// Player hasn't finished loading yet
}
```
---
## Inventory API
### Basic Inventory Operations
{{< tabs items="Get Items,Set Items,Utility Methods" >}}
{{< tab >}}
```java
// Get inventory
PlayerInventory inventory = player.getInventory();
// Get item in hand
ItemStack heldItem = player.getHeldItem();
ItemStack mainHand = player.getMainHandItem();
ItemStack offHand = player.getOffHandItem();
// Get specific slot
ItemStack slot5 = inventory.getItem(5);
// Get armor
ArmorInventory armor = player.getArmor();
ItemStack helmet = armor.getHelmet();
ItemStack chestplate = armor.getChestplate();
ItemStack leggings = armor.getLeggings();
ItemStack boots = armor.getBoots();
```
{{< /tab >}}
{{< tab >}}
```java
// Set held item
ItemStack sword = new ItemStack(ItemTypes.DIAMOND_SWORD);
player.setHeldItem(sword);
// Set specific slot
inventory.setItem(0, new ItemStack(ItemTypes.APPLE, 64));
// Set armor
armor.setHelmet(new ItemStack(ItemTypes.DIAMOND_HELMET));
armor.setChestplate(new ItemStack(ItemTypes.DIAMOND_CHESTPLATE));
armor.setLeggings(new ItemStack(ItemTypes.DIAMOND_LEGGINGS));
armor.setBoots(new ItemStack(ItemTypes.DIAMOND_BOOTS));
// Clear slot
inventory.setItem(5, null);
```
{{< /tab >}}
{{< tab >}}
```java
// Give item (finds empty slot)
ItemStack diamond = new ItemStack(ItemTypes.DIAMOND, 64);
boolean success = player.giveItem(diamond);
if (!success) {
// Inventory full, drop at feet
player.dropItem(diamond);
}
// Check for item
if (inventory.contains(ItemTypes.DIAMOND)) {
getLogger().at(Level.INFO).log("Player has diamonds");
}
// Remove item
inventory.remove(ItemTypes.DIAMOND, 10);
// Clear inventory
inventory.clear();
// Count items
int diamondCount = inventory.countItem(ItemTypes.DIAMOND);
```
{{< /tab >}}
{{< /tabs >}}
### Advanced Inventory
```java
// Open another inventory (chest, custom UI)
Inventory chest = world.getBlockEntity(chestPos).getInventory();
player.openInventory(chest);
// Create custom inventory
Inventory customInv = new Inventory(27, "Shop"); // 27 slots
customInv.setItem(13, new ItemStack(ItemTypes.DIAMOND, 1));
player.openInventory(customInv);
// Close open inventory
player.closeInventory();
// Get cursor item (what player is holding with mouse)
ItemStack cursor = player.getCursorItem();
player.setCursorItem(null);
```
---
## Permissions
### Permission Checking
{{< tabs items="Basic Checks,Permission Levels,Custom Permissions" >}}
{{< tab >}}
```java
// Check single permission
if (player.hasPermission("myplugin.admin")) {
player.sendMessage("You are an admin!");
}
// Check operator status
if (player.isOp()) {
player.sendMessage("You are an operator!");
}
// Multiple permission check
if (player.hasPermission("myplugin.fly") ||
player.hasPermission("myplugin.admin")) {
enableFlight(player);
}
```
{{< /tab >}}
{{< tab >}}
```java
// Permission hierarchy example
public boolean hasMinimumRank(Player player, String rank) {
switch (rank) {
case "owner":
return player.hasPermission("rank.owner");
case "admin":
return player.hasPermission("rank.admin") ||
player.hasPermission("rank.owner");
case "mod":
return player.hasPermission("rank.mod") ||
player.hasPermission("rank.admin") ||
player.hasPermission("rank.owner");
default:
return true;
}
}
```
{{< /tab >}}
{{< tab >}}
```java
// Grant permission
player.addPermission("myplugin.vip");
// Revoke permission
player.removePermission("myplugin.vip");
// Get all permissions
Set<String> permissions = player.getPermissions();
for (String perm : permissions) {
getLogger().at(Level.INFO).log("Has permission: " + perm);
}
// Set operator status
player.setOp(true);
```
{{< /tab >}}
{{< /tabs >}}
---
## Movement & Teleportation
### Position & Movement
{{< tabs items="Position Methods,Movement State,Velocity" >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `getPosition()` | `Vector3d` | Current position |
| `setPosition(Vector3d)` | `void` | Set position |
| `getEyePosition()` | `Vector3d` | Eye level position |
| `getRotation()` | `Vector3f` | Look direction |
| `setRotation(Vector3f)` | `void` | Set look direction |
| `getEyeDirection()` | `Vector3d` | Normalized look vector |
{{< /tab >}}
{{< tab >}}
| Method | Return Type | Description |
|--------|-------------|-------------|
| `isOnGround()` | `boolean` | Whether on solid ground |
| `isSprinting()` | `boolean` | Whether sprinting |
| `setSprinting(boolean)` | `void` | Set sprint state |
| `isSneaking()` | `boolean` | Whether sneaking |
| `setSneaking(boolean)` | `void` | Set sneak state |
| `isFlying()` | `boolean` | Whether flying |
| `setFlying(boolean)` | `void` | Set flying state |
| `isSwimming()` | `boolean` | Whether swimming |
{{< /tab >}}
{{< tab >}}
```java
// Get velocity
Vector3d velocity = player.getVelocity();
// Set velocity (launch player)
player.setVelocity(new Vector3d(0, 1.5, 0));
// Add to velocity
player.addVelocity(new Vector3d(1, 0, 0));
// Apply knockback
player.knockback(new Vector3d(-0.5, 0.5, -0.5));
```
{{< /tab >}}
{{< /tabs >}}
### Teleportation
{{< callout type="warning" >}}
Hytale uses a **component-based teleportation system**. You don't call `player.teleport()` directly. Instead, you add a `Teleport` component to the entity, and the system handles the teleportation.
{{< /callout >}}
{{< tabs items="Basic Usage,Cross-World,Options,System Details" >}}
{{< tab >}}
```java
// Get player reference from command context
Ref<EntityStore> playerRef = context.senderAsPlayerRef();
// Teleport to position in same world
Teleport teleport = new Teleport(
new Vector3d(100, 64, 100), // position
new Vector3f(0, 90, 0) // rotation (pitch, yaw, roll)
);
// Add the teleport component to trigger teleportation
playerRef.getStore().putComponent(playerRef, Teleport.getComponentType(), teleport);
// Using Transform
Transform destination = new Transform(position, rotation);
Teleport teleportFromTransform = new Teleport(destination);
```
{{< /tab >}}
{{< tab >}}
```java
// Get player reference from command context
Ref<EntityStore> playerRef = context.senderAsPlayerRef();
// Teleport to another world
World targetWorld = Universe.get().getWorld("other_world");
Teleport teleport = new Teleport(
targetWorld, // target world
new Vector3d(0, 64, 0), // position
new Vector3f(0, 0, 0) // rotation
);
playerRef.getStore().putComponent(playerRef, Teleport.getComponentType(), teleport);
// The TeleportSystems.PlayerMoveSystem handles:
// - Same world: Direct position update
// - Different world: Player removal and re-addition to target world
```
{{< /tab >}}
{{< tab >}}
```java
// Teleport with options using fluent builder
Teleport teleport = new Teleport(targetWorld, position, rotation)
.withHeadRotation(new Vector3f(0, 45, 0)) // Set head rotation
.withResetRoll() // Reset roll to 0
.withoutVelocityReset(); // Keep current velocity
// By default:
// - resetVelocity = true (player stops moving)
// - headRotation = same as body rotation
```
{{< /tab >}}
{{< tab >}}
The teleportation flow:
1. **Add Teleport component** to entity
2. **TeleportSystems.PlayerMoveSystem** detects the component
3. System sends `ClientTeleport` packet to player
4. **PendingTeleport** component tracks the teleport
5. When client confirms, position is updated
6. **Teleport component is removed** automatically
```java
// Internal packet sent:
ClientTeleport packet = new ClientTeleport(
teleportId,
new ModelTransform(position, direction, headRotation),
resetVelocity
);
```
{{< /tab >}}
{{< /tabs >}}
---
## Game Mode
{{< callout type="info" >}}
Hytale currently has **two game modes**: `Adventure` and `Creative`. These differ from Minecraft's game modes.
{{< /callout >}}
{{< tabs items="Available Modes,Getting/Setting,Creative Features" >}}
{{< tab >}}
| Mode | Value | Description |
|------|-------|-------------|
| `GameMode.Adventure` | 0 | Standard gameplay with survival mechanics |
| `GameMode.Creative` | 1 | Creative mode with building freedom |
{{< /tab >}}
{{< tab >}}
```java
// Get current game mode
GameMode mode = player.getGameMode();
// Check game mode
if (mode == GameMode.Creative) {
// Creative mode logic
} else if (mode == GameMode.Adventure) {
// Adventure mode logic
}
// Set game mode (requires component accessor)
// Note: This is typically done through systems/commands
Player.setGameMode(playerRef, GameMode.Creative, componentAccessor);
```
{{< /tab >}}
{{< tab >}}
When in Creative mode:
- Player is marked as `Invulnerable`
- `canFly` is enabled in MovementManager
- `canDecreaseItemStackDurability` returns false
- `canApplyItemStackPenalties` returns false
- Permission groups from GameModeType are applied
```java
// GameMode affects these behaviors automatically:
// - Flight enabled/disabled
// - Invulnerability
// - Item durability loss
// - Block placement restrictions
```
{{< /tab >}}
{{< /tabs >}}
---
## Server Actions
### Disconnecting Players
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Disconnect player via PacketHandler
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Disconnect with reason
playerRef.getPacketHandler().disconnect("You have been kicked!");
// Disconnect with translation message
playerRef.getPacketHandler().disconnect(
Message.translation("server.kick.reason").toString()
);
```
### Player Lookup
```java
// Get all online players through Universe
Universe universe = Universe.get();
// Players are accessed through world contexts
World world = universe.getWorld("main");
// Iterate players in world via entity store
// Players are entities with PlayerRef component
world.getEntityStore().forEach((ref, store) -> {
PlayerRef playerRef = store.getComponent(ref, PlayerRef.getComponentType());
if (playerRef != null) {
String username = playerRef.getUsername();
UUID uuid = playerRef.getUuid();
// Process player...
}
});
```
### Server Transfer
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Transfer player to another server (for server networks)
PlayerRef playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
// Redirect player to another server
playerRef.referToServer("play.example.com", 25565);
// With custom data (max 4096 bytes)
byte[] playerData = serializePlayerState(playerRef);
playerRef.referToServer("play.example.com", 25565, playerData);
```
---
## Practical Examples
### Welcome Plugin
```java
public class WelcomePlugin extends ServerPlugin {
@Override
public void setup(PluginSetup pluginSetup) {
// Register event listeners
}
@Override
public void start() {
// Subscribe to player connect event
HytaleServer.get().getEventBus()
.subscribe(PlayerConnectEvent.class, this::onPlayerConnect);
}
private void onPlayerConnect(PlayerConnectEvent event) {
String username = event.getUsername();
UUID uuid = event.getUuid();
getLogger().at(Level.INFO).log("Player connected: " + username + " (" + uuid + ")");
// Send welcome message after player is ready
// Note: PlayerReadyEvent has String key, so use registerGlobal
getEventRegistry().registerGlobal(PlayerReadyEvent.class, readyEvent -> {
Player player = readyEvent.getPlayer();
player.sendMessage(Message.raw("Welcome to the server, " + username + "!"));
});
}
@Override
public void shutdown() {
// Cleanup
}
}
```
### Permission Check Example
```java
public class PermissionPlugin extends ServerPlugin {
@Override
public void start() {
CommandRegistry registry = getCommandRegistry();
registry.register("admin", context -> {
CommandSender sender = context.getSource();
// Check permission
if (sender instanceof Player player) {
if (!player.hasPermission("myplugin.admin")) {
player.sendMessage(Message.raw("No permission!"));
return;
}
player.sendMessage(Message.raw("You are an admin!"));
}
});
}
}
```
### Inventory Management
```java
// Get entity reference from command context
Ref<EntityStore> ref = context.senderAsPlayerRef();
// Working with player inventory
Player player = ref.getStore().getComponent(ref, Player.getComponentType());
Inventory inventory = player.getInventory();
// Add item to inventory
ItemStack itemStack = new ItemStack(itemType, amount);
inventory.addItem(itemStack);
// Send inventory update to client
player.sendInventory();
// Access hotbar
HotbarManager hotbar = player.getHotbarManager();
```
---
## Best Practices
{{< callout type="info" >}}
**Player API Guidelines:**
- Use `PlayerRef` to get thread-safe player information
- Check `isValid()` before operations on stored references
- Use permissions via `player.hasPermission()` for access control
- Understand the component system: players are entities with components
{{< /callout >}}
{{< callout type="warning" >}}
**Component System:** Players in Hytale are entities managed by an ECS. Access player data via `ref.getStore()` and appropriate component types (`Player.getComponentType()`, `PlayerRef.getComponentType()`).
{{< /callout >}}
{{< callout type="error" >}}
**Critical:** Never store `Ref<EntityStore>` or `Player` references directly in static fields. Use `UUID` for storage and retrieve references via the component system when needed.
{{< /callout >}}