--- title: Spawning Entities type: docs weight: 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: ```java @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` | The entity class | | constructor | `Function` | Factory function that creates entity instances | | codec | `DirectDecodeCodec` | Serialization codec for persistence | ## Entity Base Classes Hytale provides two main entity base classes: ### Entity Base class for all entities. Implements `Component`. ```java public class SimpleEntity extends Entity { public static final DirectDecodeCodec 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. ```java public class CustomMob extends LivingEntity { public static final DirectDecodeCodec 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: ```java // 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`: ```java // 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: ```java // Called automatically when entity is added entity.loadIntoWorld(world); // Entity receives network ID int networkId = entity.getNetworkId(); // Reference is created for component access Ref ref = entity.getReference(); ``` ### Removing Entities ```java // 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: ```java 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: ```java 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 ```java 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 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 >}} ```java // Safe spawning from async context world.execute(() -> { world.spawnEntity(entity, position, rotation); }); ``` ## Spawn Validation The world validates spawn positions: ```java // 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()); } ```