Skip to content

API and Integration

TemporaryAttributeService

TemporaryAttributeService is used to attach time-limited temporary attributes to players. Typical scenarios: Buff/Debuff, skill bonuses, potion effects, and other attribute changes that need to "automatically disappear after a period of time".

Two Modes

ModeDescription
ADDStacks on existing attribute values. Same effectId repeated calls accumulate value and refresh expiry
SETDirectly overrides attribute value. Later writes override earlier ones, suitable for "force-setting an attribute to a fixed value"

Java API

java
// Get via AttributeService
TemporaryAttributeService tempService = attributeService.temporaryAttributeService();

// Stack temporary attribute: add 50 physical attack for 200 ticks (10 seconds)
tempService.add(player, "buff_atk", "physical_attack", 50.0, 200);

// Override temporary attribute: force physical defense to 100 for 100 ticks (5 seconds)
tempService.set(player, "override_def", "physical_defense", 100.0, 100);

// Remove temporary attribute
tempService.remove(player, "buff_atk");
MethodParametersDescription
add()player, effectId, attributeId, value, durationTicksStack mode. Same effectId + same attributeId accumulates value
set()player, effectId, attributeId, value, durationTicksOverride mode. Directly sets attribute value
remove()player, effectIdImmediately removes the temporary attribute with the specified effectId

effectId is the unique identifier for a temporary attribute, used to distinguish effects from different sources. For example, a buff skill uses "skill_buff_atk", a potion uses "potion_str" — they don't interfere with each other.

Usage in Action System

The companion attribute_add / attribute_set / attribute_remove actions let you operate temporary attributes directly in YAML config:

yaml
actions:
  # Stack 50 physical attack for 200 ticks
  - "attribute_add effect_id=buff_atk attribute=physical_attack value=50 duration_ticks=200"

  # Override physical defense to 100 for 100 ticks
  - "attribute_set effect_id=override_def attribute=physical_defense value=100 duration_ticks=100"

  # Remove specified effect
  - "attribute_remove effect_id=buff_atk"

Internal Mechanism

  • Expiry check runs on a dedicated daemon thread at 250ms intervals, not on the main thread
  • Temporary attribute changes immediately clear the player's combat snapshot cache and trigger attribute resync
  • ADD mode temporary attributes are stacked on top of equipment attributes via mergeValues during snapshot collection
  • SET mode temporary attributes directly override corresponding attribute values via overlayValues
  • Temporary attribute signatures participate in snapshot signature calculation, ensuring cache correctly invalidates on changes
  • On plugin shutdown (shutdown()), the cleanup thread is automatically stopped and all temporary attribute data is cleared

注意

Temporary attribute data exists only in memory and is not persisted. All temporary attributes are lost on server restart. For persistent buffs across restarts, use PDC attributes or other persistence solutions.

EmakiAttributeBridge

EmakiAttributeBridge is a bridge interface defined in CoreLib, implemented by EmakiAttribute via ServiceBackedEmakiAttributeBridge, and registered in Bukkit ServicesManager. Other modules (such as EmakiSkills) can query attributes and resource states through this interface.

java
public interface EmakiAttributeBridge {

    // Whether the bridge is available
    boolean available();

    // Read resource current value, returns -1 if not found
    double readResourceCurrent(Player player, String resourceId);

    // Read resource max value, returns -1 if not found
    double readResourceMax(Player player, String resourceId);

    // Consume resource, returns false if insufficient balance
    boolean consumeResource(Player player, String resourceId, double amount);

    // Read attribute value, returns 0 if not found
    double readAttributeValue(Player player, String attributeId);
}

Getting the Bridge Instance

java
RegisteredServiceProvider<EmakiAttributeBridge> provider =
    Bukkit.getServicesManager().getRegistration(EmakiAttributeBridge.class);
if (provider != null) {
    EmakiAttributeBridge bridge = provider.getProvider();
    if (bridge.available()) {
        double health = bridge.readResourceCurrent(player, "health");
    }
}

PdcAttributeApi

PdcAttributeApi is the public interface for PDC attribute read/write operations, also registered in Bukkit ServicesManager. External plugins can read and write attribute data on items through this interface.

java
public interface PdcAttributeApi {

    // Register an attribute source ID, returns whether it was newly registered
    boolean registerSource(String sourceId);

    // Unregister an attribute source ID
    void unregisterSource(String sourceId);

    // Check if a source ID is registered
    boolean isRegisteredSource(String sourceId);

    // Get all registered source IDs
    Set<String> registeredSources();

    // Write attribute payload to an item
    boolean write(ItemStack itemStack, PdcAttributePayload payload);

    // Convenience write method
    boolean write(ItemStack itemStack, String sourceId,
                  Map<String, Double> attributes, Map<String, String> meta);

    // Read attribute payload for a specific source
    PdcAttributePayload read(ItemStack itemStack, String sourceId);

    // Read all attribute payloads on an item
    Map<String, PdcAttributePayload> readAll(ItemStack itemStack);

    // Clear attribute payload for a specific source
    boolean clear(ItemStack itemStack, String sourceId);

    // Clear all attribute payloads on an item
    void clearAll(ItemStack itemStack);
}
PdcAttributeApi Usage Example
java
// Get API
PdcAttributeApi api = Bukkit.getServicesManager()
    .getRegistration(PdcAttributeApi.class).getProvider();

// Register source
api.registerSource("my_plugin");

// Write attributes
Map<String, Double> attributes = Map.of(
    "physical_attack", 50.0,
    "physical_crit_rate", 10.0
);
Map<String, String> meta = Map.of(
    "tier", "legendary"
);
api.write(itemStack, "my_plugin", attributes, meta);

// Read attributes
PdcAttributePayload payload = api.read(itemStack, "my_plugin");

// Clear attributes
api.clear(itemStack, "my_plugin");

AttributeContributionProvider

External plugins can implement the AttributeContributionProvider interface to inject additional attribute contributions during the attribute snapshot collection phase.

java
public interface AttributeContributionProvider {

    // Provider unique ID
    String id();

    // Priority, higher values are processed first
    int priority();

    // Collect attribute contributions for the specified entity
    Collection<AttributeContribution> collect(LivingEntity entity);
}

AttributeContribution is a simple record class:

java
public record AttributeContribution(
    String attributeId,    // Attribute ID
    double value,          // Contribution value
    String sourceId        // Source identifier
) {}

Built-in Providers

  • mythic_mob_attributes (priority=250): Reads attributes from MythicMobs mob configurations
  • mmoitems_attribute_mapping (priority=220): Maps attributes from MMOItems items

MythicMobs Integration

emaki_damage Mechanic

Triggers attribute damage calculation through the MythicMobs skill system.

yaml
# MythicMobs skill configuration example
Skills:
  - emaki_damage{damage=20;damage_type=spell;allow_critical=true} @target
ParameterTypeDefaultDescription
damage / basedoubleSkill powerBase damage value
damage_typeStringAttacker override or default typeDamage type ID
allow_critical / criticalbooleantrueWhether critical hits are allowed
allow_target_dodge / target_dodge / allow_dodge / dodgebooleanfalseWhether target dodge is allowed
calculate_target_defense / target_defense / calculate_defense / defensebooleantrueWhether to calculate target defense
trigger_mythic_on_damaged / trigger_on_damaged / mythic_on_damagedbooleanfalseWhether to trigger MythicMobs onDamaged

Mechanic aliases: emakiattribute_damage, attribute_damage

Note

allow_target_dodge defaults to false, meaning MythicMobs skills do not allow target dodge by default. To enable dodge determination, explicitly set allow_target_dodge=true.

emaki_attribute / attribute_resource Conditions

Check an entity's attribute values or resource state.

yaml
# Check attribute value
Conditions:
  - emaki_attribute{attribute=physical_attack;operator=>=;value=100} true

# Check resource
Conditions:
  - attribute_resource{resource=mana;field=current;operator=>=;value=50} true

# Range check
Conditions:
  - emaki_attribute{attribute=dodge_chance;operator=between;value=10;value_2=50} true
ParameterTypeDefaultDescription
attribute / idStringAttribute ID
resourceString""Resource ID (enters resource mode when set)
fieldStringAttribute mode value, resource mode current_valueResource field
operator / compareString>=Comparison operator
value / mindouble0Comparison value
value_2 / maxdoubleSame as valueRange upper bound (between operator only)

Supported operators: >, >=, <, <=, != (<>, ne), between

Resource field options: current (current_value, value), max (current_max), default (default_max), bonus (bonus_max), percent

Condition aliases: emakiattribute_attribute, attribute_value, attribute_resource

MythicMobs Mob Attribute Configuration

Define Emaki attributes directly in MythicMobs mob configurations:

yaml
# MythicMobs mob configuration
ExampleMob:
  Type: ZOMBIE
  Display: '&c精英僵尸'
  Health: 100
  EmakiAttribute:
    - "physical_attack: 25"
    - "physical_defense: 10"
    - "physical_crit_rate: 15"
    - "dodge_chance: 5"

Mob Attributes

Each entry in the EmakiAttribute list follows the format attribute_name: value, supporting both display_name and id. Values support mathematical expressions (parsed via exp4j).

MMOItems Integration

When the MMOItems plugin is present, EmakiAttribute automatically enables the MMOItems bridge:

  • Reads MMOItems item Stat values via the public MMOItems API
  • Maps to Emaki attributes based on the mmoitems_stat_id field in attribute definitions
  • Automatically intercepts MMOItems damage events (SpecialWeaponAttackEvent), redirecting them to the Emaki damage system
  • Supports projectile snapshot mechanism

MMOItems Bridge Improvement (3.3.0)

Starting from 3.3.0, the MMOItems bridge uses direct MMOItems public API calls instead of reflection. This means the bridge is less likely to break when MMOItems updates. If you previously encountered bridge errors after MMOItems updates, upgrading to 3.3.0 should resolve the issue.

EmakiAttributeDamageEvent

A custom damage event fired after damage calculation completes but before the damage is actually applied. Other plugins can listen to this event to modify the final damage or cancel it.

java
@EventHandler
public void onEmakiDamage(EmakiAttributeDamageEvent event) {
    LivingEntity attacker = event.getAttacker();
    LivingEntity target = event.getTarget();
    String damageType = event.getDamageTypeId();
    double baseDamage = event.getBaseDamage();
    double finalDamage = event.getFinalDamage();
    boolean critical = event.isCritical();

    // Modify final damage
    event.setFinalDamage(finalDamage * 1.5);

    // Or cancel damage
    // event.setCancelled(true);
}

Readable Fields

MethodReturn TypeDescription
getAttacker()LivingEntityAttacker
getTarget()LivingEntityTarget
getProjectile()ProjectileProjectile (may be null)
getDamageTypeId()StringDamage type ID
getBaseDamage()doubleBase damage value
getFinalDamage()doubleFinal damage value
isCritical()booleanWhether it was a critical hit
getRoll()doubleCrit random number
getCause()DamageCauseBukkit damage cause
getContext()MapContext variables (immutable copy)
getVariables()DamageContextVariablesFull variable object
getDamageContext()DamageContextFull damage context
getDamageResult()DamageResultFull damage result

Modifiable Fields

MethodDescription
setFinalDamage(double)Modify the final damage value
setCancelled(boolean)Cancel the damage

CombatDebugService

Combat debug service, controlled via the /ea debug command. When enabled, outputs detailed damage calculation logs to the console.

Debug logs include:

  • Damage context description (attacker, target, projectile, damage cause, damage type)
  • Attacker and target attribute snapshots
  • Calculation values for each stage
  • Critical hit determination result
  • Final damage value

Debug Log Format

[CombatDebug][PHASE] message

Logs are prefixed with [CombatDebug], where PHASE indicates the current calculation phase (e.g., TRACE, STAGE, etc.).

Released under the GPL-3.0 License