Codecs
Dokumentace k Codec systému pro serializaci/deserializaci objektů v Hytale.
---
Přehled
Codecs jsou systém pro konverzi Java objektů do/z BSON formátu. Hytale poskytuje předdefinované codecy pro základní typy a BuilderCodec pro komplexní objekty.
---
Základní Codecy
Import
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.ExtraInfo;
import com.hypixel.hytale.codec.EmptyExtraInfo;
Předdefinované Codecy
// Z Codec.java - předdefinované codecy
Codec.STRING // String
Codec.BOOLEAN // boolean
Codec.INTEGER // int
Codec.LONG // long
Codec.FLOAT // float
Codec.DOUBLE // double
Codec.BYTE // byte
Codec.SHORT // short// Arrays
Codec.STRING_ARRAY // String[]
Codec.INT_ARRAY // int[]
Codec.LONG_ARRAY // long[]
Codec.FLOAT_ARRAY // float[]
Codec.DOUBLE_ARRAY // double[]
Codec.BYTE_ARRAY // byte[]
// Speciální typy
Codec.PATH // java.nio.file.Path
Codec.INSTANT // java.time.Instant
Codec.DURATION // java.time.Duration
Codec.DURATION_SECONDS // Duration v sekundách (double)
Codec.LOG_LEVEL // java.util.logging.Level
Codec.UUID_BINARY // UUID jako binary
Codec.UUID_STRING // UUID jako string
---
Použití Codeců
Enkódování (Java → BSON)
// Základní typy
BsonValue bsonString = Codec.STRING.encode("Hello", EmptyExtraInfo.EMPTY);
BsonValue bsonInt = Codec.INTEGER.encode(42, EmptyExtraInfo.EMPTY);
BsonValue bsonBool = Codec.BOOLEAN.encode(true, EmptyExtraInfo.EMPTY);// S deprecated metodou (bez ExtraInfo)
BsonValue bson = Codec.STRING.encode("Hello"); // Deprecated ale funkční
Dekódování (BSON → Java)
// Základní typy
String str = Codec.STRING.decode(bsonValue, EmptyExtraInfo.EMPTY);
int num = Codec.INTEGER.decode(bsonValue, EmptyExtraInfo.EMPTY);
boolean bool = Codec.BOOLEAN.decode(bsonValue, EmptyExtraInfo.EMPTY);
---
ArrayCodec
Pro serializaci polí:
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;// Vytvoření array codeku
ArrayCodec stringArrayCodec = new ArrayCodec<>(Codec.STRING, String[]::new);
// Enkódování
String[] array = {"one", "two", "three"};
BsonArray bsonArray = stringArrayCodec.encode(array, EmptyExtraInfo.EMPTY);
// Dekódování
String[] decoded = stringArrayCodec.decode(bsonArray, EmptyExtraInfo.EMPTY);
---
MapCodec
Pro serializaci map:
import com.hypixel.hytale.codec.codecs.map.MapCodec;// Vytvoření map codeku
MapCodec stringMapCodec = new MapCodec<>(Codec.STRING, HashMap::new);
// Enkódování
Map map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
BsonDocument bsonDoc = stringMapCodec.encode(map, EmptyExtraInfo.EMPTY);
// Dekódování
Map decoded = stringMapCodec.decode(bsonDoc, EmptyExtraInfo.EMPTY);
---
EnumCodec
Pro serializaci enumů:
import com.hypixel.hytale.codec.codecs.EnumCodec;public enum GameMode {
SURVIVAL, CREATIVE, ADVENTURE
}
// Vytvoření enum codeku
EnumCodec gameModeCodec = new EnumCodec<>(GameMode.class);
// Enkódování (jako string)
BsonValue bson = gameModeCodec.encode(GameMode.SURVIVAL, EmptyExtraInfo.EMPTY);
// Výsledek: BsonString("SURVIVAL")
// Dekódování
GameMode mode = gameModeCodec.decode(new BsonString("CREATIVE"), EmptyExtraInfo.EMPTY);
---
FunctionCodec
Pro konverzi mezi typy:
import com.hypixel.hytale.codec.function.FunctionCodec;// Příklad: Path jako string
FunctionCodec pathCodec = new FunctionCodec<>(
Codec.STRING, // Podkladový codec
Paths::get, // String → Path
Path::toString // Path → String
);
// Příklad: UUID jako string
FunctionCodec uuidCodec = new FunctionCodec<>(
Codec.STRING,
UUID::fromString,
UUID::toString
);
// Příklad: Duration v sekundách
FunctionCodec durationCodec = new FunctionCodec<>(
Codec.DOUBLE,
v -> Duration.ofNanos((long)(v * TimeUnit.SECONDS.toNanos(1L))),
v -> (double)v.toNanos() / (double)TimeUnit.SECONDS.toNanos(1L)
);
---
BuilderCodec
Pro komplexní objekty s mnoha poli.
Základní Struktura
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.KeyedCodec;public class PlayerData {
private String name;
private int level;
private boolean premium;
// Výchozí konstruktor je povinný
public PlayerData() {}
// Gettery a settery
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getLevel() { return level; }
public void setLevel(int level) { this.level = level; }
public boolean isPremium() { return premium; }
public void setPremium(boolean premium) { this.premium = premium; }
// Codec definice
public static final BuilderCodec CODEC;
static {
BuilderCodec.Builder builder = BuilderCodec.builder(
PlayerData.class,
PlayerData::new
);
builder.append(
new KeyedCodec<>("Name", Codec.STRING),
PlayerData::setName,
PlayerData::getName
).add();
builder.append(
new KeyedCodec<>("Level", Codec.INTEGER),
PlayerData::setLevel,
PlayerData::getLevel
).add();
builder.append(
new KeyedCodec<>("Premium", Codec.BOOLEAN),
PlayerData::setPremium,
PlayerData::isPremium
).add();
CODEC = builder.build();
}
}
Použití BuilderCodec
// Vytvoření objektu
PlayerData player = new PlayerData();
player.setName("Steve");
player.setLevel(42);
player.setPremium(true);// Enkódování
BsonValue bson = PlayerData.CODEC.encode(player, EmptyExtraInfo.EMPTY);
// Výsledek: {"Name": "Steve", "Level": 42, "Premium": true}
// Dekódování
BsonDocument doc = BsonDocument.parse("{\"Name\": \"Alex\", \"Level\": 10, \"Premium\": false}");
PlayerData decoded = PlayerData.CODEC.decode(doc, EmptyExtraInfo.EMPTY);
Vnořené Objekty
public class Transform {
private double x, y, z;
private float yaw, pitch; public static final BuilderCodec CODEC;
static {
BuilderCodec.Builder builder = BuilderCodec.builder(
Transform.class, Transform::new
);
builder.append(new KeyedCodec<>("X", Codec.DOUBLE), Transform::setX, Transform::getX).add();
builder.append(new KeyedCodec<>("Y", Codec.DOUBLE), Transform::setY, Transform::getY).add();
builder.append(new KeyedCodec<>("Z", Codec.DOUBLE), Transform::setZ, Transform::getZ).add();
builder.append(new KeyedCodec<>("Yaw", Codec.FLOAT), Transform::setYaw, Transform::getYaw).add();
builder.append(new KeyedCodec<>("Pitch", Codec.FLOAT), Transform::setPitch, Transform::getPitch).add();
CODEC = builder.build();
}
}
public class Warp {
private String id;
private String world;
private Transform transform;
public static final BuilderCodec CODEC;
static {
BuilderCodec.Builder builder = BuilderCodec.builder(
Warp.class, Warp::new
);
builder.append(new KeyedCodec<>("Id", Codec.STRING), Warp::setId, Warp::getId).add();
builder.append(new KeyedCodec<>("World", Codec.STRING), Warp::setWorld, Warp::getWorld).add();
// Vnořený objekt používá svůj vlastní codec
builder.append(new KeyedCodec<>("Transform", Transform.CODEC), Warp::setTransform, Warp::getTransform).add();
CODEC = builder.build();
}
// Array codec pro seznamy warpů
public static final ArrayCodec ARRAY_CODEC = new ArrayCodec<>(CODEC, Warp[]::new);
}
---
KeyedCodec
Wrapper pro pojmenování klíče v JSON:
// Vytvoření keyed codeku
KeyedCodec nameCodec = new KeyedCodec<>("Name", Codec.STRING);
KeyedCodec levelCodec = new KeyedCodec<>("Level", Codec.INTEGER);
KeyedCodec transformCodec = new KeyedCodec<>("Transform", Transform.CODEC);// Klíč v JSON bude odpovídat prvnímu argumentu
// "Name": "value"
// "Level": 42
// "Transform": {...}
---
Validace
ValidatableCodec
import com.hypixel.hytale.codec.validation.ValidatableCodec;
import com.hypixel.hytale.codec.validation.ValidationResults;// BuilderCodec implementuje ValidatableCodec
// Můžeš přidat validaci:
builder.validator((object, results) -> {
if (object.getLevel() < 0) {
results.addError("Level cannot be negative");
}
if (object.getName() == null || object.getName().isEmpty()) {
results.addError("Name is required");
}
});
---
ExtraInfo
Kontext pro kódování/dekódování:
// Prázdný kontext (nejčastější použití)
ExtraInfo info = EmptyExtraInfo.EMPTY;// Thread-local kontext
ExtraInfo info = ExtraInfo.THREAD_LOCAL.get();
// S verzí
VersionedExtraInfo info = new VersionedExtraInfo(1);
---
Vlastní Codec
Implementace Codec Interface
public class Vector3dCodec implements Codec { @Override
public Vector3d decode(BsonValue bsonValue, ExtraInfo extraInfo) {
if (bsonValue == null || bsonValue.isNull()) {
return null;
}
BsonDocument doc = bsonValue.asDocument();
double x = doc.getDouble("x").getValue();
double y = doc.getDouble("y").getValue();
double z = doc.getDouble("z").getValue();
return new Vector3d(x, y, z);
}
@Override
public BsonValue encode(Vector3d vector, ExtraInfo extraInfo) {
if (vector == null) {
return BsonNull.VALUE;
}
BsonDocument doc = new BsonDocument();
doc.put("x", new BsonDouble(vector.x));
doc.put("y", new BsonDouble(vector.y));
doc.put("z", new BsonDouble(vector.z));
return doc;
}
}
// Použití
public static final Codec VECTOR3D_CODEC = new Vector3dCodec();
---
Příklad: Kompletní Warp Systém
public class Warp {
private String id;
private String world;
private Vector3d position;
private Vector3f rotation; public Warp() {}
// Gettery a settery...
// Codecy
public static final BuilderCodec CODEC;
public static final ArrayCodec ARRAY_CODEC;
static {
BuilderCodec.Builder builder = BuilderCodec.builder(Warp.class, Warp::new);
builder.append(new KeyedCodec<>("Id", Codec.STRING), Warp::setId, Warp::getId).add();
builder.append(new KeyedCodec<>("World", Codec.STRING), Warp::setWorld, Warp::getWorld).add();
builder.append(new KeyedCodec<>("Position", VECTOR3D_CODEC), Warp::setPosition, Warp::getPosition).add();
builder.append(new KeyedCodec<>("Rotation", VECTOR3F_CODEC), Warp::setRotation, Warp::getRotation).add();
CODEC = builder.build();
ARRAY_CODEC = new ArrayCodec<>(CODEC, Warp[]::new);
}
}
// Použití při ukládání/načítání
public class WarpManager {
public void saveWarps(List warps) {
Warp[] array = warps.toArray(Warp[]::new);
BsonArray bsonArray = Warp.ARRAY_CODEC.encode(array, EmptyExtraInfo.EMPTY);
BsonDocument document = new BsonDocument("Warps", bsonArray);
BsonUtil.writeDocument(warpsPath, document).join();
}
public List loadWarps() {
BsonDocument document = BsonUtil.readDocument(warpsPath).join();
if (document == null) return new ArrayList<>();
BsonArray bsonArray = document.getArray("Warps");
Warp[] array = Warp.ARRAY_CODEC.decode(bsonArray, EmptyExtraInfo.EMPTY);
return new ArrayList<>(Arrays.asList(array));
}
}
---
Shrnutí
| Typ | Codec | Příklad |
|-----|-------|---------|
| String | Codec.STRING | "text" |
| int | Codec.INTEGER | 42 |
| long | Codec.LONG | 1000 |
| double | Codec.DOUBLE | 3.14 |
| boolean | Codec.BOOLEAN | true |
| Array | ArrayCodec | [...] |
| Map | MapCodec | {...} |
| Enum | EnumCodec | "VALUE" |
| Object | BuilderCodec | {...} |
| Konverze | FunctionCodec | různé |