--- title: PlayerRef type: docs weight: 4 --- PlayerRef provides a thread-safe way to reference players across worlds and threads. ## Why PlayerRef? {{< callout type="warning" >}} Direct Player references should never be stored or used across threads. Always use PlayerRef for: - Async operations - Storing player references - Cross-world player tracking {{< /callout >}} ## Getting a PlayerRef ```java import com.hypixel.hytale.server.core.entity.entities.Player; import com.hypixel.hytale.server.core.universe.PlayerRef; import java.util.UUID; Player player = ...; PlayerRef ref = player.getPlayerRef(); // PlayerRef contains essential player data UUID uuid = ref.getUuid(); String name = ref.getUsername(); // Note: getUsername(), NOT getName() ``` ## Using PlayerRef in Async Code ```java import com.hypixel.hytale.server.core.event.events.player.PlayerConnectEvent; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.Message; import com.hypixel.hytale.component.Ref; import java.util.concurrent.CompletableFuture; getEventRegistry().register(PlayerConnectEvent.class, event -> { PlayerRef ref = event.getPlayerRef(); World world = event.getWorld(); // Use ref in async operations CompletableFuture.runAsync(() -> { // Safe to use ref here PlayerData data = loadPlayerData(ref.getUuid()); // Return to world thread to interact with player if (world != null) { world.execute(() -> { Ref entityRef = ref.getReference(); if (entityRef != null && entityRef.isValid()) { applyData(entityRef, data); } // sendMessage is directly on PlayerRef ref.sendMessage(Message.raw("Your data has been loaded!")); }); } }); }); ``` ## PlayerRef Methods ```java PlayerRef ref = player.getPlayerRef(); // Get player UUID (always available) UUID uuid = ref.getUuid(); // Get player username (always available) // Note: It's getUsername(), NOT getName() String name = ref.getUsername(); // Get entity reference Ref entityRef = ref.getReference(); // Check if reference is valid boolean isValid = ref.isValid(); // Send message directly to player ref.sendMessage(Message.raw("Hello!")); // Get transform (position/rotation) Transform transform = ref.getTransform(); ``` {{< callout type="warning" >}} **Note:** `PlayerRef.getPlayer()` does NOT exist. PlayerRef stores essential data that survives world changes, but doesn't hold a direct Player reference. {{< /callout >}} ## World PlayerRefs Worlds provide access to PlayerRefs: ```java import com.hypixel.hytale.server.core.universe.Universe; import java.util.Collection; import java.util.List; World world = Universe.get().getWorld("default"); // Get all player refs in this world Collection playerRefs = world.getPlayerRefs(); // Get all active players (returns List) List players = world.getPlayers(); ``` ## Storing Player References ```java import com.hypixel.hytale.server.core.event.events.player.PlayerDisconnectEvent; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class MyPlugin extends JavaPlugin { // Store PlayerRefs, NOT Players private final Map playerData = new ConcurrentHashMap<>(); public MyPlugin(JavaPluginInit init) { super(init); } @Override public void setup() { getEventRegistry().register(PlayerConnectEvent.class, event -> { PlayerRef ref = event.getPlayerRef(); playerData.put(ref.getUuid(), new PlayerData(ref)); }); getEventRegistry().register(PlayerDisconnectEvent.class, event -> { playerData.remove(event.getPlayerRef().getUuid()); }); } } class PlayerData { private final PlayerRef playerRef; private int score; public PlayerData(PlayerRef ref) { this.playerRef = ref; } public void addScore(int amount) { this.score += amount; playerRef.sendMessage(Message.raw("Score: " + score)); } } ``` ## Cross-World Messaging ```java // Send message to player regardless of their current world public void sendGlobalMessage(PlayerRef ref, String message) { // PlayerRef.sendMessage handles cross-world messaging directly ref.sendMessage(Message.raw(message)); } // Broadcast to all players in all worlds public void broadcastAll(String message) { Message msg = Message.raw(message); for (World world : Universe.get().getWorlds().values()) { for (PlayerRef ref : world.getPlayerRefs()) { ref.sendMessage(msg); } } } ``` ## PlayerRef vs Player | Feature | Player | PlayerRef | |---------|--------|-----------| | Thread-safe | No | Yes | | Storable | No | Yes | | Cross-world | No | Yes | | Get UUID | Yes | Yes | | Get Username | Yes (`getDisplayName()`) | Yes (`getUsername()`) | | Send Message | Yes | Yes (directly) | | Get Position | Via TransformComponent | Via `getTransform()` | ## Best Practices {{< callout type="info" >}} **Always use PlayerRef when:** - Storing player references beyond the current method - Passing to async tasks - Tracking players across world changes - Building player-related data structures {{< /callout >}} ```java // Good: Store PlayerRef private final Map trackedPlayers = new ConcurrentHashMap<>(); // Bad: Store Player directly // private final Map players = new HashMap<>(); // DON'T DO THIS // Good: Use ref in async CompletableFuture.runAsync(() -> { PlayerRef ref = ...; // ref is safe here }); // Bad: Use Player in async // CompletableFuture.runAsync(() -> { // Player player = ...; // NOT SAFE // }); ```