5.7 KiB
5.7 KiB
title, type, weight
| title | type | weight |
|---|---|---|
| PlayerRef | docs | 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
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
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<EntityStore> 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
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<EntityStore> 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:
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<PlayerRef> playerRefs = world.getPlayerRefs();
// Get all active players (returns List<Player>)
List<Player> players = world.getPlayers();
Storing Player References
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<UUID, PlayerData> 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
// 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 >}}
// Good: Store PlayerRef
private final Map<UUID, PlayerRef> trackedPlayers = new ConcurrentHashMap<>();
// Bad: Store Player directly
// private final Map<UUID, Player> 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
// });