Skip to content

Entities and Points of Interest

Terrain isn’t the only thing stored per chunk. Every chunk also has its own entities (mobs, dropped items, minecarts) and its own points of interest (job sites, beds, bells and similar blocks villagers care about). Both are stored separately from the chunk’s blocks, in their own region files, but they’re looked up the same way: by chunk coordinates, through the Dimension you already opened in the Dimensions and Chunks guide.

readEntities(chunkX, chunkZ) gives you an EntityData object, or null if that chunk’s entity file doesn’t have data for it yet.

EntityData entityData = overworld.readEntities(2, -1);
if (entityData == null) {
return;
}
for (Entity entity : entityData.entities()) {
System.out.println(entity.id() + " at " + Arrays.toString(entity.pos()));
}

Each entity comes back wrapped as either an Entity or a LivingEntity. minified-worlds tells them apart by checking whether the entity’s NBT has a Health field, which every living entity (mobs, players, animals) has and non living ones (dropped items, minecarts, arrows) don’t. LivingEntity extends Entity and adds the extra fields that only make sense for something alive, like health.

Entity fieldsCommon to every entity
idString

The entity’s type, like minecraft:zombie.

uuidUUID

The entity’s unique id.

posdouble[]

The entity’s position, as [x, y, z].

rotationfloat[]

The entity’s facing, as [yaw, pitch].

onGroundboolean

Whether the entity is currently touching the ground.

LivingEntity fieldsExtends Entity
healthfloat

The entity’s current health.

canPickUpLootboolean

Whether the entity can pick up dropped items.

persistenceRequiredboolean

Whether the entity is exempt from despawning.

Both classes have quite a few more fields than what’s listed here, raw() gives you the full NBT if you need something that isn’t wrapped yet.

If you want to spawn something into a chunk yourself, build the entity’s NBT compound and pass it to addEntity. This adds it both to the in memory list and to the underlying NBT, so it’s ready to be saved.

NbtCompound zombieNbt = new NbtCompound();
zombieNbt.setString("id", "minecraft:zombie");
zombieNbt.setFloat("Health", 20.0f);
// ... position, UUID and other fields would normally go here too
Entity zombie = entityData.addEntity(zombieNbt);

Since the Health field is what decides how the entity gets wrapped, adding it before calling addEntity gets you a LivingEntity back instead of a plain Entity.

Just like chunks, changes to entity data need to be written back explicitly.

overworld.saveEntityData(2, -1, entityData);

This creates the entity region file for that chunk if it doesn’t exist yet, so it also works when you’re populating a freshly generated chunk that never had any entities before.

Points of interest, or POIs, are how villagers find beds to sleep in and job site blocks to work at, and how bells know where to summon villagers to. They’re read the same way, through readPoi(chunkX, chunkZ), returning PoiData or null.

PoiData poiData = overworld.readPoi(2, -1);
if (poiData == null) {
return;
}
for (PoiRecord record : poiData.records()) {
System.out.println(record.type() + " at " + Arrays.toString(record.pos()));
}

Each PoiRecord is small:

posint[]

The block position of the POI, as [x, y, z].

typeString

What kind of POI it is, like minecraft:home or minecraft:armorer.

freeTicketsint

How many more villagers can still claim this POI.

POIs inside a chunk are grouped by section Y, the same vertical slices chunks use for blocks. If you only care about one section, pass its Y to records(sectionY) instead of listing everything in the chunk.

List<PoiRecord> recordsInSection = poiData.records(4);

addRecord takes a section Y, a block position, the POI type, and how many free tickets it has (this is how many villagers can claim it at once, a bed has one, most job sites have one too). It creates the section entry if it doesn’t exist yet.

poiData.addRecord(4, 10, 70, 10, "minecraft:home", 1);
overworld.savePoiData(2, -1, poiData);

That’s the core of minified-worlds: opening a world, reading its settings, walking through dimensions and chunks, and reading or writing the blocks, entities and points of interest inside them. From here, the NbtCompound returned by raw() on most of these wrapper classes is still available whenever you need to reach a field that isn’t wrapped yet.