refactor(config): complete rewrite of configuration system

BREAKING CHANGE: New section-based configuration format

New Features:
- Type-safe BlockKey sealed interface (Vanilla, Oraxen, ItemsAdder)
- Section-based config: RegionConfig, BlockConfig, ResetConfig, RewardConfig, UIConfig, TeleportConfig
- Human-readable durations: 5m, 30s, 2h, 1d
- YAML inheritance from defaults.yml
- MiniMessage support for action bar and item names
- Immutable config records with validation

Removed:
- Legacy MineConfig (17-field god object)
- ConfigParser, ConfigValidator, ConfigSerializer split classes
- Legacy FillMode enum (replaced with better version)
- Old ConfigLoader with complex parsing logic

New Files:
- data/config/block/BlockKey.java - sealed interface for block types
- data/config/block/BlockConfig.java - type-safe block weights
- data/config/block/FillMode.java - fill strategies
- data/config/region/RegionConfig.java - region management
- data/config/reset/ResetConfig.java - reset behavior with Duration
- data/config/reward/RewardConfig.java - rewards with ItemReward
- data/config/teleport/TeleportConfig.java - teleport on reset
- data/config/ui/UIConfig.java - action bar and messages
- data/config/MineConfig.java - composed from sections
- data/config/ConfigLoader.java - clean loader with defaults inheritance
- defaults.yml - default configuration template

Tests:
- BlockKeyTest.java - serialization/deserialization
- BlockConfigTest.java - weight normalization, validation
- ResetConfigTest.java - duration parsing, defaults
- UIConfigTest.java - timer formatting, action bar
- MineConfigTest.java - builder, defaults, validation
This commit is contained in:
loki5512344 2026-05-31 21:57:10 +02:00
parent f4e9458544
commit f1060f34d4
22 changed files with 2012 additions and 896 deletions

View file

@ -0,0 +1,158 @@
package dev.loki.lomines.data.config.reset;
import java.time.Duration;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* Configuration for mine reset behavior.
*/
public record ResetConfig(
Duration interval,
double percentTrigger,
boolean percentEnabled,
List<String> commands,
String broadcastMessage
) {
public static final Duration DEFAULT_INTERVAL = Duration.ofMinutes(5);
public static final double DEFAULT_PERCENT_TRIGGER = 10.0;
public ResetConfig {
// Normalize and validate interval
if (interval == null || interval.isZero() || interval.isNegative()) {
interval = DEFAULT_INTERVAL;
}
// Ensure interval is reasonable (1 second to 24 hours)
if (interval.getSeconds() < 1) {
interval = Duration.ofSeconds(1);
} else if (interval.getSeconds() > 86400) {
interval = Duration.ofHours(24);
}
// Validate percent trigger
if (percentTrigger < 0 || percentTrigger > 100) {
percentTrigger = DEFAULT_PERCENT_TRIGGER;
}
commands = commands != null ? List.copyOf(commands) : List.of();
broadcastMessage = broadcastMessage != null ? broadcastMessage : "";
}
/**
* Interval in ticks (20 ticks = 1 second).
*/
public long intervalTicks() {
return interval.getSeconds() * 20;
}
/**
* Human-readable interval string.
*/
public String intervalDisplay() {
long seconds = interval.getSeconds();
if (seconds < 60) {
return seconds + "s";
} else if (seconds < 3600) {
return (seconds / 60) + "m";
} else {
return (seconds / 3600) + "h";
}
}
/**
* Whether reset should trigger based on percent mined.
*/
public boolean isPercentTriggerEnabled() {
return percentEnabled && percentTrigger > 0;
}
/**
* Default config: 5 minutes, 10% trigger disabled.
*/
public static ResetConfig defaults() {
return new ResetConfig(
DEFAULT_INTERVAL,
DEFAULT_PERCENT_TRIGGER,
false,
List.of(),
""
);
}
/**
* Builder for fluent construction.
*/
public static Builder builder() {
return new Builder();
}
public static class Builder {
private Duration interval = DEFAULT_INTERVAL;
private double percentTrigger = DEFAULT_PERCENT_TRIGGER;
private boolean percentEnabled = false;
private List<String> commands = List.of();
private String broadcastMessage = "";
public Builder interval(Duration interval) {
this.interval = interval;
return this;
}
public Builder intervalTicks(long ticks) {
this.interval = Duration.ofSeconds(ticks / 20);
return this;
}
public Builder interval(String humanReadable) {
this.interval = parseDuration(humanReadable);
return this;
}
public Builder percentTrigger(double percent) {
this.percentTrigger = percent;
return this;
}
public Builder percentEnabled(boolean enabled) {
this.percentEnabled = enabled;
return this;
}
public Builder commands(List<String> commands) {
this.commands = commands;
return this;
}
public Builder broadcastMessage(String message) {
this.broadcastMessage = message;
return this;
}
public ResetConfig build() {
return new ResetConfig(interval, percentTrigger, percentEnabled, commands, broadcastMessage);
}
private static Duration parseDuration(String s) {
s = s.trim().toLowerCase();
try {
if (s.endsWith("s")) {
return Duration.ofSeconds(Long.parseLong(s.substring(0, s.length() - 1)));
} else if (s.endsWith("m")) {
return Duration.ofMinutes(Long.parseLong(s.substring(0, s.length() - 1)));
} else if (s.endsWith("h")) {
return Duration.ofHours(Long.parseLong(s.substring(0, s.length() - 1)));
} else if (s.endsWith("d")) {
return Duration.ofDays(Long.parseLong(s.substring(0, s.length() - 1)));
} else {
// Assume seconds if no suffix
return Duration.ofSeconds(Long.parseLong(s));
}
} catch (NumberFormatException e) {
return DEFAULT_INTERVAL;
}
}
}
}