6.4 KiB
title, type, weight
| title | type | weight |
|---|---|---|
| Spawning Entities | docs | 3 |
Create and spawn entities into the world by registering entity classes and using the World API.
Registering Entities
Before spawning entities, register them with the EntityRegistry in your plugin's start() method:
@Override
public void start() {
// Register entity with key, class, constructor, and codec
getEntityRegistry().registerEntity(
"my_custom_entity",
CustomEntity.class,
world -> new CustomEntity(world),
CustomEntity.CODEC
);
}
Registration Parameters
| Parameter | Type | Description |
|---|---|---|
| key | String |
Unique identifier for the entity type |
| clazz | Class<T> |
The entity class |
| constructor | Function<World, T> |
Factory function that creates entity instances |
| codec | DirectDecodeCodec<T> |
Serialization codec for persistence |
Entity Base Classes
Hytale provides two main entity base classes:
Entity
Base class for all entities. Implements Component<EntityStore>.
public class SimpleEntity extends Entity {
public static final DirectDecodeCodec<SimpleEntity> CODEC =
Entity.CODEC.extend(SimpleEntity.class, SimpleEntity::new);
public SimpleEntity() {
super();
}
public SimpleEntity(World world) {
super(world);
}
}
LivingEntity
Extended base class for entities with health, inventory, and stats.
public class CustomMob extends LivingEntity {
public static final DirectDecodeCodec<CustomMob> CODEC =
LivingEntity.CODEC.extend(CustomMob.class, CustomMob::new);
public CustomMob() {
super();
}
public CustomMob(World world) {
super(world);
}
@Override
protected Inventory createDefaultInventory() {
return new Inventory(36); // 36 slots
}
}
Spawning Entities
Use the World API to spawn registered entities:
// Get the world
World world = player.getWorld();
// Create entity instance
CustomMob mob = new CustomMob(world);
// Define spawn position and rotation
Vector3d position = new Vector3d(100, 64, 200);
Vector3f rotation = new Vector3f(0, 90, 0); // Facing east
// Spawn the entity (deprecated but functional)
world.spawnEntity(mob, position, rotation);
addEntity Method
For more control over spawn reasons, use addEntity:
// AddReason options: SPAWN, LOAD, TRANSFER
world.addEntity(mob, position, rotation, AddReason.SPAWN);
{{< callout type="warning" >}}
Deprecation Notice: Both spawnEntity and addEntity methods on World are deprecated. The modern approach uses the component-based EntityStore system directly. However, these methods still work and are the simplest way to spawn entities.
{{< /callout >}}
Entity Lifecycle
Loading into World
When an entity is added to a world:
// Called automatically when entity is added
entity.loadIntoWorld(world);
// Entity receives network ID
int networkId = entity.getNetworkId();
// Reference is created for component access
Ref<EntityStore> ref = entity.getReference();
Removing Entities
// Remove an entity from the world
boolean removed = entity.remove();
// Check if entity was already removed
if (entity.wasRemoved()) {
// Entity no longer valid
}
Entity Events
EntityRemoveEvent
Listen for entity removal:
getEventRegistry().register(EntityRemoveEvent.class, event -> {
Entity entity = event.getEntity();
getLogger().at(Level.INFO).log("Entity removed: " + entity.getClass().getSimpleName());
});
LivingEntityInventoryChangeEvent
Track inventory changes on living entities:
getEventRegistry().register(LivingEntityInventoryChangeEvent.class, event -> {
LivingEntity entity = event.getEntity();
// Handle inventory change
});
{{< callout type="info" >}} No Spawn Event: Hytale does not have an EntitySpawnEvent. To track spawns, consider implementing custom logic in your entity constructor or using the component system's add callbacks. {{< /callout >}}
Complete Example
public class MyPlugin extends JavaPlugin {
@Override
public void start() {
// Register custom entity
getEntityRegistry().registerEntity(
"boss_monster",
BossMonster.class,
world -> new BossMonster(world),
BossMonster.CODEC
);
// Listen for removal
getEventRegistry().register(EntityRemoveEvent.class, this::onEntityRemove);
}
private void onEntityRemove(EntityRemoveEvent event) {
if (event.getEntity() instanceof BossMonster) {
getLogger().at(Level.INFO).log("Boss monster was defeated!");
}
}
public void spawnBoss(Player player) {
World world = player.getWorld();
// Create boss at player's location
BossMonster boss = new BossMonster(world);
// Get player position and add offset
Vector3d spawnPos = player.getTransformComponent().getPosition();
spawnPos = new Vector3d(spawnPos.x + 5, spawnPos.y, spawnPos.z);
// Face the player
Vector3f rotation = new Vector3f(0, 180, 0);
// Spawn
world.spawnEntity(boss, spawnPos, rotation);
}
}
public class BossMonster extends LivingEntity {
public static final DirectDecodeCodec<BossMonster> CODEC =
LivingEntity.CODEC.extend(BossMonster.class, BossMonster::new);
public BossMonster() {
super();
}
public BossMonster(World world) {
super(world);
}
@Override
protected Inventory createDefaultInventory() {
return new Inventory(9); // Small inventory
}
@Override
public boolean isCollidable() {
return true;
}
}
Thread Safety
{{< callout type="error" >}} Important: Entity spawning should be done on the world's ticking thread. If calling from an async context, use the world's executor: {{< /callout >}}
// Safe spawning from async context
world.execute(() -> {
world.spawnEntity(entity, position, rotation);
});
Spawn Validation
The world validates spawn positions:
// Position requirements:
// - X and Z must be within +/- 33554432
// - Y must be >= -32
// Invalid positions will throw IllegalArgumentException
try {
world.spawnEntity(entity, invalidPosition, rotation);
} catch (IllegalArgumentException e) {
getLogger().warning("Invalid spawn position: " + e.getMessage());
}