Damage System
The damage system in Hytale handles all aspects of entity damage, from damage calculation to knockback and visual effects. The system is built on the Entity Component System (ECS) architecture and uses events for damage processing.
Core Components
Section titled “Core Components”DamageDataComponent
Section titled “DamageDataComponent”Located in com.hypixel.hytale.server.core.entity.damage, this component tracks damage-related data for entities.
public class DamageDataComponent implements Component<EntityStore> { private Instant lastCombatAction; private Instant lastDamageTime; private WieldingInteraction currentWielding; private Instant lastChargeTime;}Fields:
lastCombatAction- Timestamp of the last combat-related actionlastDamageTime- Timestamp when the entity last took damagecurrentWielding- The current wielding interaction being usedlastChargeTime- Timestamp of the last charge action
This component is automatically added to all living entities via the DamageDataSetupSystem.
KnockbackComponent
Section titled “KnockbackComponent”Located in com.hypixel.hytale.server.core.entity.knockback, this component handles knockback effects applied to entities.
public class KnockbackComponent implements Component<EntityStore> { private Vector3d velocity; private ChangeVelocityType velocityType; private VelocityConfig velocityConfig; private DoubleList modifiers; private float duration; private float timer;}Fields:
velocity- The knockback velocity vectorvelocityType- How the velocity should be applied (Add or Set)velocityConfig- Configuration for velocity behaviormodifiers- List of multipliers applied to the knockbackduration- How long the knockback should be applied (0 for instant)timer- Internal timer for tracking knockback duration
Methods:
addModifier(double)- Add a multiplier to the knockbackapplyModifiers()- Apply all modifiers to the velocityincrementTimer(float)- Update the knockback timer
Damage Event System
Section titled “Damage Event System”Damage Class
Section titled “Damage Class”The Damage class (in com.hypixel.hytale.server.core.modules.entity.damage) represents a damage event and extends CancellableEcsEvent.
public class Damage extends CancellableEcsEvent implements IMetaStore<Damage> { private final float initialAmount; private int damageCauseIndex; private Source source; private float amount;}Fields:
initialAmount- The original damage amount before any modificationsdamageCauseIndex- Index of the damage cause in the asset registrysource- The source of the damage (entity, environment, command, etc.)amount- The current damage amount (can be modified by systems)
Meta Keys:
The damage system uses metadata to attach additional information:
HIT_LOCATION-Vector4d- The location where the hit occurredHIT_ANGLE-Float- The angle of the hitIMPACT_PARTICLES-Particles- Particle effects to play on impactIMPACT_SOUND_EFFECT-SoundEffect- Sound effect for the impactPLAYER_IMPACT_SOUND_EFFECT-SoundEffect- Player-specific impact soundCAMERA_EFFECT-CameraEffect- Camera shake/effect to applyDEATH_ICON-String- Icon to show in death messagesBLOCKED-Boolean- Whether the damage was blockedSTAMINA_DRAIN_MULTIPLIER-Float- Multiplier for stamina drainCAN_BE_PREDICTED-Boolean- Whether damage can be client-predictedKNOCKBACK_COMPONENT-KnockbackComponent- Knockback to apply
Damage Sources
Section titled “Damage Sources”Damage can come from various sources, defined by the Damage.Source interface:
EntitySource - Damage from another entity
public static class EntitySource implements Source { protected final Ref<EntityStore> sourceRef;}ProjectileSource - Damage from a projectile, extends EntitySource
public static class ProjectileSource extends EntitySource { protected final Ref<EntityStore> projectile;}EnvironmentSource - Damage from environment (fall, drowning, etc.)
public static class EnvironmentSource implements Source { private final String type;}CommandSource - Damage from server commands
public static class CommandSource implements Source { private final CommandSender commandSender; private final String commandName;}Damage Causes
Section titled “Damage Causes”Damage causes are defined as asset files and stored in the DamageCause asset registry.
DamageCause Asset
Section titled “DamageCause Asset”public class DamageCause implements JsonAssetWithMap<String, IndexedLookupTableAssetMap<String, DamageCause>> { protected String id; protected String inherits; protected boolean durabilityLoss; protected boolean staminaLoss; protected boolean bypassResistances; protected String damageTextColor; protected String animationId = "Hurt"; protected String deathAnimationId = "Death";}Properties:
id- Unique identifier for the damage causeinherits- Another damage cause to inherit properties fromdurabilityLoss- Whether this damage causes durability loss on armor/itemsstaminaLoss- Whether this damage drains staminabypassResistances- Whether this damage ignores resistance modifiersdamageTextColor- Color for damage numbers in the UIanimationId- Animation to play when hitdeathAnimationId- Animation to play on death from this damage
Built-in Damage Causes:
PHYSICAL- Melee/physical damagePROJECTILE- Projectile damageFALL- Fall damageDROWNING- Drowning damageSUFFOCATION- Suffocation damageOUT_OF_WORLD- Void damageENVIRONMENT- Generic environmental damageCOMMAND- Damage from commands
Damage Processing Pipeline
Section titled “Damage Processing Pipeline”The damage system uses a three-stage processing pipeline via system groups:
1. Gather Damage Group
Section titled “1. Gather Damage Group”Systems in this group create and gather damage events. This is where damage is initially applied to entities.
2. Filter Damage Group
Section titled “2. Filter Damage Group”Systems in this group modify or cancel damage events. This includes:
- FilterPlayerWorldConfig - Applies world config settings for players
- FilterNPCWorldConfig - Applies world config settings for NPCs
- FilterUnkillable - Prevents damage to unkillable entities
- PlayerDamageFilterSystem - Handles player-specific damage filtering
- WieldingDamageReduction - Reduces damage based on equipped items
- ArmorDamageReduction - Reduces damage based on armor
3. Inspect Damage Group
Section titled “3. Inspect Damage Group”Systems in this group handle the effects of damage after it’s been filtered. This includes:
- RecordLastCombat - Records combat statistics
- ApplyParticles - Spawns particle effects
- ApplySoundEffects - Plays sound effects
- HitAnimation - Triggers hit animations
- TrackLastDamage - Updates last damage time
- DamageArmor - Applies durability loss to armor
- DamageStamina - Drains stamina
- DamageAttackerTool - Applies durability loss to attacking tool
- PlayerHitIndicators - Shows hit indicators to players
Knockback System
Section titled “Knockback System”Knockback is processed after damage in the Inspect Damage Group.
Knockback Types
Section titled “Knockback Types”There are several types of knockback defined in the com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.combat package:
PointKnockback - Knockback away from a specific point DirectionalKnockback - Knockback in a specific direction ForceKnockback - Raw force-based knockback
Knockback Base Class
Section titled “Knockback Base Class”public abstract class Knockback { protected float force; protected float duration; protected ChangeVelocityType velocityType = ChangeVelocityType.Add; private VelocityConfig velocityConfig;
public abstract Vector3d calculateVector(Vector3d attackerPos, float yaw, Vector3d targetPos);}Properties:
force- The magnitude of the knockbackduration- How long to apply knockback (0 for instant)velocityType- Whether to add to or set velocity (Add/Set)velocityConfig- Advanced velocity configuration
Knockback Systems
Section titled “Knockback Systems”ApplyKnockback - Applies knockback to non-player entities
- Reads the
KnockbackComponent - Applies modifiers
- Adds velocity instruction to the entity’s
Velocitycomponent - Removes the component when duration expires
ApplyPlayerKnockback - Applies knockback to players with prediction support
- Similar to ApplyKnockback but handles client-side prediction
- Uses
KnockbackSimulationcomponent for predicted knockback - Controlled by
DO_SERVER_PREDICTIONflag
Knockback Reduction
Section titled “Knockback Reduction”Knockback can be reduced through:
- WieldingKnockbackReduction - Reduces knockback based on wielded item
- ArmorKnockbackReduction - Reduces knockback based on worn armor
Using the Damage System
Section titled “Using the Damage System”Creating a Damage Event
Section titled “Creating a Damage Event”// Create damage with an entity sourceDamage.EntitySource source = new Damage.EntitySource(attackerRef);DamageCause cause = DamageCause.getAssetMap().getAsset("Physical");Damage damage = new Damage(source, cause, 10.0f);
// Add metadatadamage.putMeta(Damage.HIT_LOCATION, hitLocation);damage.putMeta(Damage.HIT_ANGLE, hitAngle);
// Fire the damage eventstore.getEventBus().fire(damage, targetRef);Listening to Damage Events
Section titled “Listening to Damage Events”Create a system that extends DamageEventSystem:
public class MyDamageListener extends DamageEventSystem {
@Override public Query<EntityStore> getQuery() { // Return Query.any() to handle all entities, or a specific query return Query.any(); }
@Override public void handle(int index, ArchetypeChunk<EntityStore> chunk, Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer, Damage damage) { // Get reference to the damaged entity Ref<EntityStore> targetRef = chunk.getReferenceTo(index);
// Modify damage damage.setAmount(damage.getAmount() * 0.5f);
// Or cancel it damage.setCancelled(true); }}Getting Target and Source References
Section titled “Getting Target and Source References”When handling a damage event, you often need both the entity that received damage (target) and the entity that dealt damage (source).
Getting the Target (damaged entity):
@Overridepublic void handle(int index, ArchetypeChunk<EntityStore> chunk, Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer, Damage damage) { // The target is obtained from the chunk - this is the entity receiving damage Ref<EntityStore> targetRef = chunk.getReferenceTo(index);
// Get components from the target Player targetPlayer = store.getComponent(targetRef, Player.getComponentType());}Getting the Source (attacker entity):
The source is stored in the Damage object. Use damage.getSource() and check/cast to the appropriate source type:
@Overridepublic void handle(int index, ArchetypeChunk<EntityStore> chunk, Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer, Damage damage) { Ref<EntityStore> targetRef = chunk.getReferenceTo(index); Damage.Source source = damage.getSource();
// Check if damage came from an entity (player or mob) if (source instanceof Damage.EntitySource entitySource) { Ref<EntityStore> attackerRef = entitySource.getRef();
// Check if attacker is a player Player attackerPlayer = store.getComponent(attackerRef, Player.getComponentType()); if (attackerPlayer != null) { String attackerName = attackerPlayer.getPlayerRef().getUsername(); // Handle player-dealt damage } }
// Check if damage came from a projectile if (source instanceof Damage.ProjectileSource projectileSource) { Ref<EntityStore> shooterRef = projectileSource.getRef(); // Who shot it Ref<EntityStore> projectileRef = projectileSource.getProjectile(); // The projectile itself }
// Check if damage came from environment (fall, drowning, etc.) if (source instanceof Damage.EnvironmentSource envSource) { String damageType = envSource.getType(); // e.g., "fall", "drowning" }
// Check if damage came from a command if (source instanceof Damage.CommandSource cmdSource) { // Damage was dealt via server command }}Complete Example - Player vs Mob Combat:
@Overridepublic void handle(int index, ArchetypeChunk<EntityStore> chunk, Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer, Damage damage) { // Get target (who received damage) Ref<EntityStore> targetRef = chunk.getReferenceTo(index);
// Get source (who dealt damage) Damage.Source source = damage.getSource(); if (!(source instanceof Damage.EntitySource entitySource)) { return; // Not entity-to-entity damage } Ref<EntityStore> attackerRef = entitySource.getRef();
// Check if a player hit a mob Player attackerPlayer = store.getComponent(attackerRef, Player.getComponentType()); Player targetPlayer = store.getComponent(targetRef, Player.getComponentType());
if (attackerPlayer != null && targetPlayer == null) { // Player attacked a mob String playerName = attackerPlayer.getPlayerRef().getUsername(); attackerPlayer.sendMessage(Message.translation( String.format("You dealt %.1f damage!", damage.getAmount()))); }
if (attackerPlayer == null && targetPlayer != null) { // Mob attacked a player targetPlayer.sendMessage(Message.translation( String.format("A mob hit you for %.1f damage!", damage.getAmount()))); }}Registering in a Specific Damage Group
Section titled “Registering in a Specific Damage Group”By default, DamageEventSystem runs in the Inspect Damage Group (after damage is applied). To actually cancel or modify damage before it affects health, you must register in the Filter Damage Group:
public class MyDamageFilter extends DamageEventSystem {
@Override public SystemGroup<EntityStore> getGroup() { // Register in the Filter Damage Group to run BEFORE damage is applied return DamageModule.get().getFilterDamageGroup(); }
@Override public Query<EntityStore> getQuery() { return Query.any(); }
@Override public void handle(int index, ArchetypeChunk<EntityStore> chunk, Store<EntityStore> store, CommandBuffer<EntityStore> commandBuffer, Damage damage) { Ref<EntityStore> targetRef = chunk.getReferenceTo(index);
// Cancel damage - this WILL prevent health loss when in Filter group damage.setCancelled(true); }}Available damage groups via DamageModule.get():
getGatherDamageGroup()- Where damage events are createdgetFilterDamageGroup()- Where damage can be modified/cancelled before affecting healthgetInspectDamageGroup()- Where effects are applied after damage (default for DamageEventSystem)
Adding Knockback to Damage
Section titled “Adding Knockback to Damage”Damage damage = new Damage(source, cause, 10.0f);
// Create knockback componentKnockbackComponent knockback = new KnockbackComponent();knockback.setVelocity(new Vector3d(0, 1, 0)); // Upward knockbackknockback.setVelocityType(ChangeVelocityType.Add);knockback.setDuration(0.2f); // Apply for 0.2 seconds
// Add to damage metadatadamage.putMeta(Damage.KNOCKBACK_COMPONENT, knockback);Death System
Section titled “Death System”When an entity’s health reaches zero, the DeathComponent is added, triggering the death processing pipeline.
DeathComponent
Section titled “DeathComponent”public class DeathComponent implements Component<EntityStore>Death-related systems handle:
- ClearHealth - Resets health to zero
- ClearInteractions - Removes active interactions
- ClearEntityEffects - Removes status effects
- PlayerKilledPlayer - Awards kill credit
- DropPlayerDeathItems - Drops inventory on death
- KillFeed - Broadcasts death messages
- PlayerDeathScreen - Shows death screen to players
- DeathAnimation - Plays death animation
- CorpseRemoval - Removes the entity after a delay
Best Practices
Section titled “Best Practices”- Use the event system - Always fire damage through the event bus to allow other plugins to modify it
- Check cancellation - Respect the cancelled state of damage events
- Use appropriate sources - Choose the correct source type for your damage
- Add metadata - Include hit location, angle, and effects for better player feedback
- Consider knockback - Most damage types should include some knockback
- Respect damage causes - Use appropriate damage cause assets for different damage types
- Handle death gracefully - Use the DeathComponent system rather than directly removing entities
Related Systems
Section titled “Related Systems”- Entity Stats System - Manages health, resistance, and other stats affected by damage
- Physics System - Handles velocity and knockback movement
- Animation System - Plays hit and death animations
- Particle System - Shows impact particles
- Sound System - Plays damage and death sounds