-
Notifications
You must be signed in to change notification settings - Fork 0
4. Persistent Data Storage (DataContainer)
The DataContainer is a powerful feature that allows you to attach arbitrary, persistent data to individual PlacedBlock instances. This data is automatically saved to the SQLite database and loaded when the server restarts.
Data is managed using a key-value system, where keys are strongly typed.
-
DataKey<T>: Represents a unique identifier for a specific piece of data of typeT. It is constructed using anIdentifierand aDataType. The recommended practice is to defineDataKeys as constants within yourBlockDefinitionclass using the protecteddataKey(path, type)method. This ensures the key's identifier is properly namespaced under the block's identifier. -
DataType<T>: Defines how data of typeTis serialized to a String for database storage and deserialized back into an object.
Monolith API provides built-in DataTypes for common primitives: IntDataType, StringDataType, UUIDDataType and etc.
Data is accessed through the data property of a PlacedBlock.
// Java Example
PlacedBlock block = ...;
DataKey<Integer> energyKey = ...;
// Setting data
block.getData().set(energyKey, 500);
// Getting data
Integer energy = block.getData().get(energyKey);
// Getting data with a default value
int safeEnergy = block.getData().getOrDefault(energyKey, 0);
// Checking if data exists
boolean hasEnergy = block.getData().contains(energyKey);
// Removing data
block.getData().remove(energyKey);// Kotlin Example
val block: PlacedBlock = ...
val energyKey: DataKey<Int> = ...
// Setting data
block.data[energyKey] = 500
// Getting data
val energy: Int? = block.data[energyKey]
// Getting data with a default value
val safeEnergy: Int = block.data.getOrDefault(energyKey, 0)
// Checking if data exists
val hasEnergy = energyKey in block.data
// Removing data
block.data.remove(energyKey)For complex data structures, you can define and register custom DataTypes. This requires implementing the DataType interface and registering it during the MonolithRegisterDataTypesEvent.
Suppose you have a complex configuration object for a machine block.
// Java Example: Custom Object
public class MachineConfig {
public final int speed;
public final boolean autoEject;
public MachineConfig(int speed, boolean autoEject) {
this.speed = speed;
this.autoEject = autoEject;
}
// Serialization logic (e.g., to JSON or a custom format)
public String serialize() {
return speed + ";" + autoEject;
}
// Deserialization logic
public static MachineConfig deserialize(String data) {
String[] parts = data.split(";");
return new MachineConfig(Integer.parseInt(parts[0]), Boolean.parseBoolean(parts[1]));
}
}// Java Example: DataType Implementation
import io.github.zawarka03.monolithAPI.api.storage.data.DataType;
import io.github.zawarka03.monolithAPI.identifier.Identifier;
import org.jetbrains.annotations.NotNull;
public class MachineConfigDataType implements DataType<MachineConfig> {
public static final MachineConfigDataType INSTANCE = new MachineConfigDataType();
private final Identifier identifier = new Identifier("industrial", "machine_config");
@Override
public @NotNull Identifier getIdentifier() {
return identifier;
}
@Override
public @NotNull String serialize(@NotNull MachineConfig value) {
return value.serialize();
}
@Override
public @NotNull MachineConfig deserialize(@NotNull String value) {
return MachineConfig.deserialize(value);
}
}Custom data types must be registered before block definitions, during the MonolithRegisterDataTypesEvent.
// Java Example: Registration
import io.github.zawarka03.monolithAPI.api.event.MonolithEventHandler;
import io.github.zawarka03.monolithAPI.api.event.events.MonolithRegisterDataTypesEvent;
import io.github.zawarka03.monolithAPI.api.event.listener.MonolithListener;
public class DataTypeRegistrationListener implements MonolithListener {
@MonolithEventHandler
public void onRegisterDataTypes(MonolithRegisterDataTypesEvent event) {
event.registry().register(MachineConfigDataType.INSTANCE);
}
}