Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
eb7ae75
Add internal packet GUI backend
Keviro May 25, 2026
50dec6c
Apply suggestions from code review
Keviro May 25, 2026
974b0b4
fix broken patch - thanks @copilot
Keviro May 25, 2026
29bc745
update import path for SpigotConversionUtil
Keviro May 25, 2026
1707b80
fix folia issues
Keviro May 25, 2026
8ca49fe
enhance PacketGuiBackend with inventory close handling
Keviro May 25, 2026
4a605a8
add PacketItemConverter for item normalization
Keviro May 26, 2026
876fd47
Improve packet GUI performance with dirty-slot rendering
Keviro May 26, 2026
c0642c2
Support packet GUI shift and keyboard clicks
Keviro May 26, 2026
46df20c
Fix packet GUI close handling for dialogs
Keviro May 26, 2026
60209d1
fix packet GUI pickup clicks
Keviro May 26, 2026
a0b4e01
fix offhand swap in packet GUIs
Keviro May 26, 2026
5775733
optimize packet GUI targeted repairs
Keviro May 26, 2026
38a6e96
optimize packet GUI repairs and async packet sending
Keviro May 26, 2026
6e6e484
offload packet item conversion
Keviro May 26, 2026
fdceae4
Fix packet GUI outside click handling
Keviro May 30, 2026
e466ecf
Add guarded native packet GUI sender
Keviro May 30, 2026
c564121
Support packet GUI player-inventory clicks
Keviro May 30, 2026
78c345f
Fix bottom shift-click packet repair
Keviro May 30, 2026
e262467
test(packet): add packetevents test deps and pin slot mapping behaviour
Keviro Jul 28, 2026
8703ae2
fix(packet): fail closed on non-pickup slot -999 clicks
Keviro Jul 28, 2026
eb03cb7
fix(packet): guard window ids and close real menus on open
Keviro Jul 28, 2026
443e77d
fix(packet): probe native sender capabilities instead of pinning one …
Keviro Jul 28, 2026
e29ff4b
fix(packet): harden session lifecycle and scheduling
Keviro Jul 28, 2026
d73af8c
fix(packet): align packet click API with the Bukkit backend
Keviro Jul 28, 2026
a04ce3f
refactor(packet): drop dead cache, dedupe slot mapping, keep click-ev…
Keviro Jul 28, 2026
f154a75
style(packet): log levels, ApiStatus, formatting and docs
Keviro Jul 28, 2026
3bf6ef0
fix(packet): recognise outside clicks by click mode, not slot sentinel
Keviro Jul 28, 2026
d6758a4
build: fix the delegating task wrapper path and add publishToMavenLocal
Keviro Jul 28, 2026
569c838
style(packet): use pattern matching in SlotClickContext#normalizeOrigin
Keviro Jul 28, 2026
ae35f8c
refactor(packet): schedule through FoliaLib instead of hand-rolled re…
Keviro Jul 28, 2026
39a19c8
fix(packet): stop an outbound close packet from killing the GUI being…
Keviro Jul 28, 2026
891b7c0
refactor(packet): drop the write-only viewer inventory mirror
Keviro Jul 28, 2026
415c2fd
refactor(packet): collapse the two-phase render plan into one
Keviro Jul 28, 2026
2a4b950
docs(packet): record the verification results from the live server run
Keviro Jul 28, 2026
22cc67b
fix(packet): send the close packet through the same transport as the …
Keviro Jul 28, 2026
12b6e6a
build: release as 1.0.5 and verify pull requests in CI
Jul 28, 2026
42df066
fix(packet): allocate fake window ids outside vanilla's range
Jul 28, 2026
8c692af
fix(packet): make the opening guard re-entrancy safe and surface aban…
Jul 28, 2026
bbbdae6
fix(packet): give the synthesized click event Bukkit slot semantics
Jul 29, 2026
846d399
perf(packet): mirror the player inventory with one packet and keep th…
Jul 29, 2026
713dd71
test(packet): extract the render coalescing state machine and cover it
Jul 29, 2026
8410373
docs(packet): correct the click contract and document the window id r…
Jul 29, 2026
192fc1f
Revert "perf(packet): mirror the player inventory with one packet"
Jul 29, 2026
a8f69a3
fix(packet): reuse the window when a view replaces one of the same shape
Jul 29, 2026
73a037f
fix(packet): settle the open-in-progress guard by ordering, not by ti…
Jul 29, 2026
89f629b
refactor(packet): stop the outbound sender from restyling items it do…
Jul 29, 2026
262d210
fix(packet): honour the graceful-degradation contract on load and on …
Jul 29, 2026
7f808d9
fix(packet): stop leaking viewers and stop touching state from the wr…
Jul 29, 2026
bd4e06b
docs(packet): record that no inventory open/close event fires for a p…
Jul 29, 2026
96518d8
build: make the Gradle wrapper executable
Jul 29, 2026
6d4c9de
fix(packet): stop a single bad packet from disabling packet mode serv…
Jul 29, 2026
3349d85
fix(packet): match vanilla's middle-click rules and build the shipped…
Jul 29, 2026
276c1f2
test(packet): cover the bottom-click routing and correct the click co…
Jul 29, 2026
0dc7b72
fix(packet): detect a refused scheduler task on Folia, and document t…
Jul 29, 2026
8e591bd
fix(packet): honour setResult, report updated titles, and validate th…
Aug 8, 2026
d77bd65
build: ignore the generated target repository
Aug 8, 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
55 changes: 55 additions & 0 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Verify

on:
pull_request:
workflow_dispatch:

concurrency:
group: verify-${{ github.ref }}
cancel-in-progress: true

jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 25

- uses: gradle/actions/setup-gradle@v4

# gitpatcher commits every patch into the generated target repository, which git refuses to do
# without an identity.
- name: Configure git identity
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"

- name: Apply patches
run: ./gradlew applyPatches --stacktrace

# shadowJar as well as the tests: `test` never builds the artifacts that actually ship, so a break in
# the javadoc or shadow configuration would otherwise only surface in the release job on master.
# spotless stays out while palantir-java-format is broken on JDK 25.
- name: Test and assemble
run: ./gradlew testPatched shadowJar --stacktrace

# A patch that no longer round-trips is the fork's easiest mistake: hand-editing patches/, or editing
# the target repository without regenerating. Both produce a tree that builds locally and cannot be
# reproduced from the committed patches.
- name: Check that the patches are up to date
run: |
./gradlew makePatches --stacktrace
# Compared against HEAD, not the index: makePatches stages the patches it regenerates, so a plain
# `git diff -- patches` compares them against themselves and can never fail.
if ! git diff --quiet HEAD -- patches; then
echo "::error::patches/ is out of sync with the target repository. Run './gradlew makePatches' and commit the result."
git diff --stat HEAD -- patches
exit 1
fi
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
.gradle
build/

# The target repository applyPatches generates from upstream + patches/. It carries its own .git, so a
# `git add -A` here does not skip it - it adds it as a gitlink, with a warning and successfully.
/inventory-framework/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
Expand Down
28 changes: 22 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,32 @@ val isWindows = System.getProperty("os.name")
.lowercase()
.contains("windows")

val gradlew = if (isWindows) "gradlew.bat" else "./gradlew"
// The wrapper is addressed by absolute path: `cmd /c gradlew.bat` resolves against PATH rather than the
// task's workingDir and fails with "gradlew.bat is either misspelled or could not be found".
val targetDir = layout.projectDirectory.dir("inventory-framework")
val gradlew = targetDir.file(if (isWindows) "gradlew.bat" else "gradlew").asFile.absolutePath

listOf("shadowJar", "publish").forEach { taskName ->
// `test` is already taken by the `java` plugin on this project, so the delegating task carries a distinct
// name. It is what CI runs to verify a pull request.
val delegatedTasks = mapOf(
"shadowJar" to "shadowJar",
"publish" to "publish",
"publishToMavenLocal" to "publishToMavenLocal",
"testPatched" to "test",
)

delegatedTasks.forEach { (taskName, delegate) ->
tasks.register<Exec>(taskName) {
group = if (taskName == "shadowJar") "build" else "publishing"
description = "Runs './gradlew $taskName' inside inventory-framework."
group = when (taskName) {
"shadowJar" -> "build"
"testPatched" -> "verification"
else -> "publishing"
}
description = "Runs './gradlew $delegate' inside inventory-framework."

dependsOn("applyPatches")
workingDir = layout.projectDirectory.dir("inventory-framework").asFile
val args = listOf(taskName)
workingDir = targetDir.asFile
val args = listOf(delegate)

if (isWindows) {
commandLine("cmd", "/c", gradlew, *args.toTypedArray())
Expand Down
269 changes: 269 additions & 0 deletions docs/packet-gui-backend.md

Large diffs are not rendered by default.

113 changes: 113 additions & 0 deletions docs/testing-with-surf-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Einen Branch-Build in surf-api testen

`surf-inventory-framework` wird nicht direkt deployed — surf-api zieht es als Maven-Dependency und shaded es
beim `shadowJar` nach `dev.slne.surf.api.libs.devnatan.inventoryframework`. Um einen Branch dieses Forks auf
einem Server zu testen, muss er also erst als Artefakt vorliegen, das surf-api auflösen kann.

```
surf-inventory-framework surf-api
7 Module, group Katalog: inventory-framework = "1.0.4"
dev.slne.forks.inventoryframework → inventory-framework-platform-paper
Version aus root gradle.properties → inventory-framework-platform-bukkit
│ │
│ publishToMavenLocal │ shadowJar relocated
▼ ▼
~/.m2 ──────────────────────────► surf-api-paper-server-*-all.jar
```

## Ablauf

**1. Fork lokal publizieren**

`gradle.properties` im Fork-Root trägt die Version. Für Branch-Tests eine SNAPSHOT-Version verwenden —
Gradle cached Release-Versionen aus mavenLocal und würde beim zweiten Durchlauf das alte Jar nehmen.

```properties
version=1.0.5-packet-guis-SNAPSHOT
```

Dann vom Fork-Root aus:

```bash
./gradlew publishToMavenLocal
```

Das führt intern `applyPatches` aus und publiziert alle sieben Module nach `~/.m2`. Prüfen:

```bash
ls ~/.m2/repository/dev/slne/forks/inventoryframework/inventory-framework-platform-bukkit/
```

**2. surf-api dagegen bauen**

```bash
cd ../surf-api
./gradlew :surf-api-paper:surf-api-paper-server:shadowJar \
-PinventoryFramework.localVersion=1.0.5-packet-guis-SNAPSHOT
```

Die Property schaltet in `buildSrc/src/main/kotlin/core-convention.gradle.kts` zwei Dinge frei:
`mavenLocal()` als Repository und eine `resolutionStrategy`, die jede Dependency der Gruppe
`dev.slne.forks.inventoryframework` auf diese Version zwingt. Der Katalogeintrag bleibt unangetastet.
Ohne die Property ist der Block inert — die Änderung ist also commit-fähig und beeinflusst CI nicht.

Zur Kontrolle loggt jedes Projekt beim Konfigurieren:

```
[surf] :surf-api-paper:surf-api-paper-server: resolving dev.slne.forks.inventoryframework
from mavenLocal at 1.0.5-packet-guis-SNAPSHOT
```

Das fertige Jar liegt unter `surf-api-paper/surf-api-paper-server/build/libs/`.

Wer die Property nicht jedes Mal tippen will, legt sie in eine lokale, nicht eingecheckte
`gradle.properties` in surf-api.

**3. Auf dem Server aktivieren**

Das Packet-Backend ist opt-in:

```
-Dinventory-framework.gui-backend=packet
```

PacketEvents muss installiert sein und vor InventoryFramework laden. Details und die
Verifikations-Checkliste stehen in `docs/packet-gui-backend.md`.

## Iterieren

Nach jeder Codeänderung im Fork. **Zuerst `makePatches`** — `publishToMavenLocal` hängt an `applyPatches`,
und das setzt das generierte Repository hart auf die *committeten* Patches zurück. Eine Änderung, die nur im
generierten Repo committet und noch nicht in einen Patch geschrieben wurde, geht dabei verloren, und der Build
läuft grün durch, weil er die alten Quellen baut. Nicht committete Änderungen im Arbeitsverzeichnis lassen
`applyPatches` dagegen mit einem Checkout-Fehler abbrechen.

```bash
cd surf-inventory-framework
# Änderung in den Ziel-Commit falten und den Patch neu schreiben:
(cd inventory-framework && git add -A && git commit --amend --no-edit --author="Keviro <keviro@slne.dev>")
./gradlew makePatches
perl -i -pe 's/\r\n/\n/g' patches/0006-*.patch # makePatches schreibt unter Windows CRLF

./gradlew publishToMavenLocal
cd ../surf-api && ./gradlew :surf-api-paper:surf-api-paper-server:shadowJar \
-PinventoryFramework.localVersion=1.0.5-packet-guis-SNAPSHOT
```

Weil die Version ein SNAPSHOT ist, zieht Gradle das neu publizierte Jar ohne
`--refresh-dependencies`.

## Vor dem Merge zurückdrehen

- `gradle.properties` im Fork: SNAPSHOT-Suffix entfernen, also zurück auf eine Release-Version.
- Die surf-api-Änderung kann bleiben (ohne Property inert) oder wird zurückgenommen.
- Der Katalogeintrag `inventory-framework` in surf-api wird erst auf die neue Version gehoben, wenn der
Fork regulär nach `reposilite.slne.dev/releases` publiziert wurde.

## Warum kein Composite Build

`includeBuild("../surf-inventory-framework/inventory-framework")` mit Dependency-Substitution wäre schneller
zu iterieren, würde aber das Projekt statt des publizierten Artefakts einbinden. Das Paper-Modul shaded beim
Publizieren das Bukkit-Modul mit hinein; eine Substitution umgeht das und baut damit etwas anderes als das,
was produktiv ausgeliefert wird. Für einen Test, der genau die produktive Artefaktform prüfen soll, ist der
mavenLocal-Umweg das ehrlichere Verfahren.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
kotlin.code.style=official
group=dev.slne.forks.inventoryframework
version=1.0.4
version=1.0.5
Empty file modified gradlew
100644 → 100755
Empty file.
Loading
Loading