Skip to content

Entity Spawn

Entity spawning in Hytale uses the ECS lifecycle system rather than traditional events. When entities are added to the world, a RefSystem can detect and respond to them.

API Reference

See full API: NPCEntity | NPCPlugin | RefSystem | WorldConfig

Concepts

Before reading this, familiarize yourself with the Event System and ECS Architecture.

Key Concept

Entity spawning uses AddReason.SPAWN to distinguish newly spawned entities from entities being loaded from save data (AddReason.LOAD).

Listening for Entity Spawns[^2]

Use a RefSystem to detect when entities are added to the world:

java
import com.hypixel.hytale.component.*;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import javax.annotation.Nonnull;

public class EntitySpawnListener extends RefSystem {

    @Nonnull
    @Override
    public Query getQuery() {
        // Listen for all NPCs
        return NPCEntity.getComponentType();
    }

    @Override
    public void onEntityAdded(@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason,
                               @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
        // Only handle spawns, not loads from save data
        if (reason != AddReason.SPAWN) {
            return;
        }

        NPCEntity npc = (NPCEntity) store.getComponent(ref, NPCEntity.getComponentType());
        String npcType = npc.getRoleName();  // e.g., "Creature_Skeleton"

        System.out.println("Entity spawned: " + npcType);
    }

    @Override
    public void onEntityRemove(@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason,
                                @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
        // Called when entity is removed
    }
}

Cancelling/Preventing Spawns

Method 1: Remove on Spawn

The simplest approach is to remove the entity immediately when it spawns:

java
public class BlockCreeperSpawns extends RefSystem {

    @Nonnull
    @Override
    public Query getQuery() {
        return NPCEntity.getComponentType();
    }

    @Override
    public void onEntityAdded(@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason,
                               @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
        if (reason != AddReason.SPAWN) {
            return;
        }

        NPCEntity npc = (NPCEntity) store.getComponent(ref, NPCEntity.getComponentType());

        // Block specific entity types
        if (npc.getRoleName().equals("Creature_Creeper")) {
            commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
        }
    }

    @Override
    public void onEntityRemove(@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason,
                                @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {}
}

Method 2: Using FailedSpawnComponent

Hytale has a built-in mechanism for preventing spawns. When an entity has a FailedSpawnComponent, the FailedSpawnSystem automatically removes it:

java
import com.hypixel.hytale.server.npc.components.FailedSpawnComponent;

// When spawning programmatically with a pre-add callback
NPCPlugin.get().spawnEntity(store, roleIndex, position, rotation, null,
    (npc, holder, s) -> {
        // Add FailedSpawnComponent to prevent spawn based on condition
        if (shouldBlockSpawn()) {
            holder.addComponent(FailedSpawnComponent.getComponentType(),
                               new FailedSpawnComponent());
        }
    },
    null  // post-spawn callback
);

Disabling All Entity Spawning

World Config Setting

Each world has a configuration option to disable NPC spawning entirely:

java
World world = Universe.get().getWorld("myworld");
WorldConfig config = world.getWorldConfig();

// Disable all environmental NPC spawning
config.setSpawningNPC(false);

// Check if spawning is enabled
boolean spawningEnabled = config.isSpawningNPC();

Setting on World Creation

java
Universe.get().addWorld("arena", "void", null).thenAccept(world -> {
    // Disable spawning in the arena world
    world.getWorldConfig().setSpawningNPC(false);
});

Freeze All NPCs[^1]

You can also freeze all NPCs in a world (prevents movement and AI):

java
world.getWorldConfig().setIsAllNPCFrozen(true);

Spawning Entities Programmatically[^3]

Basic NPC Spawn

java
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.server.npc.NPCPlugin;

// Get the entity store from your context
Store<EntityStore> store = world.getEntityStore().getStore();

// Spawn position and rotation
Vector3d position = new Vector3d(100, 65, 200);
Vector3f rotation = new Vector3f(0, 0, 0);  // pitch, yaw, roll

// Spawn by NPC type name
NPCPlugin.get().spawnNPC(store, "Creature_Skeleton", null, position, rotation);

Spawn with Callbacks

java
NPCPlugin.get().spawnEntity(store, roleIndex, position, rotation, null,
    // Pre-add callback - modify entity before it's added to world
    (npc, holder, s) -> {
        // Add custom components to the holder
        holder.addComponent(MyCustomComponent.getComponentType(),
                           new MyCustomComponent());
    },
    // Post-spawn callback - runs after successful spawn
    (npc, ref, s) -> {
        System.out.println("Spawned entity with ref: " + ref);
    }
);

Getting NPC Type Index

java
// Get the index for an NPC type name
int skeletonIndex = NPCPlugin.get().getIndex("Creature_Skeleton");

if (skeletonIndex >= 0) {
    NPCPlugin.get().spawnEntity(store, skeletonIndex, position, rotation, null, null);
}

Spawn Control Examples

Limit Spawns Per World

java
public class SpawnLimiter extends RefSystem {
    private static final int MAX_NPCS = 100;

    @Nonnull
    @Override
    public Query getQuery() {
        return NPCEntity.getComponentType();
    }

    @Override
    public void onEntityAdded(@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason,
                               @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
        if (reason != AddReason.SPAWN) {
            return;
        }

        // Count existing NPCs
        int npcCount = store.getEntityCount();  // Simplified - would need query

        if (npcCount > MAX_NPCS) {
            commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
        }
    }

    @Override
    public void onEntityRemove(@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason,
                                @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {}
}

Region-Based Spawn Control

java
public class SafeZoneSpawnBlocker extends RefSystem {
    private final Vector3d safeZoneCenter = new Vector3d(0, 64, 0);
    private final double safeZoneRadius = 50.0;

    @Nonnull
    @Override
    public Query getQuery() {
        return NPCEntity.getComponentType();
    }

    @Override
    public void onEntityAdded(@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason,
                               @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
        if (reason != AddReason.SPAWN) {
            return;
        }

        TransformComponent transform = (TransformComponent) store.getComponent(
            ref, TransformComponent.getComponentType());

        if (transform != null) {
            Vector3d pos = transform.getPosition();
            double distance = pos.distance(safeZoneCenter);

            // Remove entities that spawn in safe zone
            if (distance < safeZoneRadius) {
                commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
            }
        }
    }

    @Override
    public void onEntityRemove(@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason,
                                @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {}
}

Full Plugin Example

java
package com.example.spawncontrol;

import com.hypixel.hytale.common.plugin.PluginManifest;
import com.hypixel.hytale.component.*;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.server.core.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import javax.annotation.Nonnull;
import java.util.Set;

public class SpawnControlPlugin extends JavaPlugin {

    public static final PluginManifest MANIFEST = PluginManifest
        .corePlugin(SpawnControlPlugin.class)
        .depends(NPCPlugin.class)
        .build();

    // Entity types to block from spawning
    private static final Set<String> BLOCKED_ENTITIES = Set.of(
        "Creature_Creeper",
        "Creature_Zombie"
    );

    public SpawnControlPlugin(@Nonnull JavaPluginInit init) {
        super(init);
    }

    @Override
    protected void setup() {
        ComponentRegistryProxy<EntityStore> registry = this.getEntityStoreRegistry();
        registry.registerSystem(new SpawnFilter());

        this.getLogger().info("SpawnControl plugin loaded!");
    }

    public static class SpawnFilter extends RefSystem {

        @Nonnull
        @Override
        public Query getQuery() {
            return NPCEntity.getComponentType();
        }

        @Override
        public void onEntityAdded(@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason,
                                   @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
            // Only filter spawns, not loads
            if (reason != AddReason.SPAWN) {
                return;
            }

            NPCEntity npc = (NPCEntity) store.getComponent(ref, NPCEntity.getComponentType());
            String entityType = npc.getRoleName();

            // Block specific entity types
            if (BLOCKED_ENTITIES.contains(entityType)) {
                commandBuffer.removeEntity(ref, RemoveReason.REMOVE);
                return;
            }

            // Log spawns for debugging
            TransformComponent transform = (TransformComponent) store.getComponent(
                ref, TransformComponent.getComponentType());
            if (transform != null) {
                System.out.println("Spawned " + entityType + " at " + transform.getPosition());
            }
        }

        @Override
        public void onEntityRemove(@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason,
                                    @Nonnull Store store, @Nonnull CommandBuffer commandBuffer) {
            // Optional: track despawns
        }
    }
}

AddReason and RemoveReason

AddReasonDescription
SPAWNEntity is newly spawned (by world generation, commands, or plugins)
LOADEntity is being loaded from saved world data
RemoveReasonDescription
REMOVEEntity is being permanently removed (death, despawn, plugin removal)
UNLOADEntity is being unloaded (chunk unload) but will be saved

See Also

[^1]: See WorldConfig API for setSpawningNPC(), isSpawningNPC(), setIsAllNPCFrozen(), and other world settings [^2]: See RefSystem API for onEntityAdded() and onEntityRemove() methods [^3]: See NPCPlugin API for spawnNPC(), spawnEntity(), and getIndex() methods

Unofficial documentation · Any questions? Found a mistake? Have something you want documented? Join the Discord server at the top and let us know in #hytale!