A lightweight, high-performance YAML configuration API for Paper/Spigot plugins, featuring automatic file watching, atomic reloads, and a clean event-driven design.
- Automatic config reload on file changes (WatchService-based)
- Single global watcher (no per-file overhead)
- Atomic, thread-safe configuration replacement
- SHA-256 checksum-based change detection
- Debounce protection against duplicate reloads
- Synchronous reload events (ConfigReloadedEvent)
- Clean, minimal, and predictable API
- Designed for high-performance plugin environments
- Eliminates manual reload logic
- Guarantees consistent configuration state
- Prevents unsafe concurrent access
- Centralized architecture (single source of truth)
- Minimal overhead and easy integration
<dependency>
<groupId>dev.spexx</groupId>
<artifactId>ConfigurationAPI</artifactId>
<version>1.2.0</version>
</dependency>ConfigManager configManager = new ConfigManager(plugin);File file = new File(plugin.getDataFolder(), "config.yml");
YamlConfig config = configManager.getOrLoad(file);YamlConfig config = configManager.getOrLoadResource("config.yml");
// Copies file from JAR if it does not exist
// Automatically registers it with the watcherYamlConfig config = configManager.get(file);
String value = config.config().getString("path.to.value");
// Always returns the latest snapshot
// Safe for concurrent accessConfiguration files are automatically monitored.
When a file changes:
- The file is reloaded
- A new
YamlConfiginstance is created - The old instance is atomically replaced
- A
ConfigReloadedEventis fired
@EventHandler
public void onReload(ConfigReloadedEvent event) {
plugin.getLogger().info(() ->
"[Config] Reloaded: " +
event.getNewConfig().file().getName() +
" (" + event.getReloadTimeMs() + "ms)"
);
plugin.getLogger().info(() ->
"Checksum: " +
event.getOldChecksum() +
" -> " +
event.getNewChecksum()
);
}ConfigReloadedEvent provides:
getOldConfig()→ previous snapshotgetNewConfig()→ updated snapshotgetOldChecksum()→ previous file hashgetNewChecksum()→ new file hashgetReloadTimeMs()→ reload duration
ConfigManager (API layer)
├── delegates to GlobalConfigWatcher
└── provides access to configs
GlobalConfigWatcher (core)
├── owns config state (Map<Path, YamlConfig>)
├── owns checksums
├── handles file watching (WatchService)
├── performs reloads
└── fires events
YamlConfig
└── immutable snapshot of configuration- Watcher runs on a dedicated async thread
- File changes are processed asynchronously
- Events are dispatched synchronously on the main thread
YamlConfiginstances are immutable
- Always access configs via
ConfigManager - Do not store
YamlConfiginstances long-term - Use
ConfigReloadedEventfor reactive updates - Avoid modifying
FileConfigurationdirectly
- No duplicated configuration state
- Atomic updates (no partial reads)
- No reload if file content is unchanged
- Safe under concurrent access