Skip to content

4. Persistent Data Storage (DataContainer)

zawarka03 edited this page Jul 29, 2026 · 2 revisions

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.

4.1. DataKey and DataType

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 type T. It is constructed using an Identifier and a DataType. The recommended practice is to define DataKeys as constants within your BlockDefinition class using the protected dataKey(path, type) method. This ensures the key's identifier is properly namespaced under the block's identifier.
  • DataType<T>: Defines how data of type T is 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.

4.2. Accessing and Modifying Data

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)

4.3. Registering Custom Data Types

For complex data structures, you can define and register custom DataTypes. This requires implementing the DataType interface and registering it during the MonolithRegisterDataTypesEvent.

Example: Custom Configuration Object

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]));
    }
}

Example: Custom DataType Implementation

// 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);
    }
}

Example: Registering the Custom DataType

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

Clone this wiki locally