-
Notifications
You must be signed in to change notification settings - Fork 0
Advanced Implementations and Custom GUI Generation
Property mutations execute lifecycle broadcasts. Implement Property.Listener and bind it via addListener() to intercept state transitions.
-
onPropertyChanged(): Executes when localized disk state modifies or authoritative network synchronization completes. -
onPropertyInvalidated(): Executes when the parent property detaches from the structural hierarchy or severs an authoritative network connection.
Detachment utilizes removeListener() to drop the active sequence.
The framework provides pre-constructed listeners designed for configurable datapack values. To utilize these implementations, extend the target class located within johnsmith.configoverhauled.api.listener (e.g., IntegerPropertyListener, BooleanPropertyListener), implement the required Codec for deserialization, and execute the super constructor.
The following matrix maps the supported configuration property types to their designated listener classes for datapack injection and synchronization. All listener extensions are located within the johnsmith.configoverhauled.api.listener package.
| Property Type | Target State Type | Required Listener Extension |
|---|---|---|
Boolean |
Boolean |
BooleanPropertyListener |
Integer |
Integer |
IntegerPropertyListener |
Float |
Float |
FloatPropertyListener |
Double |
Double |
DoublePropertyListener |
Long |
Long |
LongPropertyListener |
String |
String |
StringPropertyListener |
RGBColor |
Integer |
RGBColorPropertyListener |
ARGBColor |
Integer |
ARGBColorPropertyListener |
Block |
Block |
BlockPropertyListener |
Blocks |
List<Block> |
BlocksPropertyListener |
Item |
Item |
ItemPropertyListener |
Items |
List<Item> |
ItemsPropertyListener |
Extend the dynamic property registry to support specialized data structures by defining a custom TypeDefinition. Execute registration during initial mod setup. Required parameters include a namespace identifier, a serialization codec, and a functional PropertyFactory to dictate state instantiation.
public static final DynamicPropertyTypeRegistry.TypeDefinition<UUID> UUID_TYPE =
DynamicPropertyTypeRegistry.register(
"examplemod",
"uuid",
UUIDUtil.CODEC,
(name, group, def, min, max, codec)
-> new CustomUUIDProperty(name, group, ConfigScope.LEVEL, null, (UUID) def, true)
);Extend AbstractPropertyListener to inherit built-in state caching and registry resolution. This eliminates the need to manually implement baseline synchronization logic.
public class CustomUUIDPropertyListener extends AbstractPropertyListener<UUID> {
public CustomUUIDPropertyListener(String key, UUID defaultValue) {
super(key, UUID_TYPE, defaultValue);
}
}If the target data type requires value boundary validation, extend BoundedPropertyListener. This enforces upper and lower constraints prior to state application.
public class CustomBoundedIntegerListener extends BoundedPropertyListener<Integer> {
public CustomBoundedIntegerListener(String key, Integer defaultValue, Integer min, Integer max) {
super(key, CUSTOM_INTEGER, defaultValue, min, max);
}
}Refactor the instantiation parameter signature to utilize DynamicPropertyTypeRegistry.TypeDefinition instead of primitive class wrappers.
Runtime structural injection bypasses static field definition. Utilize ConfigManager::getOrCreateDynamicProperty combined with PropertyFactory to instantiate properties from deserialized network or disk definitions. Require a ConfigDescription object to map the absolute structural coordinates for the target injection.
Property<?> dynamicProp = ExampleConfig.MANAGER.getOrCreateDynamicProperty(
description, // ConfigDescription instance defining absolute coordinates
DynamicPropertyTypeRegistry.BOOLEAN, // Type definition
true, // Baseline state
null, // Lower validation boundary
null // Upper validation boundary
);This operation targets datapack injection or authoritative server synchronization where structural configurations are unknown during the primary boot sequence.
Standard execution automates disk synchronization upon property modification. Manual I/O execution isolates synchronization to specific scopes or shifts operations off the main thread.
-
loadScope(ConfigScope targetScope): Forces a synchronous disk read operation strictly for properties bound to the specified scope. -
saveScope(ConfigScope targetScope): Forces a synchronous disk write operation strictly for properties bound to the specified scope. -
saveAll(): Dispatches an asynchronous disk write operation across all configuration scopes. Returns aCompletableFuture<Void>representing the completion state of the I/O execution.
Custom properties (e.g., UUIDProperty) require a custom WidgetFactory registered to the default screen for rendering.
Intercept the screen generation in the ModMenu, Forge, or NeoForge integration before returning it to the game:
return parentScreen -> {
ConfigManager manager = ConfigRegistry.getManager("mymod");
// 1. Create the default screen
ConfigScreen configScreen = ConfigScreenFactory.createDefault(parentScreen, manager);
// 2. Register custom widget factory for the specific property class
configScreen.registerWidget(UUIDProperty.class, (WidgetFactory) UUIDWidget::new);
// 3. Return the modified screen
return (Screen) configScreen;
};Complete widget mapping overhauls or default widget replacements require injecting a custom WidgetRegistry via Dependency Injection.
ConfigScreen configScreen = ConfigScreenFactory.createDefault(parentScreen, manager);
// Inject a completely new registry implementation
configScreen.setWidgetRegistry(new CustomWidgetRegistry());
return (Screen) configScreen;Alternatively, utilize ConfigScreenFactory.createWithRegistry to pass the custom implementation during initialization.
ConfigScreen configScreen = ConfigScreenFactory.createDefault(parentScreen, manager);
WidgetRegistry customRegistry = new CustomWidgetRegistry();
// Construct the screen with the custom registry injected
ConfigScreen configScreen = ConfigScreenFactory.createWithRegistry(parentScreen, manager, customRegistry);
return (Screen) configScreen;To override default interface rendering protocols, inject a custom screen factory prior to platform-specific GUI endpoint registration.
ExampleConfig.MANAGER.setScreenFactory((parent) -> {
return new CustomConfigScreen(parent);
});Active configuration states are mutated via the Property.set(T newValue) accessor. Executing this method instantaneously initiates disk serialization routines. For properties assigned the LEVEL or GLOBAL operational scope, this accessor concurrently dispatches outbound network payloads to synchronize the authoritative server state.
-
Network Saturation: Text input widgets must implement rigid debouncing parameters or restrict state mutation exclusively to definitive confirmation events (e.g., loss of widget focus, terminal 'Enter' keypress). Invoking
Property.set()on continuous character modification sequences triggers excessive disk I/O and immediately saturates network synchronization channels. -
Boundary Validation: Input components must independently validate structural constraints prior to executing state mutation. Properties statically allocated with numerical boundaries (
min,max) expect sanitized inputs. Pushing unvalidated or malformed states into the property accessor disrupts codec translation pipelines and induces local desynchronization from network authority. Filter invalid structural mappings strictly within the custom UI component logic.