---
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<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>`.

```java
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.

```java
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:

```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<EntityStore> 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<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 >}}

```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());
}
```
