Skip to content

Entity System

Hytale uses an Entity Component System (ECS) architecture. Entities are lightweight identifiers; all data lives in components attached to them.

See also: Entity Events for entity lifecycle events, ECS Events for component-level events

The primary way to reference entities. A Ref is a lightweight handle that points to an entity in a Store.

Ref<EntityStore> ref = ...;
// check validity before use
if (ref.isValid()) {
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
Vector3d position = transform.getPosition();
}

Key methods:

  • isValid() - Returns true if the entity still exists
  • getStore() - Returns the Store this ref belongs to
  • getIndex() - Internal index (avoid direct use)

Refs become invalid when the entity is removed. Always check isValid() before accessing components.

For references that survive entity unload/reload cycles, use PersistentRef. It stores the entity’s UUID and lazily resolves the Ref when needed.

PersistentRef persistentRef = new PersistentRef();
persistentRef.setEntity(ref, componentAccessor);
// later, even after reload:
Ref<EntityStore> resolved = persistentRef.getEntity(componentAccessor);
if (resolved != null && resolved.isValid()) {
// use the entity
}

A Holder is a temporary container for an entity’s components, used during spawning and serialization. Think of it as a blueprint for an entity before it’s added to the world.

Holder<EntityStore> holder = EntityStore.REGISTRY.newHolder();
holder.addComponent(TransformComponent.getComponentType(), new TransformComponent(position, rotation));
holder.addComponent(ModelComponent.getComponentType(), modelComponent);
// ... add more components
Ref<EntityStore> ref = store.addEntity(holder, AddReason.SPAWN);

See Spawning for the full spawning workflow.

Each World has an EntityStore that manages all entities in that world.

World world = ...;
EntityStore entityStore = world.getEntityStore();
Store<EntityStore> store = entityStore.getStore();

See Queries for finding entities by UUID, network ID, or iteration.

Entity operations must happen on the world’s tick thread. Use world.execute() for cross-thread access:

world.execute(() -> {
if (ref.isValid()) {
// safe to access entity here
TransformComponent t = store.getComponent(ref, TransformComponent.getComponentType());
}
});

See Threading for more on Hytale’s threading model.