HyCodeYourTale

Player Data

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() |

Last updated: 20. ledna 2026