--- title: Entity Components type: docs weight: 2 --- Hytale uses an Entity Component System (ECS) where entities are composed of modular components. Each component adds specific functionality or data to an entity. ## Component Categories {{< cards cols="3" >}} {{< card link="#transform--position" title="Transform & Position" subtitle="Location, rotation, collision" >}} {{< card link="#display--visual" title="Display & Visual" subtitle="Models, animations, effects" >}} {{< card link="#behavior-markers" title="Behavior Markers" subtitle="Interaction flags" >}} {{< card link="#physics" title="Physics" subtitle="Position data, collision" >}} {{< card link="#audio" title="Audio" subtitle="Movement sounds" >}} {{< card link="#custom-components" title="Custom Components" subtitle="Plugin-defined components" >}} {{< /cards >}} --- ## Component Access Pattern Before diving into specific components, understand how to access components in Hytale's ECS: ```java // Get component type from the component class ComponentType transformType = TransformComponent.getComponentType(); // Get Ref from PlayerRef or other source Ref ref = playerRef.getReference(); if (ref == null || !ref.isValid()) { return; } // Get Store from Ref, then access component via Store Store store = ref.getStore(); TransformComponent transform = store.getComponent(ref, transformType); // Check if entity has a component using Archetype Archetype archetype = store.getArchetype(ref); boolean hasComponent = archetype.contains(transformType); ``` {{< callout type="warning" >}} **Important:** `Ref` does NOT have `getComponent()` or `hasComponent()` methods directly. You must get the `Store` first via `ref.getStore()`, then call `store.getComponent(ref, componentType)`. {{< /callout >}} {{< callout type="info" >}} All entity components implement `Component` and provide a static `getComponentType()` method to retrieve the `ComponentType` for component access. {{< /callout >}} --- ## Transform & Position ### TransformComponent Core component for entity position, rotation, and chunk reference. **Package:** `com.hypixel.hytale.server.core.modules.entity.component` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | position | `Vector3d` | World position (x, y, z) | | rotation | `Vector3f` | Rotation angles (x, y, z) | | chunkRef | `Ref` | Reference to containing chunk | {{< /tab >}} {{< tab >}} - `getPosition()` - Returns world position as Vector3d - `setPosition(Vector3d)` - Sets world position (uses `assign()`) - `getRotation()` - Returns rotation angles - `setRotation(Vector3f)` - Sets rotation angles (uses `assign()`) - `teleportPosition(Vector3d)` - Teleports entity to position - `getTransform()` - Returns Transform object combining position/rotation - `getChunkRef()` - Returns chunk reference (`Ref`) - `getChunk()` - **Deprecated** - Returns WorldChunk directly {{< callout type="warning" >}} `getChunk()` is deprecated and marked for removal. Use `getChunkRef()` instead. {{< /callout >}} {{< /tab >}} {{< tab >}} ```java // Get store from ref Store store = ref.getStore(); ComponentType transformType = TransformComponent.getComponentType(); TransformComponent transform = store.getComponent(ref, transformType); if (transform != null) { // Get current position Vector3d pos = transform.getPosition(); getLogger().at(Level.INFO).log("Entity at: " + pos.x + ", " + pos.y + ", " + pos.z); // Get rotation Vector3f rot = transform.getRotation(); // Get chunk reference (recommended) Ref chunkRef = transform.getChunkRef(); } ``` {{< /tab >}} {{< /tabs >}} --- ### BoundingBox Defines the collision bounds of an entity using a `Box` object. **Package:** `com.hypixel.hytale.server.core.modules.entity.component` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | boundingBox | `Box` | The collision box with min/max corners | | detailBoxes | `Map` | Named detail boxes for precise collision | {{< /tab >}} {{< tab >}} - `getBoundingBox()` - Returns the `Box` object - `setBoundingBox(Box)` - Sets the bounding box - `getDetailBoxes()` - Returns map of detail boxes (can be null) {{< /tab >}} {{< tab >}} ```java ComponentType boundsType = BoundingBox.getComponentType(); BoundingBox bounds = store.getComponent(ref, boundsType); // Access the Box object Box box = bounds.getBoundingBox(); // Box provides min/max as Vector3d Vector3d min = box.getMin(); Vector3d max = box.getMax(); // Box utility methods double width = box.width(); // max.x - min.x double height = box.height(); // max.y - min.y double depth = box.depth(); // max.z - min.z // Center point double centerX = box.middleX(); double centerY = box.middleY(); double centerZ = box.middleZ(); // Check intersection with another box Box otherBox = otherBounds.getBoundingBox(); if (box.isIntersecting(otherBox)) { handleCollision(); } // Modify box (mutates in place) box.offset(1.0, 0, 0); // Move box by offset box.scale(2.0f); // Scale box // Access detail boxes for precise collision Map details = bounds.getDetailBoxes(); if (details != null) { for (Map.Entry entry : details.entrySet()) { // Process named detail boxes } } ``` {{< /tab >}} {{< /tabs >}} #### The Box Class The `Box` class (`com.hypixel.hytale.math.shape.Box`) is a fundamental shape class: | Method | Description | |--------|-------------| | `width()` | Returns width (X axis) | | `height()` | Returns height (Y axis) | | `depth()` | Returns depth (Z axis) | | `middleX/Y/Z()` | Returns center coordinates | | `getVolume()` | Returns total volume | | `isIntersecting(Box)` | Checks intersection with another box | | `containsPosition(x,y,z)` | Checks if point is inside | | `offset(x,y,z)` | Moves box by offset (mutates) | | `scale(float)` | Scales box (mutates) | | `union(Box)` | Expands to contain another box | | `clone()` | Creates a copy | --- ### CollisionResultComponent Stores collision detection results from the physics system. **Package:** `com.hypixel.hytale.server.core.modules.entity.component` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | collisionResult | `CollisionResult` | Full collision computation result | | collisionStartPosition | `Vector3d` | Position where collision started | | collisionPositionOffset | `Vector3d` | Offset from collision | | pendingCollisionCheck | `boolean` | Whether collision check is pending | {{< /tab >}} {{< tab >}} - `getCollisionResult()` - Returns the `CollisionResult` object - `isPendingCollisionCheck()` - Returns true if collision needs recomputation {{< /tab >}} {{< tab >}} ```java ComponentType collisionType = CollisionResultComponent.getComponentType(); CollisionResultComponent collision = store.getComponent(ref,collisionType); // Get collision result object CollisionResult result = collision.getCollisionResult(); // Check for block collisions int blockCollisionCount = result.getBlockCollisionCount(); if (blockCollisionCount > 0) { BlockCollisionData firstCollision = result.getFirstBlockCollision(); // Process collision } // Check if sliding (on ground) if (result.isSliding) { // Entity is sliding on a surface double slideStart = result.slideStart; double slideEnd = result.slideEnd; } // Check character collisions (if enabled) int charCollisionCount = result.getCharacterCollisionCount(); CharacterCollisionData charCollision = result.getFirstCharacterCollision(); // Access trigger blocks (collision triggers) CollisionDataArray triggers = result.getTriggerBlocks(); ``` {{< /tab >}} {{< /tabs >}} {{< callout type="info" >}} The `CollisionResult` class is complex and handles block collisions, character collisions, slides, and trigger blocks. It's primarily used internally by the collision system. {{< /callout >}} --- ### PositionDataComponent Tracks block type IDs at entity's position. **Package:** `com.hypixel.hytale.server.core.modules.entity.component` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | insideBlockTypeId | `int` | Block type ID entity is inside | | standingOnBlockTypeId | `int` | Block type ID entity is standing on | {{< /tab >}} {{< tab >}} - `getInsideBlockTypeId()` - Returns block type ID entity is inside - `setInsideBlockTypeId(int)` - Sets inside block type ID - `getStandingOnBlockTypeId()` - Returns block type ID beneath entity - `setStandingOnBlockTypeId(int)` - Sets standing-on block type ID {{< /tab >}} {{< tab >}} ```java ComponentType posDataType = PositionDataComponent.getComponentType(); PositionDataComponent posData = store.getComponent(ref,posDataType); // Check what block entity is standing on int standingOn = posData.getStandingOnBlockTypeId(); // Check what block entity is inside int insideBlock = posData.getInsideBlockTypeId(); // Use block type registry to get actual BlockType BlockType blockType = BlockType.getAssetMap().getAsset(standingOn); if (blockType != null) { getLogger().at(Level.INFO).log("Standing on: " + blockType.getId()); } ``` {{< /tab >}} {{< /tabs >}} --- ## Display & Visual ### DisplayNameComponent Controls the display name shown above an entity. **Package:** `com.hypixel.hytale.server.core.modules.entity.component` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | displayName | `Message` | The displayed name as a Message object | {{< /tab >}} {{< tab >}} - `getDisplayName()` - Returns the `Message` display name {{< /tab >}} {{< tab >}} ```java ComponentType nameType = DisplayNameComponent.getComponentType(); DisplayNameComponent nameComp = store.getComponent(ref,nameType); // Get current display name Message name = nameComp.getDisplayName(); // Message is the formatted text object used throughout Hytale // Use Message.raw() or Message formatting methods to create new names ``` {{< /tab >}} {{< /tabs >}} {{< callout type="info" >}} **Message Type:** Display names use Hytale's `Message` class, not plain strings. The `Message` class supports formatted text, colors, and localization. {{< /callout >}} --- ### ModelComponent Links an entity to its visual 3D model. **Package:** `com.hypixel.hytale.server.core.modules.entity.component` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | model | `Model` | Reference to the model | | isNetworkOutdated | `boolean` | Network sync flag | {{< /tab >}} {{< tab >}} - `getModel()` - Returns the Model - `setModel(Model)` - Sets the model {{< /tab >}} {{< tab >}} ```java ComponentType modelType = ModelComponent.getComponentType(); ModelComponent model = store.getComponent(ref,modelType); // Get current model Model currentModel = model.getModel(); // Set new model model.setModel(newModel); ``` {{< /tab >}} {{< /tabs >}} --- ### EntityScaleComponent Controls the scale/size of an entity. **Package:** `com.hypixel.hytale.server.core.modules.entity.component` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | scale | `float` | Uniform scale factor (default: 1.0) | | isNetworkOutdated | `boolean` | Network sync flag | {{< /tab >}} {{< tab >}} - `getScale()` - Returns the scale factor (float) - `setScale(float)` - Sets the uniform scale factor {{< /tab >}} {{< tab >}} ```java ComponentType scaleType = EntityScaleComponent.getComponentType(); EntityScaleComponent scale = store.getComponent(ref,scaleType); // Get current scale float currentScale = scale.getScale(); // Default: 1.0 // Make entity twice as large scale.setScale(2.0f); // Make entity half size scale.setScale(0.5f); // Giant boss spawn effect - track growth in tick handler private final Map, Float> growingBosses = new ConcurrentHashMap<>(); public void startGrowBoss(Ref bossRef) { EntityScaleComponent scale = store.getComponent(bossRef, scaleType); scale.setScale(0.1f); // Start small growingBosses.put(bossRef, 0.1f); } // Call this from your tick handler (every 5 ticks) public void onTick() { growingBosses.entrySet().removeIf(entry -> { Ref bossRef = entry.getKey(); float currentScale = entry.getValue(); if (currentScale >= 1.0f) { return true; // Remove from map } EntityScaleComponent scale = store.getComponent(bossRef, scaleType); if (scale != null) { float newScale = currentScale + 0.1f; scale.setScale(newScale); entry.setValue(newScale); } return false; // Keep in map }); } ``` {{< /tab >}} {{< /tabs >}} {{< callout type="warning" >}} **Uniform Scale Only:** Hytale's EntityScaleComponent uses a single `float` for uniform scaling on all axes, not per-axis Vector3f scaling. {{< /callout >}} --- ### ActiveAnimationComponent Manages currently playing animations per animation slot. **Package:** `com.hypixel.hytale.server.core.modules.entity.component` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | activeAnimations | `String[]` | Animation names per slot | | isNetworkOutdated | `boolean` | Network sync flag | {{< /tab >}} {{< tab >}} - `getActiveAnimations()` - Returns array of animation names indexed by `AnimationSlot` - `setPlayingAnimation(AnimationSlot, String)` - Sets animation for a specific slot {{< /tab >}} {{< tab >}} ```java ComponentType animType = ActiveAnimationComponent.getComponentType(); ActiveAnimationComponent anim = store.getComponent(ref,animType); // Get all active animations String[] animations = anim.getActiveAnimations(); // Set animation for specific slot anim.setPlayingAnimation(AnimationSlot.Action, "attack_swing"); anim.setPlayingAnimation(AnimationSlot.Movement, "walk"); // Clear animation on a slot anim.setPlayingAnimation(AnimationSlot.Emote, null); ``` {{< /tab >}} {{< /tabs >}} #### AnimationSlot Enum Hytale uses animation slots to layer animations: | Slot | Value | Purpose | |------|-------|---------| | `Movement` | 0 | Locomotion (walk, run, jump) | | `Status` | 1 | Status effects | | `Action` | 2 | Actions (attack, use item) | | `Face` | 3 | Facial expressions | | `Emote` | 4 | Social emotes | --- ### DynamicLight Emits dynamic light from an entity using a `ColorLight` structure. **Package:** `com.hypixel.hytale.server.core.modules.entity.component` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | colorLight | `ColorLight` | Light data (radius, RGB) | | isNetworkOutdated | `boolean` | Network sync flag | {{< /tab >}} {{< tab >}} - `getColorLight()` - Returns the `ColorLight` object - `setColorLight(ColorLight)` - Sets the light configuration {{< /tab >}} {{< tab >}} ```java ComponentType lightType = DynamicLight.getComponentType(); DynamicLight light = store.getComponent(ref,lightType); // Get current light ColorLight currentLight = light.getColorLight(); // Create new light configuration // ColorLight has public fields: radius, red, green, blue (all bytes) ColorLight torchLight = new ColorLight(); torchLight.radius = 8; // Light radius torchLight.red = (byte)255; // Warm orange color torchLight.green = (byte)200; torchLight.blue = (byte)100; light.setColorLight(torchLight); // Or use constructor ColorLight blueLight = new ColorLight( (byte)12, // radius (byte)50, // red (byte)100, // green (byte)255 // blue ); light.setColorLight(blueLight); ``` {{< /tab >}} {{< /tabs >}} #### ColorLight Structure The `ColorLight` class (`com.hypixel.hytale.protocol.ColorLight`) has public fields: | Field | Type | Description | |-------|------|-------------| | `radius` | `byte` | Light radius | | `red` | `byte` | Red component (0-255) | | `green` | `byte` | Green component (0-255) | | `blue` | `byte` | Blue component (0-255) | --- ### HeadRotation Controls head rotation independently from body. **Package:** `com.hypixel.hytale.server.core.modules.entity.component` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | rotation | `Vector3f` | Head rotation (x, y, z) | {{< /tab >}} {{< tab >}} - `getRotation()` - Returns head rotation as Vector3f - `setRotation(Vector3f)` - Sets head rotation (uses `assign()`) - `getDirection()` - Returns look direction as Vector3d - `getAxisDirection()` - Returns axis-aligned direction as Vector3i - `teleportRotation(Vector3f)` - Teleports head rotation {{< /tab >}} {{< tab >}} ```java ComponentType headType = HeadRotation.getComponentType(); HeadRotation head = store.getComponent(ref,headType); // Get current head rotation Vector3f rot = head.getRotation(); // Set head rotation (pitch, yaw, roll) head.setRotation(new Vector3f(-30, 45, 0)); // Look up-right // Get look direction as normalized vector Vector3d direction = head.getDirection(); // Get axis direction (discrete: -1, 0, or 1 per axis) Vector3i axisDir = head.getAxisDirection(); // Teleport rotation (instant, no interpolation) head.teleportRotation(new Vector3f(0, 180, 0)); // Face backward ``` {{< /tab >}} {{< /tabs >}} --- ## Behavior Markers Singleton components that act as flags modifying entity behavior. They use the singleton pattern with an `INSTANCE` field. ### Interactable {{< badge "Singleton" >}} Marks an entity as interactable by players. ```java ComponentType type = Interactable.getComponentType(); // Check if entity has component using archetype Store store = ref.getStore(); Archetype archetype = store.getArchetype(ref); if (archetype.contains(type)) { // Entity is interactable } // Component is singleton - use INSTANCE Interactable component = Interactable.INSTANCE; ``` --- ### Invulnerable {{< badge "Singleton" >}} Makes an entity immune to damage. ```java ComponentType type = Invulnerable.getComponentType(); // Check invulnerability in damage handler getEventRegistry().register(EntityDamageEvent.class, event -> { Ref targetRef = event.getTarget(); if (targetRef != null && targetRef.isValid()) { Store store = targetRef.getStore(); Archetype archetype = store.getArchetype(targetRef); if (archetype.contains(type)) { event.setCancelled(true); } } }); ``` --- ### Intangible {{< badge "Singleton" >}} Makes an entity pass through other entities and blocks (no collision). ```java ComponentType type = Intangible.getComponentType(); // Ghost entity - no collision Store store = ref.getStore(); Archetype archetype = store.getArchetype(ref); if (archetype.contains(type)) { // Entity passes through obstacles } ``` --- ## Audio ### MovementAudioComponent Handles movement-related sounds with a predicate system. **Package:** `com.hypixel.hytale.server.core.modules.entity.component` {{< tabs items="Fields,Methods,Example" >}} {{< tab >}} | Field | Type | Description | |-------|------|-------------| | shouldHearPredicate | `ShouldHearPredicate` | Predicate for who hears sounds | | lastInsideBlockTypeId | `int` | Last block entity was inside | | nextMoveInRepeat | `float` | Timer for movement sound repeat | {{< /tab >}} {{< tab >}} - `getShouldHearPredicate(Ref)` - Returns predicate for sound hearing - `getLastInsideBlockTypeId()` - Returns last inside block ID - `setLastInsideBlockTypeId(int)` - Sets last inside block ID - `canMoveInRepeat()` - Returns true if repeat timer is active - `tickMoveInRepeat(float)` - Advances repeat timer, returns true if elapsed - `setNextMoveInRepeat(float)` - Sets next repeat interval {{< /tab >}} {{< tab >}} ```java ComponentType audioType = MovementAudioComponent.getComponentType(); MovementAudioComponent movementAudio = store.getComponent(ref,audioType); // Get predicate for who should hear movement sounds MovementAudioComponent.ShouldHearPredicate predicate = movementAudio.getShouldHearPredicate(entityRef); // The predicate returns true for entities that should hear the sound // (excludes the owner by default) boolean shouldHear = predicate.test(otherEntityRef); // Check move-in repeat timer if (movementAudio.canMoveInRepeat()) { float dt = 1.0f / 20.0f; // Delta time if (movementAudio.tickMoveInRepeat(dt)) { // Timer elapsed, play repeat sound } } // Disable repeat sounds movementAudio.setNextMoveInRepeat(MovementAudioComponent.NO_REPEAT); ``` {{< /tab >}} {{< /tabs >}} --- ## Custom Components ### Creating Custom Components Create custom components by implementing `Component`: ```java public class CustomDataComponent implements Component { // Static component type reference (set during registration) private static ComponentType TYPE; // Component data private int level = 1; private String faction = "neutral"; public static ComponentType getComponentType() { return TYPE; } public int getLevel() { return level; } public void setLevel(int level) { this.level = level; } public String getFaction() { return faction; } public void setFaction(String faction) { this.faction = faction; } @Override @Nonnull public Component clone() { CustomDataComponent copy = new CustomDataComponent(); copy.level = this.level; copy.faction = this.faction; return copy; } } ``` ### Registering Components Register components during plugin start: ```java @Override public void start() { // Register with EntityStoreRegistry ComponentType type = getEntityStoreRegistry().register( "custom_data", CustomDataComponent.class, CustomDataComponent::new ); // Store type reference for static access CustomDataComponent.TYPE = type; } ``` ### Using Custom Components ```java // Get component type ComponentType type = CustomDataComponent.getComponentType(); // Get store from ref Store store = ref.getStore(); // Check if entity has component Archetype archetype = store.getArchetype(ref); if (archetype.contains(type)) { CustomDataComponent data = store.getComponent(ref, type); if (data.getLevel() > 10) { // High level entity logic } } ``` --- ## Best Practices {{< callout type="info" >}} **Component Guidelines:** - Use `getComponentType()` static method to get the `ComponentType` for access - Always check if a component exists using `hasComponent()` before accessing - Register custom components in `start()`, after all plugins have completed setup - Implement `clone()` for components that need copying - Use singleton pattern (static `INSTANCE`) for marker components {{< /callout >}} {{< callout type="warning" >}} **Network Sync:** Many components have an `isNetworkOutdated` flag. This is managed automatically by the network system - avoid setting it manually unless you understand the networking layer. {{< /callout >}} {{< callout type="error" >}} **Thread Safety:** Component access should be done on the world's ticking thread. Use `world.isInThread()` to verify, or use the task system for cross-thread operations. {{< /callout >}} --- ## Component Type Reference | Component | Package | Type | Purpose | |-----------|---------|------|---------| | TransformComponent | entity.component | Data | Position, rotation, chunk | | BoundingBox | entity.component | Data | Collision bounds | | CollisionResultComponent | entity.component | Data | Collision results | | PositionDataComponent | entity.component | Data | Block context | | DisplayNameComponent | entity.component | Data | Visible name | | ModelComponent | entity.component | Data | 3D model | | EntityScaleComponent | entity.component | Data | Size scaling | | ActiveAnimationComponent | entity.component | Data | Animations | | DynamicLight | entity.component | Data | Light emission | | HeadRotation | entity.component | Data | Head direction | | MovementAudioComponent | entity.component | Data | Movement sounds | | Interactable | entity.component | Singleton | Can be interacted | | Invulnerable | entity.component | Singleton | Cannot be damaged | | Intangible | entity.component | Singleton | No collision |