Mod Bridge
The client-mod bridge — the server half of the button that a client mod draws inside the chest / furnace / barrel screen.
Why the button lives in a client mod
A container screen in Minecraft has exactly as many slots as the container has, and they are all real storage. A server plugin cannot add a widget to it: the only server-side way to fake one is to replace the whole screen with a custom inventory, which risks losing or duplicating the player's items and fights every storage/sorting mod on the server. A client mod, by contrast, adds a button to the existing screen in a few lines and never touches the slots at all.
So the split is: the client mod draws and clicks, the server owns the truth. This class is that server side, and it is deliberately usable by any mod — the protocol is plain text on a namespaced channel, documented below, with no library to link against.
Channel
papermc-despawned-items:targetsHandshake. A mod says hello; the server answers with what it will allow. This is what lets a mod render a real interface instead of guessing — and lets it hide that interface entirely on a server that does not want it.
→ HELLO <client-protocol-version>
← WELCOME <server-protocol-version> <capability> <capability> …
← UNAVAILABLE <reason> # server has client-mod support off, or you lack permissionServer → client (state):
TARGET <world> <x> <y> <z> <owner-uuid> <enabled> <priority> <contraband>
ABSENT <world> <x> <y> <z> # that block is not a despawn target
DENIED <world> <x> <y> <z> <reason> # the request was refused, with a human reasonClient → server (requests; every one is re-validated server-side):
QUERY <world> <x> <y> <z> # what is the state of this block?
MARK <world> <x> <y> <z> # register it as a despawn target
UNMARK <world> <x> <y> <z> # unregister it
TOGGLE <world> <x> <y> <z> # flip enabled/disabled, keeping the registration
PRIORITY <world> <x> <y> <z> <n> # set draw weight 1–10
CONTRABAND <world> <x> <y> <z> <bool> # opt this target in or out of receiving banned itemsThe server owner is in charge
Every one of these is refused unless both targets.client-mod.enabled and the player's despi.client permission allow it — see ClientAccess. A server that wants nothing to do with client mods switches one config key and the protocol goes silent; /despi keeps working exactly as before, for everyone, on any client.
Trust model
Nothing the client sends is trusted. Every request re-checks, server-side: that the sender has the permission, that the block is close enough to actually be interacting with it, that the player owns the target (or is elevated), and — for MARK — that they are under their configured location limit. A modified client can therefore do nothing through this channel that it could not already do by typing the commands.
Conflict and synergy
The channel name is namespaced to this plugin, so it cannot collide. Nothing is ever sent to a client that has not registered the channel, so vanilla and unmodded-client players are completely unaffected. And because the protocol is text on an open channel, a different mod — a storage manager, a minimap, an admin HUD — can implement the same three verbs and interoperate without this plugin knowing it exists.
Functions
Announces a change to every listening player.
Encodes one target as the documented TARGET … line.
Encodes "this block is not a despawn target".
Parses and executes one client request. Split out from onPluginMessageReceived so tests drive it directly without a network stack.
Sends whatever the current truth is for location: a TARGET line or an ABSENT one.
Tells player the state of one target.