From fe99f3b3d8e6669da3b855e9b25b47e511bc09ce Mon Sep 17 00:00:00 2001 From: Lukas Jost Date: Sat, 17 Oct 2026 22:11:25 +0200 Subject: [PATCH] feat(catalog): publish versioned permission manifest --- paper/build.gradle.kts | 8 + .../META-INF/grounds/permissions.index | 1 + .../permissions/plugin-scene-editor.json | 466 ++++++++++++++++++ .../scene/editor/paper/PaperPackagingTest.kt | 34 ++ 4 files changed, 509 insertions(+) create mode 100644 paper/src/main/resources/META-INF/grounds/permissions.index create mode 100644 paper/src/main/resources/META-INF/grounds/permissions/plugin-scene-editor.json diff --git a/paper/build.gradle.kts b/paper/build.gradle.kts index 3b36e5d..ac30852 100644 --- a/paper/build.gradle.kts +++ b/paper/build.gradle.kts @@ -64,3 +64,11 @@ publishing { } } } + +// Keep the publishing artifact version with its catalog when bundled into a host JAR. +tasks.named("processResources") { + inputs.property("permissionManifestVersion", project.version.toString()) + filesMatching("META-INF/grounds/permissions/*.json") { + expand("version" to project.version.toString()) + } +} diff --git a/paper/src/main/resources/META-INF/grounds/permissions.index b/paper/src/main/resources/META-INF/grounds/permissions.index new file mode 100644 index 0000000..21e1096 --- /dev/null +++ b/paper/src/main/resources/META-INF/grounds/permissions.index @@ -0,0 +1 @@ +META-INF/grounds/permissions/plugin-scene-editor.json diff --git a/paper/src/main/resources/META-INF/grounds/permissions/plugin-scene-editor.json b/paper/src/main/resources/META-INF/grounds/permissions/plugin-scene-editor.json new file mode 100644 index 0000000..e89a63c --- /dev/null +++ b/paper/src/main/resources/META-INF/grounds/permissions/plugin-scene-editor.json @@ -0,0 +1,466 @@ +{ + "source": "plugin-scene-editor", + "permissions": [ + { + "key": "grounds.scene", + "label": "Use the scene editor", + "description": "Use the scene editor.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.edit", + "label": "Create and edit scene elements", + "description": "Create and edit scene elements.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.create", + "label": "Create a scene document", + "description": "Create a scene document.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.info", + "label": "Inspect an open scene", + "description": "Inspect an open scene.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.validate", + "label": "Validate an open scene", + "description": "Validate an open scene.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.reload", + "label": "Reload a scene from disk", + "description": "Reload a scene from disk.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.history", + "label": "Inspect scene history", + "description": "Inspect scene history.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.undo", + "label": "Undo scene edits", + "description": "Undo scene edits.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.redo", + "label": "Redo scene edits", + "description": "Redo scene edits.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.tool.give", + "label": "Receive the scene editor tool", + "description": "Receive the scene editor tool.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.save", + "label": "Save scene changes to the current build world", + "description": "Save scene changes to the current build world.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.recovery", + "label": "Recover or export invalid scene files", + "description": "Recover or export invalid scene files.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.recovery.backup-and-create", + "label": "Back up invalid source and create a scene", + "description": "Back up invalid source and create a scene.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.recovery.export", + "label": "Export scene diagnostics", + "description": "Export scene diagnostics.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.recovery.discard-and-reload", + "label": "Discard local edits after confirmation", + "description": "Discard local edits after confirmation.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.catalogs.status", + "label": "Inspect scene catalog status", + "description": "Inspect scene catalog status.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.lease.override", + "label": "Take over an editor lease", + "description": "Take over an editor lease.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.lease.status", + "label": "Inspect an editor lease", + "description": "Inspect an editor lease.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.lease.release", + "label": "Release an owned editor lease", + "description": "Release an owned editor lease.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.prop.list", + "label": "List props", + "description": "List props.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.prop.create", + "label": "Create props", + "description": "Create props.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.prop.select", + "label": "Select props", + "description": "Select props.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.prop.position.set", + "label": "Set prop position", + "description": "Set prop position.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.prop.position.here", + "label": "Place a prop at the editor", + "description": "Place a prop at the editor.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.prop.position.add", + "label": "Offset prop position", + "description": "Offset prop position.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.prop.rotation.set", + "label": "Set prop rotation", + "description": "Set prop rotation.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.prop.rotation.add", + "label": "Offset prop rotation", + "description": "Offset prop rotation.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.prop.scale.set", + "label": "Scale props", + "description": "Scale props.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.prop.clone", + "label": "Clone props", + "description": "Clone props.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.prop.remove", + "label": "Remove props", + "description": "Remove props.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.list", + "label": "List NPCs", + "description": "List NPCs.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.create", + "label": "Create NPCs", + "description": "Create NPCs.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.select", + "label": "Select NPCs", + "description": "Select NPCs.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.position.set", + "label": "Set NPC position", + "description": "Set NPC position.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.position.here", + "label": "Place an NPC at the editor", + "description": "Place an NPC at the editor.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.position.add", + "label": "Offset NPC position", + "description": "Offset NPC position.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.rotation.set", + "label": "Set NPC rotation", + "description": "Set NPC rotation.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.rotation.add", + "label": "Offset NPC rotation", + "description": "Offset NPC rotation.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.scale.set", + "label": "Scale NPCs", + "description": "Scale NPCs.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.label.set", + "label": "Change NPC labels", + "description": "Change NPC labels.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.action.set", + "label": "Set parameterless catalog actions on NPC triggers", + "description": "Set parameterless catalog actions on NPC triggers.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.collision.set", + "label": "Set NPC collision mode", + "description": "Set NPC collision mode.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.collision.strength", + "label": "Set NPC bounce strength", + "description": "Set NPC bounce strength.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.collision.distance", + "label": "Set approximate NPC bounce distance", + "description": "Set approximate NPC bounce distance.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.clone", + "label": "Clone NPCs", + "description": "Clone NPCs.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + }, + { + "key": "grounds.scene.npc.remove", + "label": "Remove NPCs", + "description": "Remove NPCs.", + "supportedScopes": [ + "GLOBAL", + "SERVER_TYPE", + "SERVER" + ] + } + ], + "sourceVersion": "${version}" +} diff --git a/paper/src/test/kotlin/gg/grounds/scene/editor/paper/PaperPackagingTest.kt b/paper/src/test/kotlin/gg/grounds/scene/editor/paper/PaperPackagingTest.kt index da145bc..fdb5132 100644 --- a/paper/src/test/kotlin/gg/grounds/scene/editor/paper/PaperPackagingTest.kt +++ b/paper/src/test/kotlin/gg/grounds/scene/editor/paper/PaperPackagingTest.kt @@ -5,8 +5,42 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test +import org.yaml.snakeyaml.Yaml class PaperPackagingTest { + @Test + fun `catalog includes every declared Bukkit permission with the artifact version`() { + JarFile(System.getProperty("paper.shadowJar")).use { jar -> + fun read(name: String) = + jar.getInputStream(requireNotNull(jar.getJarEntry(name))).bufferedReader().use { + it.readText() + } + val descriptor = Yaml().load>(read("plugin.yml")) + val resource = "META-INF/grounds/permissions/plugin-scene-editor.json" + assertEquals( + listOf(resource), + read("META-INF/grounds/permissions.index") + .lineSequence() + .filter { it.isNotBlank() } + .toList(), + ) + // Parse raw maps so dotted permission names stay literal, rather than + // being converted into Bukkit configuration paths. + val catalog = Yaml().load>(read(resource)) + assertEquals("plugin-scene-editor", catalog["source"]) + assertEquals(descriptor["version"], catalog["sourceVersion"]) + @Suppress("UNCHECKED_CAST") + val entries = catalog["permissions"] as List> + val declared = (descriptor["permissions"] as Map<*, *>).keys + assertEquals(declared, entries.map { it["key"] }.toSet()) + entries.forEach { entry -> + assertTrue((entry["label"] as String).isNotBlank()) + assertTrue((entry["description"] as String).isNotBlank()) + assertEquals(listOf("GLOBAL", "SERVER_TYPE", "SERVER"), entry["supportedScopes"]) + } + } + } + @Test fun `shadow jar embeds the public api and required scene dependencies once`() { val (entries, catalogVersion) =