Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 84 additions & 81 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
<dependency>
<groupId>dev.spexx</groupId>
<artifactId>ConfigurationAPI</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
</dependency>
```

### 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<Path, YamlConfig>)
├── 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