Skip to content

Commit f96b74f

Browse files
authored
Add Nexus deployment workflow, Maven publishing, and utilities (#3)
* Add Nexus deploy workflow and Maven publishing Add a GitHub Actions workflow to deploy artifacts to Negative Games Nexus for release and snapshot branches. The workflow sets up JDK 21 and Gradle, injects Nexus credentials into ~/.gradle/gradle.properties from secrets, adjusts module apiVersion for release/snapshot branches, and runs ./gradlew publish with an isRelease flag. Update root build.gradle.kts to apply maven-publish and configure a nexus repository that selects snapshots or releases based on the isRelease property and reads credentials from gradle.properties. Add maven-publish and shadow publishing configuration to common and paper modules: set module ids, domain, apiVersion, Java 21 toolchain, disable the plain jar, configure shadowJar as the published artifact, and include POM metadata (license, developer, url). * Update deploy workflow for Kotlin DSL builds Switch sed targets from build.gradle to build.gradle.kts and update the regexes to match Kotlin DSL variable declarations (var apiVersion = "...") for both release and snapshot branches. This ensures version bump/removal works for build.gradle.kts files. Also add a missing newline at end of file. * Remove disabling of tasks.jar Delete the tasks.jar { enabled = false } block from common/build.gradle.kts so the default jar task is no longer disabled. This restores normal jar packaging behavior while keeping the shadowJar configuration (archiveBaseName and archiveVersion) intact. * Add README and PaperCommand; update registry (#2) Add project README documenting modules, usage, and APIs. Introduce PaperCommand interface (CloudCommand specialized for CommandSourceStack) and update PaperCommandRegistry to discover/register PaperCommand beans (remove unused CloudCommand import). This aligns command discovery with the Paper-specific command type and adds library documentation. * Add IntList utility to parse integer ranges Introduce IntList with a static parse(List<String>) method that converts strings into a List<Integer>. Supports single integers and inclusive ranges (e.g., "5-10"); input segments are trimmed before parsing. Useful for parsing config or input lists; invalid numeric input will throw NumberFormatException. * Add JsonUtil utility for Gson file I/O Introduce JsonUtil, a final utility class (Slf4j) providing Gson-based JSON file operations. Adds methods to load/save objects: loadFromFile, loadTypeFromFile, loadFromDirectory, saveToFile and saveTypeToFile. Uses UTF-8 encoding, Optional/Collection return types, basic file existence checks and error logging, and prevents instantiation. * Add Cooldowns component for player cooldowns Introduce a Cooldowns Spring component and Bukkit Listener to manage per-player, per-key cooldowns. Uses a Guava Table backed by concurrent maps to store expiry timestamps (epoch millis), provides addCooldown overloads (millis and Duration) and an isOnCooldown check. Clears a player's cooldowns on PlayerQuitEvent to prevent memory leaks. * Add OptionalBool utility for boolean ops Introduce OptionalBool to wrap boolean values and provide Optional-like, functional-style operations. The class uses singleton instances for true/false, offers isTrue/isFalse checks, conditional executors (ifTrue, ifFalse, ifTrueOrElse) and mapping utilities (mapIfTrue, mapIfFalse, mapIfTrueOrElse) that return Optional or computed values. Also overrides equals, hashCode, and toString. Placed in games.negative.engine.util for use across the codebase. * Add Reloadable and reload impl to PaperPlugin Introduce a new Reloadable interface with a single reload() method and implement plugin reload handling in PaperPlugin. PaperPlugin now invokes Reloadable beans via invokeBeans, calling their reload methods and logging any failures using SLF4J (@slf4j). Adds the new common interface file and updates imports in the Paper module. * Register Spring Listeners on enable; log commands PaperPlugin: onEnable now invokes Spring beans of type org.bukkit.event.Listener and registers them with the server PluginManager, logging any registration failures. Added the Listener import. PaperCommandRegistry: added an info log "Registering commands" at the start of onEnable to surface command registration in logs. * Bump API version to 1.1.0 Update apiVersion from 1.0.0 to 1.1.0 in common/build.gradle.kts and paper/build.gradle.kts to reflect the API minor release.
1 parent 12727a2 commit f96b74f

11 files changed

Lines changed: 740 additions & 4 deletions

File tree

README.md

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
# plugin-engine
2+
3+
A Java 21 library for building Minecraft Paper plugins with reusable building blocks for commands, scheduling, GUIs, messaging, configuration, and utility helpers.
4+
5+
## What this library provides
6+
7+
`plugin-engine` is split into two modules:
8+
9+
| Module | Artifact | Purpose |
10+
| --- | --- | --- |
11+
| common | `games.negative.engine:plugin-engine-common` | Platform-agnostic APIs and helpers (configuration, messages, command abstractions, utilities). |
12+
| paper | `games.negative.engine:plugin-engine-paper` | Paper-specific implementations (plugin base class, schedulers, command registration, GUI framework, item builder, jobs). |
13+
14+
## Compatibility
15+
16+
- Java 21
17+
- Paper API `1.21.8-R0.1-SNAPSHOT` (for the `paper` module)
18+
19+
## Installation
20+
21+
Add the Negative Games Maven repository:
22+
23+
### Gradle (Kotlin DSL)
24+
25+
```kotlin
26+
repositories {
27+
maven("https://repo.negative.games/repository/maven-releases/")
28+
maven("https://repo.negative.games/repository/maven-snapshots/")
29+
}
30+
```
31+
32+
Then add dependencies:
33+
34+
```kotlin
35+
dependencies {
36+
implementation("games.negative.engine:plugin-engine-paper:1.0.0")
37+
// or: implementation("games.negative.engine:plugin-engine-common:1.0.0")
38+
}
39+
```
40+
41+
### Maven
42+
43+
```xml
44+
<repositories>
45+
<repository>
46+
<id>negative-games-releases</id>
47+
<url>https://repo.negative.games/repository/maven-releases/</url>
48+
</repository>
49+
<repository>
50+
<id>negative-games-snapshots</id>
51+
<url>https://repo.negative.games/repository/maven-snapshots/</url>
52+
</repository>
53+
</repositories>
54+
```
55+
56+
```xml
57+
<dependencies>
58+
<dependency>
59+
<groupId>games.negative.engine</groupId>
60+
<artifactId>plugin-engine-paper</artifactId>
61+
<version>1.0.0</version>
62+
</dependency>
63+
</dependencies>
64+
```
65+
66+
Use `-SNAPSHOT` versions when consuming snapshot builds.
67+
68+
## Quick start (Paper plugins)
69+
70+
### 1) Extend `PaperPlugin`
71+
72+
```java
73+
package com.example;
74+
75+
import games.negative.engine.paper.PaperPlugin;
76+
77+
public final class ExamplePlugin extends PaperPlugin {
78+
}
79+
```
80+
81+
`PaperPlugin` initializes scheduler and MiniMessage utilities for you and exposes the shared `Plugin` contract (`directory()`, `fetchBeans(...)`).
82+
83+
### 2) Optional: custom library loader
84+
85+
If you need extra runtime libraries, extend `PaperPluginLoader`:
86+
87+
```java
88+
package com.example;
89+
90+
import games.negative.engine.paper.loader.PaperPluginLoader;
91+
import org.eclipse.aether.graph.Dependency;
92+
import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver;
93+
94+
public final class ExamplePluginLoader extends PaperPluginLoader {
95+
@Override
96+
public void addLibraries(MavenLibraryResolver resolver) {
97+
Dependency dep = dependency("com.example:example-lib:1.2.3");
98+
resolver.addDependency(dep);
99+
}
100+
}
101+
```
102+
103+
## Commands
104+
105+
`paper` includes `PaperCommandRegistry`, which auto-discovers Spring beans implementing `CloudCommand` and `CloudArgument`.
106+
107+
```java
108+
package com.example.command;
109+
110+
import games.negative.engine.command.CloudCommand;
111+
import games.negative.moss.spring.SpringComponent;
112+
import io.papermc.paper.command.brigadier.CommandSourceStack;
113+
import org.incendo.cloud.CommandManager;
114+
import org.incendo.cloud.annotations.Command;
115+
import org.incendo.cloud.annotations.CommandDescription;
116+
117+
@SpringComponent
118+
public final class ExampleCommand implements PaperCommand {
119+
120+
@Command("example")
121+
@CommandDescription("Example command")
122+
public void example(CommandSourceStack source) {
123+
124+
}
125+
}
126+
```
127+
128+
## Scheduling and jobs
129+
130+
Use the static `Scheduler` accessors:
131+
132+
```java
133+
Scheduler.sync().run(task -> {
134+
// Main-thread/global region work
135+
});
136+
137+
Scheduler.async().run(task -> {
138+
// Async work
139+
});
140+
141+
Scheduler.entity(player).execute(() -> {
142+
// Entity-thread safe work
143+
}, 1L);
144+
```
145+
146+
For recurring background logic, create beans implementing `SyncJob` or `AsyncJob`; `JobScheduler` auto-registers them on enable:
147+
148+
```java
149+
@SpringComponent
150+
public final class AnnounceJob implements SyncJob {
151+
@Override
152+
public Duration interval() {
153+
return Duration.ofSeconds(30);
154+
}
155+
156+
@Override
157+
public void tick(ScheduledTask task) {
158+
// Repeating logic
159+
}
160+
}
161+
```
162+
163+
## GUI framework
164+
165+
Use `ChestMenu`, `PaginatedMenu`, or `HopperMenu` with `Button`:
166+
167+
```java
168+
public final class ExampleMenu extends ChestMenu {
169+
public ExampleMenu(Player player) {
170+
super(player, "<green>Example", 3);
171+
172+
addButton(13, Button.builder()
173+
.item(viewer -> ItemBuilder.of(Material.EMERALD)
174+
.name("<green>Click me")
175+
.build())
176+
.action((button, viewer, event) -> viewer.sendMessage(Component.text("Clicked")))
177+
.build());
178+
}
179+
}
180+
```
181+
182+
Menus are cached per-player and handled by `PlayerInventoryController`.
183+
184+
## Messages and placeholders
185+
186+
`Message` and `MiniMessageUtil` support MiniMessage formatting plus PlaceholderAPI integration (when present):
187+
188+
```java
189+
Message.of("<gold>Hello, <name>!")
190+
.send(player, Placeholder.parsed("name", player.getName()));
191+
```
192+
193+
`PaperLocalizationPlatform` provides PlaceholderAPI and relational placeholder parsing for Paper audiences.
194+
195+
## Configuration
196+
197+
Use `Configuration<T>` with ConfigLib-backed YAML storage:
198+
199+
```java
200+
public final class ExampleConfig {
201+
public String prefix = "<gray>[Example]</gray>";
202+
}
203+
204+
Configuration<ExampleConfig> config = Configuration.config(
205+
plugin.directory().resolve("config.yml").toFile(),
206+
ExampleConfig.class
207+
);
208+
209+
String prefix = config.get().prefix;
210+
```
211+
212+
## Item building
213+
214+
`ItemBuilder` simplifies `ItemStack` creation:
215+
216+
```java
217+
ItemStack stack = ItemBuilder.of(Material.DIAMOND_SWORD)
218+
.name("<aqua>Starter Sword")
219+
.lore(List.of("<gray>Given on join"))
220+
.unbreakable(true)
221+
.glowing(true)
222+
.build();
223+
```
224+
225+
## Utilities in `common`
226+
227+
- `TimeUtil`: parse/format durations (`1d2h30m`, `H:MM:SS`, etc.)
228+
- `NumberUtil`: decimal formatting, ordinals (`1st`, `2nd`), condensed numbers (`1.2M`)
229+
- `AABB` (paper): simple axis-aligned bounding box representation
230+
231+
## Build and publish
232+
233+
From repository root:
234+
235+
```bash
236+
./gradlew clean build
237+
```
238+
239+
Publishing (used by CI for `release` / `snapshot` branches):
240+
241+
```bash
242+
./gradlew clean publish -PisRelease=true # releases repo
243+
./gradlew clean publish -PisRelease=false # snapshots repo
244+
```
245+

common/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ plugins {
66

77
var id = "plugin-engine-common"
88
var domain = "games.negative.engine"
9-
var apiVersion = "1.0.0"
9+
var apiVersion = "1.1.0"
1010

1111
repositories {
1212
mavenCentral()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package games.negative.engine.state;
2+
3+
public interface Reloadable {
4+
5+
void reload();
6+
7+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package games.negative.engine.util;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
6+
public class IntList {
7+
8+
/**
9+
* Parses a list of strings into a list of integers.
10+
* Strings can represent single integers or ranges (e.g., "5-10").
11+
*
12+
* @param strings The list of strings to parse.
13+
* @return A list of integers.
14+
*/
15+
public static List<Integer> parse(List<String> strings) {
16+
List<Integer> list = new ArrayList<>();
17+
18+
for (String string : strings) {
19+
if (string.contains("-")) {
20+
String[] range = string.split("-", 2);
21+
int start = Integer.parseInt(range[0].trim());
22+
int end = Integer.parseInt(range[1].trim());
23+
24+
for (int i = start; i <= end; i++) {
25+
list.add(i);
26+
}
27+
} else {
28+
list.add(Integer.parseInt(string.trim()));
29+
}
30+
}
31+
32+
return list;
33+
}
34+
}

0 commit comments

Comments
 (0)