216 lines
5.7 KiB
Markdown
216 lines
5.7 KiB
Markdown
---
|
|
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<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
|
|
|
|
```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<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:
|
|
|
|
```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<PlayerRef> playerRefs = world.getPlayerRefs();
|
|
|
|
// Get all active players (returns List<Player>)
|
|
List<Player> 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<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
|
|
|
|
```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<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
|
|
// });
|
|
```
|