Overview
Create typed behavior for a player or process and supply its implementation.
Create a capability when test authors need another typed action or observation. Start by choosing its owner, then choose the mechanism that supplies the behavior.
| Behavior | Public contract | Provider |
|---|---|---|
| Player behavior using observations or other capabilities | PlayerCapability | PlayerCapabilityProvider |
| Player actions through the native protocol client | PlayerCapability | ProtocolPlayerCapabilityProvider |
| Server or proxy behavior through its agent, independent of a player | ProcessCapability | AgentProcessCapabilityProvider |
ProcessCapability belongs to the global API. A custom RunningProcess implementation can
supply it through the inherited CapabilityOwner lookup contract without an agent.
AgentProcessCapabilityProvider is the factory used when an agent supplies that process behavior.
For example, PluginCommands could belong to a player and use Messages to execute commands with
that player's permissions. A process capability could read your plugin's server-wide state
through a native handler. Choose the owner by who can normally do or know the thing, not by how you
implement it: what a client can do or see belongs to the player, and what a server or proxy can do or
know belongs to the process. The built-in Players capability is process-owned because a server knows
who is on it; Alice's client does not.
Create the example capability
This section builds a process-owned Echo capability backed by an agent. It proves that your
packaged native handler can run in the selected process and return a result, without creating a
simulated player. Replace its contract with a useful domain operation after the installation
path works.
- Define the public contract.
- Define its operation contract and handler, then build the agent provider.
- Install the handler and package and test the extension through a consumer build.
A protocol adapter supplies player behavior carried by packets. That path does not require completing the agent example first.
Arrange the artifacts
| Example artifact | Owns | Anvil API dependency |
|---|---|---|
echo-api | Echo and its public models | api |
echo-operations | Shared request/response descriptors | agent-api |
echo-host | Process capability provider using a typed request channel | capability-agent-api |
echo-agent | Handler loaded by the platform agent | agent-server-api |
echo | Consumer dependency wiring | The API and selected host implementation |
Your artifacts use your own Maven group and package namespace. The host and agent artifacts share
operation contracts through echo-operations, which exports agent-api for its descriptor
types. Host wiring derives a ChannelOperation from those descriptors and sends it through the
capability request channel. The host does not depend on the handler implementation. For packet behavior,
the protocol adapter occupies the host implementation role and may also supply worker-side code.
Anvil artifact IDs in this table use the Maven group me.whereareiam.anvil.
Declare dependencies and support
The runtime orders creation by declared predecessor capabilities. Access to other capabilities is
restricted to those dependencies, within the same owner. A player capability can declare Messages
and obtain it through context.requireCapability(Messages.class). A process capability backed by
an agent can declare Dispatch and obtain it through its provider context.
A required dependency the owner lacks keeps the capability from being created, and retrieving it
then names the missing dependency. A capability that only uses another when it is there declares it
as optional instead, with .optionalCapability(Type.class): it is created after that capability when
the owner has it, and context.findCapability(Type.class) is empty when the owner does not. The
built-in Dialogs uses Commands this way.
All capability providers return a CapabilityDescriptor from descriptor(). It contains provider
identity, contract version, and required and optional capability types. The shared capability-api owns this
contract, generic provider/context interfaces, typed request descriptors/channels, and neutral player
contracts under capability.api.player. Use PlayerCapabilityProvider when the implementation
needs only player identity/version, observations, declared dependencies, and cleanup.
Protocol-backed player providers use capability-protocol-api, and their native worker extensions implement
WorkerExtension from protocol-api, which it includes.
ProtocolPlayerCapabilityProvider names what it needs from the player's library: requiredProtocols(), the edition
protocols its worker extension speaks, such as JavaProtocol, and workerBacked() for an extension that needs no
edition protocol. Only a capability that uses one library's own services restricts supportedLibraries(). Its
creation context supplies the protocol channel and the services of the player's protocol library. Composition skips
the provider and its dependents for players whose library lacks a required protocol, whose library runs no worker
extensions while the provider is worker-backed, or whose worker does not install its capability. Declare Session only when the implementation uses it.
Agent-backed process providers use capability-agent-api, which includes the shared
capability contracts. Its process context adds a scoped request channel for native agent work.
AgentProcessCapabilityProvider can restrict platforms with supportsPlatform(...).
Agent providers explains its scope and cleanup hooks.
For exact signatures, consult the
PlayerCapabilityProvider,
ProtocolPlayerCapabilityProvider
and AgentProcessCapabilityProvider
source Javadocs. These links use dev; select your release tag when checking a released version.
Implement an existing capability
An alternative implementation of Movement supplies the existing API's methods. Arrange the
installed wiring and protocol selection so exactly one provider contributes Movement.class to a
player. The same uniqueness rule applies to capabilities contributed to a process. Multiple matching
providers are an error.
Capability lookup uses the exact registered class. A Java interface extending another capability interface does not automatically register the parent type. There is no implicit augmentation of an already installed capability. To compose additional behavior, define a capability with explicit dependencies on the existing APIs.