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
| Mode | Description |
|---|---|
ADD | Stacks on existing attribute values. Same effectId repeated calls accumulate value and refresh expiry |
SET | Directly overrides attribute value. Later writes override earlier ones, suitable for "force-setting an attribute to a fixed value" |
Java API
// 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");| Method | Parameters | Description |
|---|---|---|
add() | player, effectId, attributeId, value, durationTicks | Stack mode. Same effectId + same attributeId accumulates value |
set() | player, effectId, attributeId, value, durationTicks | Override mode. Directly sets attribute value |
remove() | player, effectId | Immediately 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:
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
ADDmode temporary attributes are stacked on top of equipment attributes viamergeValuesduring snapshot collectionSETmode temporary attributes directly override corresponding attribute values viaoverlayValues- 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.
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
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.
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
// 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.
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:
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 configurationsmmoitems_attribute_mapping(priority=220): Maps attributes from MMOItems items
MythicMobs Integration
emaki_damage Mechanic
Triggers attribute damage calculation through the MythicMobs skill system.
# MythicMobs skill configuration example
Skills:
- emaki_damage{damage=20;damage_type=spell;allow_critical=true} @target| Parameter | Type | Default | Description |
|---|---|---|---|
damage / base | double | Skill power | Base damage value |
damage_type | String | Attacker override or default type | Damage type ID |
allow_critical / critical | boolean | true | Whether critical hits are allowed |
allow_target_dodge / target_dodge / allow_dodge / dodge | boolean | false | Whether target dodge is allowed |
calculate_target_defense / target_defense / calculate_defense / defense | boolean | true | Whether to calculate target defense |
trigger_mythic_on_damaged / trigger_on_damaged / mythic_on_damaged | boolean | false | Whether 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.
# 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| Parameter | Type | Default | Description |
|---|---|---|---|
attribute / id | String | — | Attribute ID |
resource | String | "" | Resource ID (enters resource mode when set) |
field | String | Attribute mode value, resource mode current_value | Resource field |
operator / compare | String | >= | Comparison operator |
value / min | double | 0 | Comparison value |
value_2 / max | double | Same as value | Range 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:
# 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_idfield 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.
@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
| Method | Return Type | Description |
|---|---|---|
getAttacker() | LivingEntity | Attacker |
getTarget() | LivingEntity | Target |
getProjectile() | Projectile | Projectile (may be null) |
getDamageTypeId() | String | Damage type ID |
getBaseDamage() | double | Base damage value |
getFinalDamage() | double | Final damage value |
isCritical() | boolean | Whether it was a critical hit |
getRoll() | double | Crit random number |
getCause() | DamageCause | Bukkit damage cause |
getContext() | Map | Context variables (immutable copy) |
getVariables() | DamageContextVariables | Full variable object |
getDamageContext() | DamageContext | Full damage context |
getDamageResult() | DamageResult | Full damage result |
Modifiable Fields
| Method | Description |
|---|---|
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] messageLogs are prefixed with [CombatDebug], where PHASE indicates the current calculation phase (e.g., TRACE, STAGE, etc.).