Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
b639133
chore: fix .DS_Store typo and ignore logs/+PR_DESCRIPTION.md
BrainRTP Apr 22, 2026
7b43fa8
refactor: move plugin sources into plugin/ subproject (prep for multi…
BrainRTP Apr 22, 2026
1fd6cd6
build: introduce multi-module root (settings + common subprojects con…
BrainRTP Apr 22, 2026
6351c86
build: adapt plugin/build.gradle to new location (drop root-supplied …
BrainRTP Apr 22, 2026
b8e0297
build: add empty :api subproject skeleton (java-library, paper 1.21.11)
BrainRTP Apr 22, 2026
8b1fceb
feat(api): import sources from abstract-menus-api repo (verbatim, no …
BrainRTP Apr 22, 2026
27512a4
feat(plugin): depend on :api subproject (drop external JitPack API dep)
BrainRTP Apr 22, 2026
f4917d0
build(api): add maven-publish with GitHub Packages target
BrainRTP Apr 22, 2026
e7fb399
ci: add jitpack.yml for multi-module api publishing
BrainRTP Apr 22, 2026
72a8505
ci(build): support multi-module — upload api artifacts alongside plugin
BrainRTP Apr 22, 2026
34635f5
ci(release): attach api artifacts (binary+sources+javadoc) alongside …
BrainRTP Apr 22, 2026
d990f68
ci: add publish-api workflow (GitHub Packages on release)
BrainRTP Apr 22, 2026
80130aa
release: bump to 2.0.0-alpha (first monorepo cut)
BrainRTP Apr 22, 2026
2791687
docs(api): rich javadoc example on EconomyHandler
BrainRTP Apr 22, 2026
6a12a61
feat(api): add MenuExtension interface — addon contract
BrainRTP Apr 22, 2026
b5d1fb4
feat(api): add TypeRegistry<T> — owner-aware replacement for Types.* …
BrainRTP Apr 22, 2026
5425272
feat(api): add AbstractMenusApi — entry interface (replaces AbstractM…
BrainRTP Apr 22, 2026
14698fd
feat(plugin): TypeRegistryImpl with owner-tracked register/unregister
BrainRTP Apr 22, 2026
fa0194f
feat(plugin): AbstractMenusApiImpl — runtime holder of registries + p…
BrainRTP Apr 22, 2026
ae30736
feat(plugin): instantiate AbstractMenusApiImpl in onEnable + publish …
BrainRTP Apr 22, 2026
112360d
feat(plugin): CoreExtension + five empty bundle stubs (dogfood scaffold)
BrainRTP Apr 22, 2026
1e0aa69
feat(core): populate CoreActionsBundle from MenuActions.init (not wir…
BrainRTP Apr 22, 2026
d46391f
feat(core): populate CoreRulesBundle from MenuRules.init
BrainRTP Apr 22, 2026
52fb1ec
feat(core): populate CoreItemPropsBundle from ItemProps.init
BrainRTP Apr 22, 2026
292b432
feat(core): populate CoreActivatorsBundle from Activators.init
BrainRTP Apr 22, 2026
45a6794
feat(core): populate CoreCatalogsBundle from Catalogs.init
BrainRTP Apr 22, 2026
fd8f0ba
refactor(plugin): switch core registration to CoreExtension (dogfood …
BrainRTP Apr 22, 2026
02d551b
refactor(plugin): migrate Types.* call sites to AbstractMenusApi.get().*
BrainRTP Apr 22, 2026
f39dae4
refactor(core): introduce CoreItemPropertyKeys constants holder
BrainRTP Apr 22, 2026
f7e4385
refactor(api)!: remove Types — replaced by AbstractMenusApi + TypeReg…
BrainRTP Apr 22, 2026
e046cba
refactor(api)!: remove AbstractMenusPlugin + AbstractMenusProvider
BrainRTP Apr 22, 2026
3ce550d
test(core): CoreExtensionTest — verify registrations after onEnable
BrainRTP Apr 22, 2026
1a3a451
feat(addon): AddonConf + HOCON parser (8 tests)
BrainRTP Apr 22, 2026
ad32c6c
feat(addon): AddonDependencyGraph — DFS topsort + cycle detection (7 …
BrainRTP Apr 22, 2026
0406f65
feat(addon): AddonClassLoader — parent-first for API/Bukkit/JDK, chil…
BrainRTP Apr 23, 2026
f0c7b50
feat(addon): AddonStatus enum + LoadedAddon container
BrainRTP Apr 23, 2026
2f6f4a3
feat(addon): AddonManager skeleton (no loading logic yet)
BrainRTP Apr 23, 2026
0d47c02
feat(addon): AddonManager.discover — scan addons dir + parse addon.conf
BrainRTP Apr 23, 2026
feca7e3
feat(addon): AddonManager.loadAll — sort + pluginDep filter + onLoad/…
BrainRTP Apr 23, 2026
93f0bdf
feat(addon): AddonManager.unloadAll — onDisable + classloader close
BrainRTP Apr 23, 2026
a7e4022
feat(addon): AddonManager.reload — single-addon hot reload
BrainRTP Apr 23, 2026
aac110c
feat(plugin): wire AddonManager into onEnable + onDisable
BrainRTP Apr 23, 2026
b675c49
feat(plugin): /am addons list|reload|info command
BrainRTP Apr 23, 2026
7fb11a0
test(addon): AddonManagerIntegrationTest — end-to-end jar load + regi…
BrainRTP Apr 23, 2026
4b7704a
feat(api): add ProviderRegistry — pluggable replacement for Handlers …
BrainRTP Apr 23, 2026
4fb42a4
feat(api): expose ProviderRegistry via AbstractMenusApi.providers()
BrainRTP Apr 23, 2026
a50d3df
feat(plugin): ProviderRegistryImpl with 5-section generic storage (8 …
BrainRTP Apr 23, 2026
35538dd
feat(plugin): AbstractMenusApiImpl exposes ProviderRegistryImpl
BrainRTP Apr 23, 2026
23ea6af
feat(core): CoreProvidersBundle — registerProviders via new SPI (not …
BrainRTP Apr 23, 2026
99a335f
feat(core): invoke CoreProvidersBundle from CoreExtension.onEnable
BrainRTP Apr 23, 2026
ce082fd
refactor(plugin): migrate Handlers.* call sites to AbstractMenusApi.p…
BrainRTP Apr 23, 2026
02688b1
refactor(api)!: remove Handlers facade + AbstractMenus.registerProviders
BrainRTP Apr 23, 2026
5c92ab5
feat(plugin): ProviderRegistryImpl consults config defaults before au…
BrainRTP Apr 23, 2026
94ebd3e
feat(plugin): config.conf providers{} block + MainConfig.providerDefault
BrainRTP Apr 23, 2026
85cb622
feat(plugin): wire MainConfig.providerDefault into ProviderRegistryImpl
BrainRTP Apr 23, 2026
65a8e1b
feat(data): economy actions/rule accept optional provider: HOCON field
BrainRTP Apr 23, 2026
263763b
test(data): MoneyProviderSelectionTest — provider: selection + fail-a…
BrainRTP Apr 23, 2026
daa2c28
docs(api): rich javadoc on Level/Permissions/Placeholder/Skin handlers
BrainRTP Apr 23, 2026
d449d63
docs(api): rich javadoc on core root interfaces (Action/Rule/Activato…
BrainRTP Apr 23, 2026
5845713
docs(api): rich javadoc on Menu/Item/ItemProperty/Slot
BrainRTP Apr 23, 2026
efc1c7f
docs(api): rich javadoc on Slot{Index,Pos,Range,Matrix} variants
BrainRTP Apr 23, 2026
a5d577f
docs(api): rich javadoc on Var/VarBuilder/VariableManager
BrainRTP Apr 23, 2026
0427b5b
docs(api): rich javadoc on Colors — legacy/hex code conversion utility
BrainRTP Apr 23, 2026
1850d23
edit gitignore
BrainRTP Apr 23, 2026
4f6cb9a
small changes
BrainRTP Apr 23, 2026
a33a405
feat(addons): /am addons load|rescan + tab completion
BrainRTP Apr 27, 2026
187f6c9
merge: master into task/api-merge
BrainRTP Apr 27, 2026
d79a879
fix: defer initial menu load to ServerLoadEvent
BrainRTP Apr 28, 2026
cd5b39b
build: rename plugin thin jar to AbstractMenus-<ver>-thin.jar
BrainRTP Apr 28, 2026
56d1033
build: rename api jar to AbstractMenus-api-<ver>.jar locally
BrainRTP Apr 28, 2026
c7c0560
build: aggregate shipping artifacts into <root>/build/dist/
BrainRTP Apr 28, 2026
68a1c5d
build: also trigger dist on shadowJar and assemble
BrainRTP Apr 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
33 changes: 26 additions & 7 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ jobs:
with:
gradle-home-cache-cleanup: true

- name: Build + tests
run: ./gradlew --no-daemon build shadowJar
- name: Build + tests (all modules) + shadowJar
run: ./gradlew --no-daemon build :plugin:shadowJar

- name: Publish test report
if: always()
Expand All @@ -49,14 +49,33 @@ jobs:
fail_on_failure: true
require_tests: false

- name: Resolve JAR path
id: jar
run: echo "path=$(ls build/libs/AbstractMenus-*.jar | head -n1)" >> "$GITHUB_OUTPUT"
- name: Resolve plugin JAR path
id: plugin_jar
# Skip the dev-only -thin jar; only the shaded fat jar ships.
run: echo "path=$(ls plugin/build/libs/AbstractMenus-*.jar | grep -v -- '-thin\.jar$' | head -n1)" >> "$GITHUB_OUTPUT"

- name: Upload shaded JAR
- name: Resolve API JAR paths
id: api_jars
run: |
echo "binary=$(ls api/build/libs/AbstractMenus-api-*.jar | grep -v 'sources\|javadoc' | head -n1)" >> "$GITHUB_OUTPUT"
echo "sources=$(ls api/build/libs/AbstractMenus-api-*-sources.jar | head -n1)" >> "$GITHUB_OUTPUT"
echo "javadoc=$(ls api/build/libs/AbstractMenus-api-*-javadoc.jar | head -n1)" >> "$GITHUB_OUTPUT"

- name: Upload plugin shadow JAR
uses: actions/upload-artifact@v4
with:
name: AbstractMenus-jar
path: ${{ steps.jar.outputs.path }}
path: ${{ steps.plugin_jar.outputs.path }}
if-no-files-found: error
retention-days: 14

- name: Upload api artifacts
uses: actions/upload-artifact@v4
with:
name: AbstractMenus-api
path: |
${{ steps.api_jars.outputs.binary }}
${{ steps.api_jars.outputs.sources }}
${{ steps.api_jars.outputs.javadoc }}
if-no-files-found: error
retention-days: 14
40 changes: 40 additions & 0 deletions .github/workflows/publish-api.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Publish API to GitHub Packages

on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag to re-publish'
required: true
type: string

permissions:
contents: read
packages: write

jobs:
publish:
name: Publish :api to GH Packages
runs-on: ubuntu-latest
steps:
- name: Checkout (release tag)
uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name || inputs.tag }}

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4

- name: Publish :api
run: ./gradlew --no-daemon :api:publish
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43 changes: 26 additions & 17 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
workflow_dispatch:
inputs:
tag:
description: 'Existing release tag to attach a rebuilt JAR to'
description: 'Existing release tag to attach rebuilt JARs to'
required: true
type: string

Expand All @@ -15,7 +15,7 @@ permissions:

jobs:
attach-jar:
name: Build + attach JAR to release
name: Build + attach JARs to release
runs-on: ubuntu-latest
steps:
- name: Checkout (release tag)
Expand All @@ -36,30 +36,39 @@ jobs:
- name: Verify tag matches project version
run: |
TAG="${{ github.event.release.tag_name || inputs.tag }}"
# Strip a leading 'v' if present (v1.18.0 → 1.18.0)
TAG_VERSION="${TAG#v}"
GRADLE_VERSION=$(grep -E "^version\s+['\"]" build.gradle | sed -E "s/.*['\"]([^'\"]+)['\"].*/\1/")
echo "release tag: $TAG"
echo "tag version: $TAG_VERSION"
echo "gradle version: $GRADLE_VERSION"
GRADLE_VERSION=$(grep -E "^\s*version\s*=?\s*['\"]" build.gradle | head -n1 | sed -E "s/.*['\"]([^'\"]+)['\"].*/\1/")
echo "release tag: $TAG"
echo "tag version: $TAG_VERSION"
echo "gradle version: $GRADLE_VERSION"
if [ "$TAG_VERSION" != "$GRADLE_VERSION" ]; then
echo "::error::Release tag version ($TAG_VERSION) does not match build.gradle version ($GRADLE_VERSION). Bump build.gradle before tagging, or retag."
echo "::error::Release tag version ($TAG_VERSION) does not match root build.gradle version ($GRADLE_VERSION). Bump build.gradle before tagging, or retag."
exit 1
fi

- name: Build shaded JAR (skip tests — already run in Build workflow on the tagged commit)
run: ./gradlew --no-daemon shadowJar
- name: Build shadow plugin + api artifacts (skip tests — already run on the tagged commit by build.yml)
run: ./gradlew --no-daemon :plugin:shadowJar :api:build

- name: Resolve JAR path
id: jar
- name: Resolve artifact paths
id: paths
run: |
JAR=$(ls build/libs/AbstractMenus-*.jar | head -n1)
echo "path=$JAR" >> "$GITHUB_OUTPUT"
echo "name=$(basename "$JAR")" >> "$GITHUB_OUTPUT"
# Skip the dev-only -thin jar; only the shaded fat jar ships.
PLUGIN=$(ls plugin/build/libs/AbstractMenus-*.jar | grep -v -- '-thin\.jar$' | head -n1)
API_BIN=$(ls api/build/libs/AbstractMenus-api-*.jar | grep -v 'sources\|javadoc' | head -n1)
API_SRC=$(ls api/build/libs/AbstractMenus-api-*-sources.jar | head -n1)
API_DOC=$(ls api/build/libs/AbstractMenus-api-*-javadoc.jar | head -n1)
echo "plugin=$PLUGIN" >> "$GITHUB_OUTPUT"
echo "api_bin=$API_BIN" >> "$GITHUB_OUTPUT"
echo "api_src=$API_SRC" >> "$GITHUB_OUTPUT"
echo "api_doc=$API_DOC" >> "$GITHUB_OUTPUT"

- name: Attach JAR to release
- name: Attach JARs to release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event.release.tag_name || inputs.tag }}
files: ${{ steps.jar.outputs.path }}
files: |
${{ steps.paths.outputs.plugin }}
${{ steps.paths.outputs.api_bin }}
${{ steps.paths.outputs.api_src }}
${{ steps.paths.outputs.api_doc }}
fail_on_unmatched_files: true
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.gradle
.idea
build
.DS_Strore
.DS_Store
logs
77 changes: 77 additions & 0 deletions api/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
plugins {
id 'java-library'
id 'maven-publish'
}

// Local file in api/build/libs/ is AbstractMenus-api-<ver>.jar so
// it visually groups with AbstractMenus-<ver>.jar from :plugin and an
// op who copies both off CI artifacts can tell at a glance both are AM.
//
// Maven publish artifactId stays 'api' (project.name) so the coord
// 'ru.abstractmenus:api:<ver>' is unchanged for consumers - Gradle's
// maven-publish renames files to <artifactId>-<version>.jar inside the
// repository regardless of what archivesName is on the local jar task.
base {
archivesName = 'AbstractMenus-api'
}

repositories {
// root already supplies papermc + jitpack + mavenCentral + mavenLocal
}

dependencies {
compileOnly 'io.papermc.paper:paper-api:1.21.11-R0.1-SNAPSHOT'
api 'com.github.AbstractMenus:hocon:1.0.6'

compileOnly 'org.projectlombok:lombok:1.18.44'
annotationProcessor 'org.projectlombok:lombok:1.18.44'

testImplementation 'org.junit.jupiter:junit-jupiter-api:5.14.3'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.14.3'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

java {
withSourcesJar()
withJavadocJar()
}

javadoc {
title = "AbstractMenus ${project.version} API"
options.addStringOption('Xdoclint:none', '-quiet')
}

publishing {
publications {
maven(MavenPublication) {
from components.java
// artifactId defaults to project.name == 'api'
// → Maven coord: ru.abstractmenus:api:<version>

pom {
name = 'AbstractMenus API'
description = 'Public API for the AbstractMenus Minecraft plugin (Paper/Folia).'
url = 'https://github.com/AbstractMenus/minecraft-plugin'
licenses {
license {
name = 'MIT License'
url = 'https://github.com/AbstractMenus/minecraft-plugin/blob/master/LICENSE'
}
}
scm {
url = 'https://github.com/AbstractMenus/minecraft-plugin'
}
}
}
}
repositories {
maven {
name = 'GitHubPackages'
url = uri('https://maven.pkg.github.com/AbstractMenus/minecraft-plugin')
credentials {
username = System.getenv('GITHUB_ACTOR')
password = System.getenv('GITHUB_TOKEN')
}
}
}
}
140 changes: 140 additions & 0 deletions api/src/main/java/ru/abstractmenus/api/AbstractMenusApi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package ru.abstractmenus.api;

import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import ru.abstractmenus.api.inventory.ItemProperty;
import ru.abstractmenus.api.inventory.Menu;
import ru.abstractmenus.api.variables.VariableManager;
import ru.abstractmenus.hocon.api.serialize.NodeSerializers;

import java.util.Optional;

/**
* Runtime entry point for AbstractMenus. Exposes the type registries, the
* shared HOCON {@link NodeSerializers}, menu lifecycle operations, and the
* variable manager.
*
* <p>Obtain the running instance via {@link #get()}:
*
* <pre>{@code
* AbstractMenusApi api = AbstractMenusApi.get();
* api.actions().register("myAction", MyAction.class, new MyAction.Serializer(), this);
* }</pre>
*
* <p>The implementation is registered against Bukkit's {@code ServicesManager}
* during the plugin's {@code onEnable()}. Do not call {@link #get()} before
* AbstractMenus has enabled &mdash; you will get {@code null}. For
* plugin-as-addon (Path 1) usage, call from your own {@code onEnable()} with
* {@code depend: [AbstractMenus]} in {@code plugin.yml} to guarantee ordering.
*
* @see MenuExtension
* @see TypeRegistry
*/
public interface AbstractMenusApi {

/**
* Look up the running API instance from Bukkit's ServicesManager.
*
* @return the running API, or {@code null} if AbstractMenus has not
* enabled yet
*/
static AbstractMenusApi get() {
return Bukkit.getServicesManager().load(AbstractMenusApi.class);
}

// ---- Registries -----------------------------------------------------

/** @return the action registry */
TypeRegistry<Action> actions();

/** @return the rule registry */
TypeRegistry<Rule> rules();

/** @return the activator registry */
TypeRegistry<Activator> activators();

/** @return the item-property registry */
TypeRegistry<ItemProperty> itemProperties();

/** @return the catalog registry */
TypeRegistry<Catalog<?>> catalogs();

/**
* Handler-provider registry (economy, permissions, levels, placeholders,
* skins). Replaces the old static {@code Handlers} facade.
*
* @return the provider registry
*/
ProviderRegistry providers();

/**
* Shared HOCON serializer collection. Type registrations add to this
* automatically; consumers rarely need to access it directly.
*
* @return the shared serializer collection
*/
NodeSerializers serializers();

// ---- Subsystems -----------------------------------------------------

/** @return the plugin's variable manager */
VariableManager variables();

/**
* The underlying Bukkit {@link Plugin} instance (the AbstractMenus
* plugin itself). Exposed for scheduler access and listener registration;
* prefer the high-level methods on this interface when possible.
*
* @return the plugin
*/
Plugin getPlugin();

// ---- Menu lifecycle (migrated from AbstractMenusPlugin) -------------

/**
* Reload all menus from {@code plugins/AbstractMenus/menus/}. Called by
* {@code /am reload}.
*/
void loadMenus();

/**
* Open a menu for a player with a triggering activator and context.
*
* @param activator the activator that triggered opening (may be
* {@code null} for programmatic opens)
* @param ctx opening context (source entity/block/etc., may be
* {@code null})
* @param player the viewer
* @param menu the menu to open
*/
void openMenu(Activator activator, Object ctx, Player player, Menu menu);

/**
* Open a menu for a player with no activator or context.
*
* @param player the viewer
* @param menu the menu to open
*/
void openMenu(Player player, Menu menu);

/**
* Get the menu currently open for {@code player}.
*
* @param player the viewer
* @return the open menu, or {@link Optional#empty()} if the player has no
* AbstractMenus menu open
*/
Optional<Menu> getOpenedMenu(Player player);

// ---- Meta -----------------------------------------------------------

/**
* API version string, taken from the plugin's {@code version}. Purely
* diagnostic &mdash; logged on startup and surfaced in
* {@code /am addons list}.
*
* @return the API version (e.g. {@code "2.0.0-alpha"})
*/
String apiVersion();
}
Loading
Loading