Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0be96c4
chore: ignore local worktrees
Jakubk15 Jul 20, 2026
6f01fb6
docs: design ParcelLockers API module
Jakubk15 Jul 20, 2026
be98f38
docs: plan ParcelLockers API module implementation
Jakubk15 Jul 20, 2026
7ecefcb
build: split API and plugin modules
Jakubk15 Jul 20, 2026
6bea75b
feat: extract parcel and locker API contracts
Jakubk15 Jul 20, 2026
9841eb0
feat: expose locker service implementation
Jakubk15 Jul 20, 2026
e60fa90
feat: expose ParcelLockers API provider
Jakubk15 Jul 23, 2026
57c52e1
build: publish ParcelLockers API artifact
Jakubk15 Jul 23, 2026
d2b29a2
fix: route public sends through dispatch workflow
Jakubk15 Jul 24, 2026
56b3d15
fix: compensate failed parcel dispatch setup
Jakubk15 Jul 24, 2026
1c0f319
fix: reserve sender storage during dispatch
Jakubk15 Jul 24, 2026
11457bd
fix: enforce item storage consistency protocol
Jakubk15 Jul 24, 2026
5d6ddc9
fix: serialize item storage owner operations
Jakubk15 Jul 24, 2026
110bd8e
style: remove trailing whitespace
Jakubk15 Jul 24, 2026
d27dbf4
fix: enforce public parcel operation contracts
Jakubk15 Jul 24, 2026
d8356fe
fix: enforce parcel operation integrity
Jakubk15 Jul 24, 2026
b2588c2
fix: serialize parcel lifecycle mutations
Jakubk15 Jul 24, 2026
7d14ed1
fix: harden parcel operation coordination
Jakubk15 Jul 24, 2026
820eea9
fix: preserve admin parcel lifecycle integrity
Jakubk15 Jul 28, 2026
7143301
Merge origin/master into codex/issue-176-api-module
Jakubk15 Jul 28, 2026
c8383a5
codestyle changes
Jakubk15 Jul 30, 2026
212e5c1
properly annotate fields
Jakubk15 Jul 30, 2026
ab8ee53
Merge branch 'master' into codex/issue-176-api-module
Jakubk15 Aug 15, 2026
76c0fec
remove unnecessary whitespace
Jakubk15 Aug 15, 2026
7713d85
Delete parcellockers-api/src/test/java/com/eternalcode/parcellockers/…
Jakubk15 Aug 15, 2026
0ae9b5e
Update AGENTS.md
Jakubk15 Aug 15, 2026
9eee03d
Merge branch 'codex/issue-176-api-module' of https://github.com/Etern…
Jakubk15 Aug 15, 2026
b659a48
dependency: Update Gradle to v9.7.1 (#243)
renovate[bot] Aug 24, 2026
28f933e
dependency: Update junit-framework monorepo to v6.1.3 (#244)
renovate[bot] Aug 24, 2026
b521b65
dependency: Update dependency com.discord4j:discord4j-core to v3.3.3 …
renovate[bot] Aug 24, 2026
be5a1b4
dependency: Update plugin xyz.jpenilla.run-paper to v3.1.0 (#245)
renovate[bot] Aug 24, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ build/
.codex/
.gemini/
graphify-out/
.worktrees/

.DS_Store
[Dd]esktop.ini
Expand Down
21 changes: 17 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
# AGENTS.md

This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
This file provides guidance to AI agents when working with code in this repository.

## Philosophy and Code Style

- You can communicate all of your views and report submit objections to the user's way of thinking.
- Use 'this' wherever possible (this.field, this.method(), etc.).
- Use SOLID, KISS, Clean Code principles, DRY (Don't Repeat Yourself), no copy-paste coding, Defensive copying for safety
- Prefer meaningful variable names
- Use fail-fast approach, validate early, throw meaningful exceptions
- Create immutable objects where possible, use builder pattern for complex objects, no magic numbers/strings - prefer constants
- If asked by user for a simple task/edit - do not invoke unnecessary skills such as TDD or planning.

## Build & Run Commands

Expand All @@ -15,17 +25,20 @@ This file provides guidance to Codex (Codex.ai/code) when working with code in t
./gradlew test --tests "com.eternalcode.parcellockers.database.ParcelRepositoryIntegrationTest"

# Start a local Paper server with the plugin loaded (downloads Paper + dependencies automatically)
./gradlew runServer
./gradlew runServer --console=plain
```

Requires JDK 21+. The `runServer` task uses an Adoptium Java 25 toolchain and auto-downloads LuckPerms, VaultUnlocked, and EssentialsX. To train its AOT cache, comment out `-XX:AOTCache=server.aot`, uncomment `-XX:AOTCacheOutput=server.aot`, run the server with a representative workload, and stop it cleanly. Restore the original comments for subsequent runs. The cache is stored as `run/server.aot` and includes all classes loaded during training, including ParcelLockers. Uncomment the DiscordSRV line in `build.gradle.kts` to test that integration locally.
Requires JDK 21+. The `runServer` task uses a Java 25 toolchain and auto-downloads LuckPerms, VaultUnlocked, and EssentialsX. Uncomment the DiscordSRV line in `build.gradle.kts` to test that integration locally.

## Architecture Overview

ParcelLockers is a Paper plugin (Minecraft 1.21) that lets players transfer items between parcel locker blocks across the world, with optional Discord notifications.
ParcelLockers is a Paper plugin (Minecraft 1.21+) that lets players transfer items between parcel locker blocks across the world, with optional Discord notifications.

### Entry Point & Wiring

The project consists of two Gradle modules: `parcellockers-api`, which contains the API the plugin depends on. Other plugins' developers can hook into this API and integrate with our plugin.
API Implementation and the code of the plugin itself is in the `parcellockers-plugin` directory.

`ParcelLockers.java` (`onEnable`) is the manual DI root — all components are instantiated and wired there in order: config → database → repositories → managers/services → GUIs → commands → event controllers. There is no DI framework; dependencies are passed via constructors.

### Domain Layers
Expand Down
71 changes: 0 additions & 71 deletions CLAUDE.md

This file was deleted.

1 change: 1 addition & 0 deletions CLAUDE.md
227 changes: 28 additions & 199 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,211 +1,40 @@
import net.minecrell.pluginyml.paper.PaperPluginDescription
import xyz.jpenilla.runtask.task.AbstractRun

plugins {
id("java")
id("de.eldoria.plugin-yml.paper") version "0.9.0"
id("xyz.jpenilla.run-paper") version "3.0.2"
id("com.gradleup.shadow") version "9.6.1"
id("com.modrinth.minotaur") version "2.+"
}

group = "com.eternalcode"
version = "0.5.1-BETA"

repositories {
maven("https://maven-central.storage-download.googleapis.com/maven2/") // maven central mirror

maven("https://repo.triumphteam.dev/snapshots/")
maven("https://jitpack.io")
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://repo.eternalcode.pl/releases")
maven("https://storehouse.okaeri.eu/repository/maven-public/")
maven("https://nexus.scarsz.me/content/groups/public/") // DiscordSRV
}

dependencies {
// minecraft development api
compileOnly("io.papermc.paper:paper-api:${Versions.PAPER_API}")
paperLibrary("dev.rollczi:litecommands-bukkit:${Versions.LITECOMMANDS}")
paperLibrary("dev.rollczi:litecommands-adventure:${Versions.LITECOMMANDS}")

// gui
paperLibrary("dev.triumphteam:triumph-gui-paper:${Versions.TRIUMPH_GUI}")

// configs
paperLibrary("eu.okaeri:okaeri-configs-serdes-commons:${Versions.OKAERI_CONFIGS}")
paperLibrary("eu.okaeri:okaeri-configs-serdes-bukkit:${Versions.OKAERI_CONFIGS}")
paperLibrary("eu.okaeri:okaeri-configs-yaml-bukkit:${Versions.OKAERI_CONFIGS}")

// gitcheck
paperLibrary("com.eternalcode:gitcheck:${Versions.GITCHECK}")

// metrics
implementation("org.bstats:bstats-bukkit:${Versions.BSTATS}")

// database
paperLibrary("com.zaxxer:HikariCP:${Versions.HIKARICP}")
paperLibrary("com.j256.ormlite:ormlite-jdbc:${Versions.ORMLITE}")
paperLibrary("com.h2database:h2:${Versions.H2}")
paperLibrary("org.postgresql:postgresql:${Versions.POSTGRESQL}")

// lombok
compileOnly("org.projectlombok:lombok:${Versions.LOMBOK}")
annotationProcessor("org.projectlombok:lombok:${Versions.LOMBOK}")

// jetbrains annotations
compileOnly("org.jetbrains:annotations:${Versions.JETBRAINS_ANNOTATIONS}")

// jackson-bukkit
paperLibrary("de.eldoria.jacksonbukkit:paper:${Versions.JACKSON_BUKKIT}")

// completable-futures
paperLibrary("com.spotify:completable-futures:${Versions.COMPLETABLE_FUTURES}")

// eternalcode commons
paperLibrary("com.eternalcode:eternalcode-commons-adventure:${Versions.ETERNALCODE_COMMONS}")
paperLibrary("com.eternalcode:eternalcode-commons-bukkit:${Versions.ETERNALCODE_COMMONS}")
paperLibrary("com.eternalcode:eternalcode-commons-shared:${Versions.ETERNALCODE_COMMONS}")

// multification
paperLibrary("com.eternalcode:multification-bukkit:${Versions.MULTIFICATION}")
paperLibrary("com.eternalcode:multification-okaeri:${Versions.MULTIFICATION}")

// caffeine
paperLibrary("com.github.ben-manes.caffeine:caffeine:${Versions.CAFFEINE}")

// vault
compileOnly("com.github.MilkBowl:VaultAPI:${Versions.VAULT_API}")

// discord integration library
paperLibrary("com.discord4j:discord4j-core:${Versions.DISCORD4J}")

// discordsrv (optional integration)
compileOnly("com.discordsrv:discordsrv:${Versions.DISCORDSRV}")

testImplementation("org.junit.jupiter:junit-jupiter-api:${Versions.JUNIT}")
testImplementation("org.junit.jupiter:junit-jupiter-params:${Versions.JUNIT}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${Versions.JUNIT}")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")

testImplementation("org.mockito:mockito-core:${Versions.MOCKITO}")

testImplementation("org.testcontainers:junit-jupiter:${Versions.TESTCONTAINERS}")
testImplementation("org.testcontainers:mysql:${Versions.TESTCONTAINERS}")
testImplementation("com.zaxxer:HikariCP:${Versions.HIKARICP}")
testImplementation("com.j256.ormlite:ormlite-jdbc:${Versions.ORMLITE}")
testImplementation("com.h2database:h2:${Versions.H2}")
testImplementation("mysql:mysql-connector-java:${Versions.MYSQL_CONNECTOR}")
testImplementation("de.eldoria.jacksonbukkit:paper:${Versions.JACKSON_BUKKIT}")
testImplementation("com.eternalcode:eternalcode-commons-adventure:${Versions.ETERNALCODE_COMMONS}")
testImplementation("com.eternalcode:eternalcode-commons-bukkit:${Versions.ETERNALCODE_COMMONS}")
testImplementation("com.eternalcode:eternalcode-commons-shared:${Versions.ETERNALCODE_COMMONS}")
testImplementation("com.eternalcode:multification-bukkit:${Versions.MULTIFICATION}")
testImplementation("com.github.MilkBowl:VaultAPI:${Versions.VAULT_API}")
testImplementation("dev.triumphteam:triumph-gui-paper:${Versions.TRIUMPH_GUI}")

testImplementation("io.papermc.paper:paper-api:${Versions.PAPER_API}")
testImplementation("eu.okaeri:okaeri-configs-yaml-bukkit:${Versions.OKAERI_CONFIGS}")
}

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}

paper {
name = "ParcelLockers"
main = "com.eternalcode.parcellockers.ParcelLockers"
version = project.version.toString()
apiVersion = "1.21"
author = "EternalCodeTeam"
website = "https://github.com/EternalCodeTeam/ParcelLockers"
loader = "com.eternalcode.parcellockers.ParcelLockersLibraryLoader"
generateLibrariesJson = true
foliaSupported = false
serverDependencies {
register("Vault") {
required = true
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
}
register("DiscordSRV") {
required = false
load = PaperPluginDescription.RelativeLoadOrder.BEFORE
}
id("de.eldoria.plugin-yml.paper") version "0.9.0" apply false
id("xyz.jpenilla.run-paper") version "3.1.0" apply false
id("com.gradleup.shadow") version "9.6.1" apply false
id("com.modrinth.minotaur") version "2.+" apply false
}

allprojects {
group = "com.eternalcode"
version = "0.5.1-BETA"

repositories {
maven("https://maven-central.storage-download.googleapis.com/maven2/")
maven("https://repo.triumphteam.dev/snapshots/")
maven("https://jitpack.io")
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://repo.eternalcode.pl/releases")
maven("https://storehouse.okaeri.eu/repository/maven-public/")
maven("https://nexus.scarsz.me/content/groups/public/")
}
}

tasks.withType<JavaCompile> {
options.encoding = "UTF-8"
options.isIncremental = true
options.compilerArgs.add("-parameters")
options.release = 21
}
subprojects {
apply(plugin = "java")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't this considered a legacy way of loading plugins?


tasks.withType<AbstractRun> {
javaLauncher = javaToolchains.launcherFor {
vendor = JvmVendorSpec.ADOPTIUM
languageVersion = JavaLanguageVersion.of(25)
extensions.configure<JavaPluginExtension> {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
}

modrinth {
token.set(providers.environmentVariable("MODRINTH_TOKEN"))
projectId.set("parcellockers")
versionNumber.set(project.version.toString())
versionType.set(getVersionType(project.version.toString()))
// changelog.set(providers.environmentVariable("MODRINTH_CHANGELOG"))
debugMode.set(providers.environmentVariable("MODRINTH_DEBUG").map(String::toBoolean).orElse(false))
uploadFile.set(tasks.shadowJar)
gameVersions.addAll("1.21.11", "26.1", "26.1.1", "26.1.2", "26.2")
loaders.addAll("bukkit", "paper", "purpur")
syncBodyFrom = rootProject.file("README.md").readText()
}

tasks {
runServer {
minecraftVersion("26.2")
downloadPlugins {
modrinth("luckperms", "v5.5.53-bukkit")
modrinth("vaultunlocked", "2.20.2")
modrinth("essentialsx", "2.22.0")
// modrinth("discordsrv", "1.30.4") // uncomment to test with DiscordSRV integration
}
jvmArgs(
"-Dcom.mojang.eula.agree=true",
"-Xlog:aot=info"
)
// jvmArgs("-XX:AOTCacheOutput=server.aot")
jvmArgs("-XX:AOTCache=server.aot")
tasks.withType<JavaCompile>().configureEach {
options.encoding = "UTF-8"
options.isIncremental = true
options.compilerArgs.add("-parameters")
options.release = 21
}

test {
tasks.withType<Test>().configureEach {
useJUnitPlatform()
}

shadowJar {
archiveFileName.set("ParcelLockers v${project.version}.jar")

exclude(
"org/intellij/lang/annotations/**",
"org/jetbrains/annotations/**",
"META-INF/**"
)

mergeServiceFiles()

val relocationPrefix = "com.eternalcode.parcellockers.libs"

listOf(
"org.bstats"
).forEach { relocate(it, "$relocationPrefix.$it") }
}
}

fun getVersionType(version: String): String {
return when {
version.contains("SNAPSHOT") -> "beta"
version.contains("alpha", true) -> "alpha"
version.contains("beta", true) -> "beta"
else -> "release"
}
}
5 changes: 3 additions & 2 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ object Versions {

const val LOMBOK = "1.18.46"
const val JETBRAINS_ANNOTATIONS = "26.1.0"
const val JSPECIFY = "1.0.0"

const val JACKSON_BUKKIT = "2.0.0"
const val COMPLETABLE_FUTURES = "0.3.6"
Expand All @@ -26,10 +27,10 @@ object Versions {
const val CAFFEINE = "3.2.4"
const val VAULT_API = "1.7.1"

const val DISCORD4J = "3.3.2"
const val DISCORD4J = "3.3.3"
const val DISCORDSRV = "1.30.5"

const val JUNIT = "6.1.2"
const val JUNIT = "6.1.3"
const val TESTCONTAINERS = "1.21.4"
const val MYSQL_CONNECTOR = "8.0.33"
const val MOCKITO = "5.23.0"
Expand Down
Loading