# GriefPrevention3D v18.2.11
**Wiki:** https://github.com/castledking/GriefPrevention3D/wiki
## Fixes
### Dynamically registered commands failing with "Unknown or incomplete command"
This resolves the `/acb` issue carried as a known issue in v18.2.10, where the command
produced a client-side **"Unknown or incomplete command"** error with **nothing at all
in the console** — no exception, no warning, no permission denial.
`registerDynamicStandaloneCommand` and `registerDynamicRootCommand` registered commands
by reflecting into `SimpleCommandMap`'s `knownCommands` map and calling `put()` directly:
```java
knownCommands.remove(lowerName);
knownCommands.put(lowerName, dynamicCommand);
knownCommands.put(prefix + ":" + lowerName, dynamicCommand);
```
On Paper (and its forks), commands injected this way never pass through the server's
Brigadier command registration, so they are absent from the **client's** command tree.
The client then rejects the command locally and never sends it to the server at all —
which is exactly why the console stayed silent. `CommandMap.register()` performs the
Brigadier-aware registration; a raw map `put()` does not.
This failure mode was already known in the codebase: `syncShapedCommandRegistration`
carries the comment *"Use CommandMap.register() so the implementation (Paper/Brigadier)
stays consistent with plugin.yml aliases. Raw knownCommands.put() can leave
/shapedclaim broken…"* — that call site had been fixed, but the dynamic standalone and
dynamic root command paths were still using raw puts.
**Fix:** both paths now call `CommandMap.register()` first. Reflection is used only as a
fallback when the plain command name is genuinely already taken, in which case the
displaced command is still recorded so `unregisterAllDynamicCommands()` can restore it
(behavior added in v18.2.9). When the name is taken and the map cannot be read, a
warning is logged noting the command is still reachable as
`/griefprevention3d:<command>` instead of failing silently.
### Command map reflection broken on server forks (DivineMC, Purpur, etc.)
Two reflection lookups in `UnifiedCommandHandler` assumed an exact class layout and
threw on any server whose implementation classes sit deeper in the hierarchy:
1. `getCommandMap()` called `Bukkit.getServer().getClass().getDeclaredField("commandMap")`
— a `getDeclaredField` lookup on the *leaf* runtime class. A fork that subclasses
`CraftServer` has no such declared field on the subclass, so this returned `null` and
dynamic registration was skipped entirely.
2. All three `knownCommands` lookups used
`map.getClass().getSuperclass().getDeclaredField("knownCommands")`, assuming the field
lives exactly one level up. That holds for vanilla CraftBukkit
(`CraftCommandMap extends SimpleCommandMap`) but throws `NoSuchFieldException` on any
fork that inserts its own `CommandMap` subclass.
Notably, `GriefPrevention.findKnownCommandsMap()` already walked the full class hierarchy
correctly — `UnifiedCommandHandler` simply had not been brought in line with it.
**Fix:**
- `getCommandMap()` now prefers Paper's `Server#getCommandMap()` API (reflectively, so
Spigot compatibility is retained), and otherwise walks the server class hierarchy
instead of inspecting only the leaf class.
- Added a `findKnownCommands(CommandMap)` helper that walks the command map's class
hierarchy, mirroring `findKnownCommandsMap`. All three former single-level reflection
sites (dynamic standalone registration, dynamic root registration, and
`unregisterAllDynamicCommands`) now use it.
- `unregisterAllDynamicCommands()` logs a warning instead of proceeding when the command
map cannot be read.
These are the reflection assumptions most likely to break on a non-vanilla server jar,
and they fail in a way that produces no diagnostic output — hardening them removes a
class of silent, fork-specific command breakage.
## Notes
The plugin does **not** platform-detect between Bukkit and Fabric at runtime. The Fabric
support in `platforms/fabric-1.21.11` is a separate Gradle subproject producing a
separate artifact; those classes are not present in the Bukkit jar. The only runtime
class detection is feature detection for knockback handlers. A Paper-family fork is
always driven through the normal Bukkit code path.
## Files Changed
```
M gradle.properties
M pom.xml
M src/main/java/com/griefprevention/commands/UnifiedCommandHandler.java
```
Optional dependencies
QuickShop-Hikari
Any compatible version
VaultUnlocked
Any compatible version
PlaceholderAPI
Any compatible version
Developer information
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle.
maven { name = "Modrinth"; url = "https://api.modrinth.com/maven" }