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.
1. Reading entities
Section titled “1. Reading entities”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()));}val entityData = overworld.readEntities(2, -1) ?: return
for (entity in entityData.entities()) { println("${entity.id()} at ${entity.pos().contentToString()}")}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
idStringThe entity’s type, like minecraft:zombie.
uuidUUIDThe entity’s unique id.
posdouble[]The entity’s position, as [x, y, z].
rotationfloat[]The entity’s facing, as [yaw, pitch].
onGroundbooleanWhether the entity is currently touching the ground.
LivingEntity fieldsExtends Entity
healthfloatThe entity’s current health.
canPickUpLootbooleanWhether the entity can pick up dropped items.
persistenceRequiredbooleanWhether 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.
2. Adding an entity
Section titled “2. Adding an entity”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.
3. Saving entity changes
Section titled “3. Saving entity changes”Just like chunks, changes to entity data need to be written back explicitly.
overworld.saveEntityData(2, -1, entityData);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.
4. Reading points of interest
Section titled “4. Reading points of interest”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()));}val poiData = overworld.readPoi(2, -1) ?: return
for (record in poiData.records()) { println("${record.type()} at ${record.pos().contentToString()}")}Each PoiRecord is small:
posint[]The block position of the POI, as [x, y, z].
typeStringWhat kind of POI it is, like minecraft:home or minecraft:armorer.
freeTicketsintHow 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);5. Adding a point of interest
Section titled “5. Adding a point of interest”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);poiData.addRecord(4, 10, 70, 10, "minecraft:home", 1)overworld.savePoiData(2, -1, poiData)That’s it
Section titled “That’s it”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.