25 KiB
title, type, weight
| title | type | weight |
|---|---|---|
| Entity Components | docs | 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:
// Get component type from the component class
ComponentType<EntityStore, TransformComponent> transformType =
TransformComponent.getComponentType();
// Get Ref<EntityStore> from PlayerRef or other source
Ref<EntityStore> ref = playerRef.getReference();
if (ref == null || !ref.isValid()) {
return;
}
// Get Store from Ref, then access component via Store
Store<EntityStore> store = ref.getStore();
TransformComponent transform = store.getComponent(ref, transformType);
// Check if entity has a component using Archetype
Archetype<EntityStore> archetype = store.getArchetype(ref);
boolean hasComponent = archetype.contains(transformType);
{{< callout type="warning" >}}
Important: Ref<EntityStore> 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<EntityStore> 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<ChunkStore> |
Reference to containing chunk |
| {{< /tab >}} | ||
| {{< tab >}} |
getPosition()- Returns world position as Vector3dsetPosition(Vector3d)- Sets world position (usesassign())getRotation()- Returns rotation anglessetRotation(Vector3f)- Sets rotation angles (usesassign())teleportPosition(Vector3d)- Teleports entity to positiongetTransform()- Returns Transform object combining position/rotationgetChunkRef()- Returns chunk reference (Ref<ChunkStore>)getChunk()- Deprecated - Returns WorldChunk directly
{{< callout type="warning" >}}
getChunk() is deprecated and marked for removal. Use getChunkRef() instead.
{{< /callout >}}
{{< /tab >}}
{{< tab >}}
// Get store from ref
Store<EntityStore> store = ref.getStore();
ComponentType<EntityStore, TransformComponent> 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<ChunkStore> 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<String, DetailBox[]> |
Named detail boxes for precise collision |
| {{< /tab >}} | ||
| {{< tab >}} |
getBoundingBox()- Returns theBoxobjectsetBoundingBox(Box)- Sets the bounding boxgetDetailBoxes()- Returns map of detail boxes (can be null) {{< /tab >}} {{< tab >}}
ComponentType<EntityStore, BoundingBox> 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<String, DetailBox[]> details = bounds.getDetailBoxes();
if (details != null) {
for (Map.Entry<String, DetailBox[]> 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 theCollisionResultobjectisPendingCollisionCheck()- Returns true if collision needs recomputation {{< /tab >}} {{< tab >}}
ComponentType<EntityStore, CollisionResultComponent> 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<BlockCollisionData> 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 insidesetInsideBlockTypeId(int)- Sets inside block type IDgetStandingOnBlockTypeId()- Returns block type ID beneath entitysetStandingOnBlockTypeId(int)- Sets standing-on block type ID {{< /tab >}} {{< tab >}}
ComponentType<EntityStore, PositionDataComponent> 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 theMessagedisplay name {{< /tab >}} {{< tab >}}
ComponentType<EntityStore, DisplayNameComponent> 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 ModelsetModel(Model)- Sets the model {{< /tab >}} {{< tab >}}
ComponentType<EntityStore, ModelComponent> 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 >}}
ComponentType<EntityStore, EntityScaleComponent> 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<Ref<EntityStore>, Float> growingBosses = new ConcurrentHashMap<>();
public void startGrowBoss(Ref<EntityStore> 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<EntityStore> 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 byAnimationSlotsetPlayingAnimation(AnimationSlot, String)- Sets animation for a specific slot {{< /tab >}} {{< tab >}}
ComponentType<EntityStore, ActiveAnimationComponent> 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 theColorLightobjectsetColorLight(ColorLight)- Sets the light configuration {{< /tab >}} {{< tab >}}
ComponentType<EntityStore, DynamicLight> 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 Vector3fsetRotation(Vector3f)- Sets head rotation (usesassign())getDirection()- Returns look direction as Vector3dgetAxisDirection()- Returns axis-aligned direction as Vector3iteleportRotation(Vector3f)- Teleports head rotation {{< /tab >}} {{< tab >}}
ComponentType<EntityStore, HeadRotation> 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.
ComponentType<EntityStore, Interactable> type = Interactable.getComponentType();
// Check if entity has component using archetype
Store<EntityStore> store = ref.getStore();
Archetype<EntityStore> 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.
ComponentType<EntityStore, Invulnerable> type = Invulnerable.getComponentType();
// Check invulnerability in damage handler
getEventRegistry().register(EntityDamageEvent.class, event -> {
Ref<EntityStore> targetRef = event.getTarget();
if (targetRef != null && targetRef.isValid()) {
Store<EntityStore> store = targetRef.getStore();
Archetype<EntityStore> 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).
ComponentType<EntityStore, Intangible> type = Intangible.getComponentType();
// Ghost entity - no collision
Store<EntityStore> store = ref.getStore();
Archetype<EntityStore> 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<EntityStore>)- Returns predicate for sound hearinggetLastInsideBlockTypeId()- Returns last inside block IDsetLastInsideBlockTypeId(int)- Sets last inside block IDcanMoveInRepeat()- Returns true if repeat timer is activetickMoveInRepeat(float)- Advances repeat timer, returns true if elapsedsetNextMoveInRepeat(float)- Sets next repeat interval {{< /tab >}} {{< tab >}}
ComponentType<EntityStore, MovementAudioComponent> 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<EntityStore>:
public class CustomDataComponent implements Component<EntityStore> {
// Static component type reference (set during registration)
private static ComponentType<EntityStore, CustomDataComponent> TYPE;
// Component data
private int level = 1;
private String faction = "neutral";
public static ComponentType<EntityStore, CustomDataComponent> 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<EntityStore> clone() {
CustomDataComponent copy = new CustomDataComponent();
copy.level = this.level;
copy.faction = this.faction;
return copy;
}
}
Registering Components
Register components during plugin start:
@Override
public void start() {
// Register with EntityStoreRegistry
ComponentType<EntityStore, CustomDataComponent> type =
getEntityStoreRegistry().register(
"custom_data",
CustomDataComponent.class,
CustomDataComponent::new
);
// Store type reference for static access
CustomDataComponent.TYPE = type;
}
Using Custom Components
// Get component type
ComponentType<EntityStore, CustomDataComponent> type =
CustomDataComponent.getComponentType();
// Get store from ref
Store<EntityStore> store = ref.getStore();
// Check if entity has component
Archetype<EntityStore> 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 theComponentTypefor 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 |