Player Data
Dokumentace k ukládání a správě dat hráčů v Hytale.
Obsah
| Soubor | Popis |
|--------|-------|
| DATA_STORAGE.md | PlayerStorage interface, providery, Holder serializace |
| CACHING.md | Cachování dat, dirty tracking, thread-safe patterns |
| PERSISTENCE.md | BsonUtil, ukládání/načítání, backup systém |
---
Rychlý Přehled
Data Hráče
Player Data
├── Runtime (ECS komponenty)
│ ├── Player - základní hráčská data
│ ├── PlayerRef - reference a UUID
│ ├── PlayerConfigData - konfigurace
│ └── Custom komponenty - vlastní runtime data
│
└── Persistent (PlayerStorage)
├── Holder - serializovaný kontejner
├── JSON soubory - players/{uuid}.json
└── Custom data - vlastní persistence
---
Přístupy k Player Datům
1. ECS Komponenty (Runtime)
Pro data která existují pouze když je hráč online:
// Vlastní komponenta
public class PlayerStats implements Component {
private int kills;
private int deaths;
}// Registrace
@Override
protected void setup() {
playerStatsType = getEntityStoreRegistry().registerComponent(
PlayerStats.class,
PlayerStats::new
);
}
// Použití
PlayerStats stats = store.ensureAndGetComponent(ref, playerStatsType);
stats.incrementKills();
2. PlayerStorage (Persistence)
Pro data která přetrvávají i po odpojení:
// Získání storage
PlayerStorage storage = Universe.get().getPlayerStorage();// Načtení
CompletableFuture> future = storage.load(uuid);
// Uložení
storage.save(uuid, holder);
3. Hybrid (Runtime + Persistence)
Kombinace obou přístupů:
// Při připojení - načti z persistence do komponenty
playerStorage.load(uuid).thenAccept(holder -> {
world.execute(() -> {
applyToComponents(player, holder);
});
});// Při odpojení - ulož z komponenty do persistence
collectFromComponents(player, holder);
playerStorage.save(uuid, holder);
---
PlayerStorage Providers
| Provider | ID | Popis |
|----------|-----|-------|
| DefaultPlayerStorageProvider | "Hytale" | Výchozí - deleguje na Disk |
| DiskPlayerStorageProvider | "Disk" | Ukládá do JSON souborů |
| Custom | - | Vlastní implementace |
---
Životní Cyklus Dat
PlayerReadyEvent
↓
Async načtení z PlayerStorage
↓
world.execute() - sync aplikace na komponenty
↓
[Hráč hraje - data v komponentách]
↓
Periodické auto-save (volitelné)
↓
PlayerDisconnectEvent
↓
Sběr dat z komponent
↓
Async uložení do PlayerStorage
---
Thread Safety
Správný Vzor
// Async načtení
CompletableFuture.supplyAsync(() -> {
return loadFromStorage(uuid); // Async I/O
}).thenAccept(data -> {
// Sync aplikace na world thread
world.execute(() -> {
applyData(player, data); // Bezpečný přístup
});
});
Špatný Vzor
// ŠPATNĚ - blokuje world thread
world.execute(() -> {
PlayerData data = loadFromStorage(uuid); // Blokující!
applyData(player, data);
});
---
BsonUtil API
| Metoda | Typ | Popis |
|--------|-----|-------|
| readDocument(path) | Async | Načte JSON s backup fallback |
| writeDocument(path, doc) | Async | Zapíše JSON s automatickou zálohou |
| readDocumentNow(path) | Sync | Blokující načtení |
| writeToBytes(doc) | Sync | Serializace do byte[] |
| readFromBytes(bytes) | Sync | Deserializace z byte[] |
---
PlayerConfigData
Vestavěná třída pro konfigurační data hráče:
PlayerConfigData data = player.getPlayerConfigData();// Per-world data
PlayerWorldData worldData = data.getPerWorldData("overworld");
// Známé recepty
Set recipes = data.getKnownRecipes();
// Reputation
Object2IntMap reputation = data.getReputationData();
// Dirty tracking
data.markChanged();
boolean wasDirty = data.consumeHasChanged();
---
Příklad: Kompletní Player Data Manager
public class MyPlayerDataManager {
private final Map cache = new ConcurrentHashMap<>();
private final Path dataFolder; public CompletableFuture load(UUID uuid) {
return CompletableFuture.supplyAsync(() -> {
MyPlayerData cached = cache.get(uuid);
if (cached != null) return cached;
Path file = dataFolder.resolve(uuid + ".json");
BsonDocument doc = BsonUtil.readDocumentNow(file);
MyPlayerData data;
if (doc != null) {
data = MyPlayerData.CODEC.decode(doc, ExtraInfo.THREAD_LOCAL.get());
} else {
data = new MyPlayerData(uuid);
}
cache.put(uuid, data);
return data;
});
}
public CompletableFuture save(UUID uuid) {
MyPlayerData data = cache.get(uuid);
if (data == null) return CompletableFuture.completedFuture(null);
BsonDocument doc = MyPlayerData.CODEC.encode(data, ExtraInfo.THREAD_LOCAL.get()).asDocument();
return BsonUtil.writeDocument(dataFolder.resolve(uuid + ".json"), doc);
}
public void unload(UUID uuid) {
cache.remove(uuid);
}
}
---
Shrnutí
| Typ Dat | Uložení | Kdy použít |
|---------|---------|------------|
| Runtime only | ECS komponenta | Data pouze pro online session |
| Persistent | PlayerStorage | Data která přetrvávají |
| Hybrid | Oboje | Runtime výkon + persistence |
| Operace | Metoda/Event |
|---------|--------------|
| Načtení | PlayerReadyEvent + storage.load() |
| Uložení | PlayerDisconnectEvent + storage.save() |
| Auto-save | ScheduledExecutorService |
| Aplikace dat | world.execute() |
| I/O operace | CompletableFuture.runAsync() |