diff --git a/README.md b/README.md index a6fc886..299d652 100644 --- a/README.md +++ b/README.md @@ -1,129 +1,132 @@ -# ConfigurationAPI +### ConfigurationAPI -A lightweight, high-performance YAML configuration API with automatic file watching, diff tracking, and atomic reloads for Paper/Spigot plugins. - ---- +A lightweight, high-performance YAML configuration API for Paper/Spigot plugins, featuring automatic file watching, atomic reloads, and a clean event-driven design. ### Features - Automatic config reload on file changes (WatchService-based) -- Atomic, thread-safe configuration swapping -- Line-level diff tracking (changed / added / removed) -- Change summary abstraction (`ConfigChangeSummary`) -- Robust against partial writes and filesystem edge cases -- Optimized (no regex, O(n) diff, minimal allocations) -- Clean integration with Bukkit/Paper event system +- 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 ### Why ConfigurationAPI? - - Eliminates manual reload logic -- Provides insight into config changes (diffs) +- Guarantees consistent configuration state - Prevents unsafe concurrent access -- Designed for high-performance plugin environments - -## Installation - -### Maven +- Centralized architecture (single source of truth) +- Minimal overhead and easy integration +### Installation +#### Maven ```xml dev.spexx ConfigurationAPI - 1.0.4 + 1.0.5 ``` -### 1. Create ConfigManager +### Usage + +#### 1. Create ConfigManager ```java ConfigManager configManager = new ConfigManager(plugin); ``` - -### 2. Load a configuration file +#### 2. Load a configuration file ```java File file = new File(plugin.getDataFolder(), "config.yml"); YamlConfig config = configManager.getOrLoad(file); ``` - -### 2.1 Load default resource (eg.: config.yml) +#### 2.1 Load default resource (e.g. config.yml) ```java YamlConfig config = configManager.getOrLoadResource("config.yml"); -# Copies from JAR if missing -# Automatically registers watcher +# Copies file from JAR if it does not exist +# Automatically registers it with the watcher + ``` +#### 3. Access configuration +```java +YamlConfig config = configManager.get(file); +String value = config.config().getString("path.to.value"); -## 3. Automatic Reloads +# Always returns the latest snapshot +# Safe for concurrent access +``` + +#### 4. Automatic Reloads Configuration files are automatically monitored. When a file changes: -- It is reloaded atomically +- The file is reloaded +- A new `YamlConfig` instance is created +- The old instance is atomically replaced - A `ConfigReloadedEvent` is fired -- Diff + summary are provided -### 4. Listening to Reload Events + +#### 5. Listening to Reload Events ```java @EventHandler public void onReload(ConfigReloadedEvent event) { - ConfigChangeSummary summary = event.getSummary(); - plugin.getLogger().info(() -> - "[Config] Reloaded: " + summary + "[Config] Reloaded: " + + event.getNewConfig().file().getName() + + " (" + event.getReloadTimeMs() + "ms)" ); - for (ConfigLineDifference diff : event.getDiffs()) { - plugin.getLogger().info(() -> - "[ConfigWatcher] file=" + - event.getConfig().file().getName() + - " line=" + diff.getLineNumber() + - " delta=" + diff.getCharDelta() - ); - } + plugin.getLogger().info(() -> + "Checksum: " + + event.getOldChecksum() + + " -> " + + event.getNewChecksum() + ); } ``` -### 5. Change Summary -```java -ConfigChangeSummary summary = event.getSummary(); +#### 6. Event Data +`ConfigReloadedEvent` provides: +- `getOldConfig()` → previous snapshot +- `getNewConfig()` → updated snapshot +- `getOldChecksum()` → previous file hash +- `getNewChecksum()` → new file hash +- `getReloadTimeMs()` → reload duration -summary.changedLines(); -summary.addedLines(); -summary.removedLines(); -summary.getTotalChanges(); -summary.hasChanges(); -``` - -### 6. Line Differences -```java -for (ConfigLineDifference diff : event.getDiffs()) { - - int line = diff.getLineNumber(); - String oldLine = diff.getOldLine(); - String newLine = diff.getNewLine(); - int delta = diff.getCharDelta(); - - if (diff.isOnlyWhitespaceChange()) { - // ignore formatting changes if needed - } -} -``` - -### 7. Architecture Overview +#### 7. Architecture Overview ```yml -ConfigManager - ├── YamlConfig (immutable snapshot) - ├── YamlConfigWatcher (file watcher) - │ └── Diff engine (O(n)) - └── Events (ConfigReloadedEvent) +ConfigManager (API layer) + ├── delegates to GlobalConfigWatcher + └── provides access to configs + +GlobalConfigWatcher (core) + ├── owns config state (Map) + ├── owns checksums + ├── handles file watching (WatchService) + ├── performs reloads + └── fires events + +YamlConfig + └── immutable snapshot of configuration ``` -### 8. Thread Safety -- `YamlConfig` is immutable -- Reloads are atomic -- Watcher runs async, events fire sync (main thread) -- Safe for concurrent reads - -### 9. Best Practices -- Always fetch config via ConfigManager -- Avoid storing long-lived YamlConfig references -- Use events for reactive updates +#### 8. Threading Model +- Watcher runs on a dedicated async thread +- File changes are processed asynchronously +- Events are dispatched synchronously on the main thread +- `YamlConfig` instances are immutable + +#### 9. Best Practices +- Always access configs via `ConfigManager` +- Do not store `YamlConfig` instances long-term +- Use `ConfigReloadedEvent` for reactive updates +- Avoid modifying `FileConfiguration` directly + +#### 10. Guarantees +- No duplicated configuration state +- Atomic updates (no partial reads) +- No reload if file content is unchanged +- Safe under concurrent access