Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
Expand Down
19 changes: 13 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ To keep the project clean and maintainable, please follow these principles:
- Avoid unnecessary complexity or over-engineering

#### Development Setup

Requirements:

- Java 21+
- Maven 3+

Expand All @@ -26,46 +28,51 @@ mvn clean package
```

#### Workflow

1. Fork the repository (use a personal fork, not an organization)
2. Create a new branch:
3. `git checkout -b feature/my-change`
4. Make your changes
5. Commit using clear messages: `git commit -m "feat: improve config loading performance"`
6. Push and open a Pull Request


#### Code Style

- Follow standard Java conventions (IDE defaults are fine)
- Keep methods focused and small
- Use meaningful names
- Avoid var where clarity suffers
- Add Javadoc for all public APIs

#### Documentation

- All public classes and methods must include Javadoc
- Javadoc must be doclint-compliant (no invalid HTML)
- Include `@since` where appropriate


#### Testing

- Ensure your changes do not break existing behavior
- If applicable, test:
- config reloads
- invalid YAML handling
- watcher behavior
- config reloads
- invalid YAML handling
- watcher behavior

#### Pull Request Notes

> Keep PRs focused and minimal

- Explain why the change is needed
- Large changes should be discussed in an issue first


#### What Not to Do

- Do not introduce breaking changes without discussion
- Do not add unnecessary dependencies
- Do not change public API behavior without justification

#### Questions

If you're unsure about something, open an issue first — happy to help.

---
Expand Down
201 changes: 93 additions & 108 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,132 +3,117 @@

## ConfigurationAPI

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)
- 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
- Guarantees consistent configuration state
- Prevents unsafe concurrent access
- Centralized architecture (single source of truth)
- Minimal overhead and easy integration

### Installation
#### Maven
This is a simple yet somewhat robust plugin I wrote in my free time.
It allows developers to not worry about config changes and implementing /reload commands in their java plugins.
It is a Paper based API, well, a Paper based plugin. It will only work on Paper servers. Don't try to install it on spigot
servers, it won't work. I might make a fork of it in the near future, to also support spigot.
<br><br>
This plugin automatically tracks any configs you configure it to track, and reloads them, when
they were manually edited (without code), and you can also write to them by code, of course. So it works
both ways.

## Features

- Simple API
- File watching (WatchService)
- checksum validation (no unnecessary reloads)
- Debounce protection
- Single watcher (no per-file threads)
- A cool sync event! (`ConfigReloadEvent`)

## Installation
Currently, it's only available as a jar. I'll see what I
can do in the near future about that.

```xml
<dependency>
<groupId>dev.spexx</groupId>
<artifactId>ConfigurationAPI</artifactId>
<version>1.2.0</version>
<version>1.3.0</version>
</dependency>
```

### Usage
#### Full sample (with comments)

#### 1. Create ConfigManager
```java
ConfigManager configManager = new ConfigManager(plugin);
import dev.spexx.configurationAPI.manager.ConfigManager;

// ConfigManager takes a plugin instance, should only
// be initialized once!
ConfigManager configManager = new ConfigManager(this);

// Sample 1 - registering files from the plugin jar itself
// Which means you should NOT use saveDefaultConfig() / saveConfig()
// anywhere in your plugin.

// This is where you want the config from the plugin resources to be saved.
File myPluginConfigDestination = new File(getDataFolder(), "config.yml");

// 2nd parameter, "config.yml" is the path inside the jar. Should really leave that as it is, only
// change your file name. 3rd parameter is your plugin instance - so we know from which plugin we're
// pulling the config.
configManager.registerFromJar(myPluginConfigDestination, "config.yml", this);

// I recommend putting all of this in your onEnable, at the end don't forget to
// actually start the watch service. Note that you can also dynamically create files during
// runtime, you don't have to do everything in onEnable.
manager.start();

// Sample 2 - registering custom yaml files, except not from plugin jar
// This is fairly simple!
YamlConfig myCustomConfig = manager.register(
new File(getDataFolder(), "data.yml")
);

// What you're doing here is creating a file, in your plugin folder - getDataFolder(),
// and you're naming it 'data.yml', that's about it.
// You can write to it:
myCustomConfig.get().set("hello", "world");

// Read from it (get() - returns the latest internally cached config,
// basically, the latest config that matches the file on your disk)
// with get(), you get raw YamlConfiguration access, so you're able to do
// anything. If you do change anything in the file don't forget to
// save it in the end: myCustomConfig.save(), api will handle the rest.
@Nullable String myValue = myCustomConfig.get().getString("something.here");

// ---
// If you find this confusing, there is javadoc at every method / public variable. So you won't be lost!
```
#### 2. Load a configuration file
```java
File file = new File(plugin.getDataFolder(), "config.yml");

YamlConfig config = configManager.getOrLoad(file);
```
#### 2.1 Load default resource (e.g. config.yml)
```java
YamlConfig config = configManager.getOrLoadResource("config.yml");
#### Listen for reload (If you need to)

// 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");

// Always returns the latest snapshot
// Safe for concurrent access
```
import org.jetbrains.annotations.NotNull;

#### 4. Automatic Reloads
Configuration files are automatically monitored.

When a file changes:
- The file is reloaded
- A new `YamlConfig` instance is created
- The old instance is atomically replaced
- A `ConfigReloadedEvent` is fired


#### 5. Listening to Reload Events
```java
@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()
);
public void onReload(@NotNull ConfigReloadEvent event) {
getLogger().info("Reloaded: " + event.getConfigName());
}
```

#### 6. Event Data
`ConfigReloadedEvent` provides:
- `getOldConfig()` → previous snapshot
- `getNewConfig()` → updated snapshot
- `getOldChecksum()` → previous file hash
- `getNewChecksum()` → new file hash
- `getReloadTimeMs()` → reload duration

#### 7. Architecture Overview
```yml
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
#### What this event provides
```
## ConfigReloadEvent

| Method | Description |
|--------------------|------------------------------------------|
| `getConfigName()` | Full name of the file (e.g. `config.yml`)|
| `getNewConfig()` | Updated configuration (latest state) |
| `getOldChecksum()` | Checksum before reload |
| `getNewChecksum()` | Checksum after reload |
```

#### Event behavior
When file changes:
- Change is detected internally in watcher
- Checksum is computed
- If checksum is different, reload event is fired, and new file is cached
- New file overrides the latest file (if any) in the cache
- If checksum isn't different, nothing happens, as there were no changes in the file
- Event is fired on main thread

---

#### 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
### Thank you for reading!
5 changes: 5 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@ Only the latest version of ConfigurationAPI is actively supported.
Older versions may not receive security updates.

#### Reporting a Vulnerability

If you discover a security vulnerability, please **do not open a public issue**.

Instead, report it privately:

- Contact: spexx@spexx.dev

---

#### What to Report

Please report:

- Remote code execution risks
- File system vulnerabilities
- Unsafe config handling
Expand All @@ -25,12 +28,14 @@ Please report:
---

#### Response Time

- Initial response: within a few days
- Fix timeline: depends on severity

---

#### Responsible Disclosure

Please allow time for a fix before publicly disclosing vulnerabilities.

Thank you for helping keep the project secure.
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>dev.spexx</groupId>
<artifactId>ConfigurationAPI</artifactId>
<version>1.2.0</version>
<version>1.3.0</version>
<packaging>jar</packaging>

<name>ConfigurationAPI</name>
Expand Down
Loading
Loading