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