HyCodeYourTale

BSON/JSON Files

BSON/JSON Files

Detailní dokumentace práce s BSON/JSON soubory v Hytale.

---

Přehled

Hytale používá BSON (Binary JSON) formát pro konfigurační a datové soubory. Soubory jsou ukládány s příponou .json ale interně pracují s BSON strukturami.

---

BsonUtil API

Hlavní třída pro práci se soubory: com.hypixel.hytale.server.core.util.BsonUtil

Import

import com.hypixel.hytale.server.core.util.BsonUtil;
import org.bson.BsonDocument;
import org.bson.BsonString;
import org.bson.BsonInt32;
import org.bson.BsonArray;
import org.bson.BsonValue;

---

Čtení Souborů

Asynchronní Čtení

Path path = Universe.get().getPath().resolve("config.json");

// Asynchronní čtení s backup fallback
CompletableFuture future = BsonUtil.readDocument(path);

future.thenAccept(document -> {
if (document != null) {
// Zpracování dokumentu
String value = document.getString("key").getValue();
}
});

// Nebo blokující
BsonDocument document = BsonUtil.readDocument(path).join();

Synchronní Čtení

// Okamžité čtení (blokující)
BsonDocument document = BsonUtil.readDocumentNow(path);

Čtení bez Backup

// Pokud nechcete automatický fallback na .bak soubor
CompletableFuture future = BsonUtil.readDocument(path, false);

Z Dekompilovaného Kódu - Implementace

// BsonUtil.java - readDocument
@Nonnull
public static CompletableFuture readDocument(@Nonnull Path file, boolean backup) {
BasicFileAttributes attributes;
try {
attributes = Files.readAttributes(file, BasicFileAttributes.class);
} catch (IOException var4) {
if (backup) {
return readDocumentBak(file); // Fallback na .bak
}
return CompletableFuture.completedFuture(null);
}

if (attributes.size() == 0L) {
LOGGER.at(Level.WARNING).log("Error loading file %s, file was found to be entirely empty", file);
return backup ? readDocumentBak(file) : CompletableFuture.completedFuture(null);
}

CompletableFuture future = CompletableFuture
.supplyAsync(() -> Files.readString(file))
.thenApply(BsonDocument::parse);

return backup ? future.exceptionallyCompose(t -> readDocumentBak(file)) : future;
}

---

Zápis Souborů

Asynchronní Zápis

Path path = Universe.get().getPath().resolve("config.json");
BsonDocument document = new BsonDocument();
document.put("key", new BsonString("value"));
document.put("count", new BsonInt32(42));

// Asynchronní zápis s automatickým backup
CompletableFuture future = BsonUtil.writeDocument(path, document);
future.join(); // Počkej na dokončení

Zápis bez Backup

// Bez vytváření .bak souboru
BsonUtil.writeDocument(path, document, false).join();

Synchronní Zápis s Codecem

// Pro komplexní objekty s codecem
BsonUtil.writeSync(path, MyClass.CODEC, myObject, getLogger());

Automatický Backup

Při zápisu se automaticky:
1. Vytvoří parent adresáře pokud neexistují
2. Přejmenuje existující soubor na .bak
3. Zapíše nový soubor

// Příklad: config.json → config.json.bak → nový config.json

---

BSON Typy

Základní Typy

| Java Typ | BSON Třída | Příklad |
|----------|------------|---------|
| String | BsonString | new BsonString("text") |
| int | BsonInt32 | new BsonInt32(42) |
| long | BsonInt64 | new BsonInt64(1000L) |
| double | BsonDouble | new BsonDouble(3.14) |
| boolean | BsonBoolean | new BsonBoolean(true) |
| null | BsonNull | BsonNull.VALUE |

Kolekce

| Java Typ | BSON Třída |
|----------|------------|
| List, Array | BsonArray |
| Map, Object | BsonDocument |
| byte[] | BsonBinary |

---

BsonDocument

Vytvoření

// Prázdný dokument
BsonDocument doc = new BsonDocument();

// S inicializací
BsonDocument doc = new BsonDocument("key", new BsonString("value"));

// Z JSON stringu
BsonDocument doc = BsonDocument.parse("{\"key\": \"value\", \"count\": 42}");

Zápis Hodnot

BsonDocument doc = new BsonDocument();

// Základní typy
doc.put("name", new BsonString("Server"));
doc.put("port", new BsonInt32(25565));
doc.put("maxPlayers", new BsonInt64(100L));
doc.put("ratio", new BsonDouble(1.5));
doc.put("enabled", new BsonBoolean(true));

// Vnořený dokument
BsonDocument nested = new BsonDocument();
nested.put("x", new BsonInt32(100));
nested.put("y", new BsonInt32(64));
nested.put("z", new BsonInt32(200));
doc.put("spawn", nested);

// Array
BsonArray array = new BsonArray();
array.add(new BsonString("player1"));
array.add(new BsonString("player2"));
doc.put("admins", array);

Čtení Hodnot

// Základní typy
String name = doc.getString("name").getValue();
int port = doc.getInt32("port").getValue();
long maxPlayers = doc.getInt64("maxPlayers").getValue();
double ratio = doc.getDouble("ratio").getValue();
boolean enabled = doc.getBoolean("enabled").getValue();

// Vnořený dokument
BsonDocument spawn = doc.getDocument("spawn");
int x = spawn.getInt32("x").getValue();

// Array
BsonArray admins = doc.getArray("admins");
for (BsonValue value : admins) {
String admin = value.asString().getValue();
}

Kontrola Existence

// Kontrola klíče
if (doc.containsKey("optionalKey")) {
String value = doc.getString("optionalKey").getValue();
}

// Bezpečné čtení s výchozí hodnotou
int port = doc.containsKey("port")
? doc.getInt32("port").getValue()
: 25565; // Výchozí hodnota

---

BsonArray

Vytvoření a Plnění

BsonArray array = new BsonArray();

// Přidání prvků
array.add(new BsonString("item1"));
array.add(new BsonString("item2"));
array.add(new BsonInt32(100));

Iterace

// For-each
for (BsonValue value : array) {
if (value.isString()) {
String str = value.asString().getValue();
} else if (value.isInt32()) {
int num = value.asInt32().getValue();
}
}

// Stream
List strings = array.stream()
.filter(BsonValue::isString)
.map(v -> v.asString().getValue())
.collect(Collectors.toList());

Přístup k Prvkům

// Podle indexu
BsonValue first = array.get(0);

// Velikost
int size = array.size();

// Je prázdný?
boolean empty = array.isEmpty();

---

Konverze

BSON ↔ JSON String

// BsonDocument → JSON String
String json = BsonUtil.toJson(document);

// JSON String → BsonDocument
BsonDocument doc = BsonDocument.parse(json);

BSON ↔ Bytes

// BsonDocument → byte[]
byte[] bytes = BsonUtil.writeToBytes(document);

// byte[] → BsonDocument
BsonDocument doc = BsonUtil.readFromBytes(bytes);

BSON ↔ JsonElement (Gson)

// BsonDocument → JsonElement
JsonElement element = BsonUtil.translateBsonToJson(document);

// JsonElement → BsonValue
BsonValue bson = BsonUtil.translateJsonToBson(element);

---

JSON Writer Settings

Hytale používá specifické nastavení pro JSON výstup:

// Z BsonUtil.java
public static final JsonWriterSettings SETTINGS = JsonWriterSettings.builder()
.outputMode(JsonMode.STRICT)
.indent(true)
.newLineCharacters("\n")
.int64Converter((value, writer) -> writer.writeNumber(Long.toString(value)))
.build();

Výsledný JSON:

  • Formátovaný s odsazením

  • Unix line endings (\n)

  • Long hodnoty jako čísla (ne jako {"$numberLong": "123"})

---

Cesty k Souborům

Universe Path

// Hlavní adresář serveru
Path universePath = Universe.get().getPath();

// Typické cesty
Path configPath = universePath.resolve("plugins/myplugin/config.json");
Path dataPath = universePath.resolve("data/players.json");
Path warpsPath = universePath.resolve("warps.json");

Plugin Data Path

// V pluginu - vlastní datový adresář
Path pluginPath = getDataFolder();
Path configPath = pluginPath.resolve("config.json");

Vytvoření Adresářů

Path path = Universe.get().getPath().resolve("plugins/myplugin/data/file.json");
Path parent = path.getParent();

// Vytvoř všechny parent adresáře
if (!Files.exists(parent)) {
Files.createDirectories(parent);
}

---

Error Handling

Bezpečné Čtení

public void loadConfig() {
Path path = Universe.get().getPath().resolve("config.json");

try {
BsonDocument document = BsonUtil.readDocument(path).join();

if (document == null) {
getLogger().atWarning().log("Config not found, using defaults");
createDefaultConfig();
return;
}

// Zpracování...

} catch (Exception e) {
getLogger().at(Level.SEVERE).withCause(e).log("Failed to load config:");
createDefaultConfig();
}
}

Validace

public void loadConfig(BsonDocument doc) {
// Kontrola povinných klíčů
if (!doc.containsKey("version")) {
throw new IllegalArgumentException("Missing 'version' in config");
}

// Kontrola typu
if (!doc.get("port").isInt32()) {
throw new IllegalArgumentException("'port' must be an integer");
}

// Validace hodnoty
int port = doc.getInt32("port").getValue();
if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Invalid port: " + port);
}
}

---

Příklad: Kompletní Config Manager

public class ConfigManager {
private final Path configPath;
private BsonDocument config;

public ConfigManager(Path dataFolder) {
this.configPath = dataFolder.resolve("config.json");
}

public CompletableFuture load() {
return BsonUtil.readDocument(configPath).thenAccept(doc -> {
if (doc != null) {
this.config = doc;
} else {
this.config = createDefault();
save();
}
});
}

public CompletableFuture save() {
return BsonUtil.writeDocument(configPath, config);
}

private BsonDocument createDefault() {
BsonDocument doc = new BsonDocument();
doc.put("version", new BsonInt32(1));
doc.put("debug", new BsonBoolean(false));
doc.put("maxWarps", new BsonInt32(100));
return doc;
}

public int getMaxWarps() {
return config.getInt32("maxWarps").getValue();
}

public void setMaxWarps(int value) {
config.put("maxWarps", new BsonInt32(value));
}

public boolean isDebug() {
return config.getBoolean("debug").getValue();
}
}

---

Shrnutí

| Operace | Metoda |
|---------|--------|
| Async čtení | BsonUtil.readDocument(path) |
| Sync čtení | BsonUtil.readDocumentNow(path) |
| Async zápis | BsonUtil.writeDocument(path, doc) |
| Sync zápis | BsonUtil.writeSync(path, codec, value, logger) |
| BSON → JSON | BsonUtil.toJson(doc) |
| JSON → BSON | BsonDocument.parse(json) |
| BSON → bytes | BsonUtil.writeToBytes(doc) |
| bytes → BSON | BsonUtil.readFromBytes(bytes) |

Last updated: 20. ledna 2026