Init
This commit is contained in:
828
content/world/entities/entity-components.fr.md
Normal file
828
content/world/entities/entity-components.fr.md
Normal file
@@ -0,0 +1,828 @@
|
||||
---
|
||||
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<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 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<ChunkStore>`)
|
||||
- `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<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 the `Box` object
|
||||
- `setBoundingBox(Box)` - Sets the bounding box
|
||||
- `getDetailBoxes()` - Returns map of detail boxes (can be null)
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
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 the `CollisionResult` object
|
||||
- `isPendingCollisionCheck()` - Returns true if collision needs recomputation
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
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 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<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 the `Message` display name
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
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 Model
|
||||
- `setModel(Model)` - Sets the model
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
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 >}}
|
||||
```java
|
||||
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 by `AnimationSlot`
|
||||
- `setPlayingAnimation(AnimationSlot, String)` - Sets animation for a specific slot
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
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 the `ColorLight` object
|
||||
- `setColorLight(ColorLight)` - Sets the light configuration
|
||||
{{< /tab >}}
|
||||
{{< tab >}}
|
||||
```java
|
||||
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 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<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.
|
||||
|
||||
```java
|
||||
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.
|
||||
|
||||
```java
|
||||
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).
|
||||
|
||||
```java
|
||||
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 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<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>`:
|
||||
|
||||
```java
|
||||
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:
|
||||
|
||||
```java
|
||||
@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
|
||||
|
||||
```java
|
||||
// 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 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 |
|
||||
Reference in New Issue
Block a user