Context values and the Metadata system provide a type-safe way to share and persist data within the ability system.
The metadata system allows you to attach data to various holders (AbilityMetadataHolder), such as Players or even the Ability itself.
Keys are unique identifiers for a piece of data, linked to a specific type.
public static final AbilityMetadataKey<Integer> KILL_COUNT = new AbilityMetadataKey<>("kill_count");Any AbilityMetadataHolder can store these values.
// Setting a value
ability.getMetadata(player).set(KILL_COUNT, 10);
// Reading a value (with optional default)
int kills = ability.getMetadata(player).getOrDefault(KILL_COUNT, 0);AbilityActionValue is a wrapper used to provide values to Actions. These can be:
- Static: A fixed value (e.g.,
10.0). - Dynamic: Values pulled from the context, metadata, or configuration.
This allows the same Action class to behave differently based on how it is bound.
The AbilityActionContext is passed to every restriction and action. It contains:
- The Trigger Context: The raw data from the event (e.g., who was hit, how much damage).
- The Ability Instance: Access to the ability's configuration and metadata.
Most implementations of AbilityActionContext implement AbilityPlayerContext, which also provides access to the AbilityPlayer and Player objects.
private final AbilityActionValue<AbilityPlayerContext, Integer> value = (action) -> action.getAbility().getMetadata(action.getContext().getBukkitPlayer()).getOrDefault(LEVEL_KEY, 1);
@Override
public void handle(AbilityActionContext<AbilityPlayerContext> context) {
// Get the player
Player player = context.getContext().getBukkitPlayer();
// Get metadata from the player
int level = value.getValue(context);
}- Type Safety:
AbilityMetadataKeyensures you don't cast to the wrong type. - Persistence: The metadata system can be hooked into a database to save values across sessions.
- Decoupling: Different plugins can share data via keys without knowing about each other's implementation.