Prefab System
Hytale’s Prefab System allows you to create, save, and place pre-built structures in the world. Prefabs can contain blocks, fluids, entities, and nested child prefabs with support for rotation and weighted spawning.
Architecture Overview
Section titled “Architecture Overview”PrefabStore (Singleton)├── PREFAB_CACHE - Cached BlockSelection objects├── getPrefab() - Load prefabs from disk├── savePrefab() - Save prefabs to disk└── Path resolution - Server, Asset, WorldGen prefabs
BlockSelection├── Block data - Block IDs, rotations, fillers├── Fluid data - Fluid IDs and levels├── Entity data - Entity holders├── Anchor point - Placement origin└── place() - Place in world
IPrefabBuffer├── Column-based storage - Efficient block iteration├── Child prefabs - Nested prefab references├── forEach() - Block/entity iteration└── Rotation support - PrefabRotation transformsPrefabStore
Section titled “PrefabStore”The PrefabStore singleton manages loading, saving, and caching prefabs. Access it via the static get() method:
import com.hypixel.hytale.server.core.prefab.PrefabStore;import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection;
PrefabStore store = PrefabStore.get();Loading Prefabs
Section titled “Loading Prefabs”// Load from server prefabs directoryBlockSelection prefab = store.getServerPrefab("structures/house.prefab.json");
// Load from asset pack prefabsBlockSelection assetPrefab = store.getAssetPrefab("buildings/tower.prefab.json");
// Load from world generation prefabsBlockSelection worldGenPrefab = store.getWorldGenPrefab("dungeons/cave.prefab.json");
// Load from any asset pack (searches all packs)BlockSelection anyPrefab = store.getAssetPrefabFromAnyPack("trees/oak.prefab.json");
// Load from absolute pathPath absolutePath = Path.of("/path/to/prefab.prefab.json");BlockSelection customPrefab = store.getPrefab(absolutePath);Loading Multiple Prefabs
Section titled “Loading Multiple Prefabs”import java.util.Map;import java.nio.file.Path;
// Load all prefabs from a directoryMap<Path, BlockSelection> prefabs = store.getServerPrefabDir("structures/houses");
// Iterate loaded prefabsfor (Map.Entry<Path, BlockSelection> entry : prefabs.entrySet()) { Path path = entry.getKey(); BlockSelection selection = entry.getValue(); // Process each prefab}Saving Prefabs
Section titled “Saving Prefabs”BlockSelection selection = /* ... */;
// Save to server prefabs (fails if exists)store.saveServerPrefab("mystructure.prefab.json", selection);
// Save with overwrite optionstore.saveServerPrefab("mystructure.prefab.json", selection, true);
// Save to specific pathPath outputPath = Path.of("/custom/path/structure.prefab.json");store.savePrefab(outputPath, selection, true);Prefab Paths
Section titled “Prefab Paths”// Get base pathsPath serverPrefabs = store.getServerPrefabsPath(); // prefabs/Path assetPrefabs = store.getAssetPrefabsPath(); // Assets/Server/Prefabs/Path worldGenPrefabs = store.getWorldGenPrefabsPath(); // WorldGen/Default/Prefabs/
// Get prefabs path for specific asset packAssetPack pack = /* ... */;Path packPrefabs = store.getAssetPrefabsPathForPack(pack);BlockSelection
Section titled “BlockSelection”The BlockSelection class represents a prefab’s content including blocks, fluids, and entities.
Creating a Selection
Section titled “Creating a Selection”import com.hypixel.hytale.server.core.prefab.selection.standard.BlockSelection;import com.hypixel.hytale.math.vector.Vector3i;
// Empty selectionBlockSelection selection = new BlockSelection();
// With initial capacityBlockSelection selection = new BlockSelection(1000, 10); // blocks, entities
// Copy from another selectionBlockSelection copy = new BlockSelection(otherSelection);Setting Properties
Section titled “Setting Properties”// Set position (world coordinates)selection.setPosition(100, 64, 200);
// Set anchor point (relative to position)selection.setAnchor(0, 0, 0);
// Set selection boundsselection.setSelectionArea( new Vector3i(-5, 0, -5), // min new Vector3i(5, 10, 5) // max);Adding Blocks
Section titled “Adding Blocks”import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;import com.hypixel.hytale.component.Holder;import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
// Add block at world positionint blockId = BlockType.getAssetMap().getAsset("hytale:stone").getBlockId();selection.addBlockAtWorldPos(x, y, z, blockId, rotation, filler, supportValue);
// Add block at local position (relative to selection position)selection.addBlockAtLocalPos(localX, localY, localZ, blockId, rotation, filler, supportValue);
// Add block with component stateHolder<ChunkStore> state = /* block state holder */;selection.addBlockAtLocalPos(localX, localY, localZ, blockId, rotation, filler, supportValue, state);
// Add empty block (air)selection.addEmptyAtWorldPos(x, y, z);Adding Fluids
Section titled “Adding Fluids”import com.hypixel.hytale.server.core.asset.type.fluid.Fluid;
int fluidId = Fluid.getAssetMap().getAsset("hytale:water").getAssetIndex();byte fluidLevel = 8; // 0-8, where 8 is full
selection.addFluidAtWorldPos(x, y, z, fluidId, fluidLevel);selection.addFluidAtLocalPos(localX, localY, localZ, fluidId, fluidLevel);Adding Entities
Section titled “Adding Entities”import com.hypixel.hytale.component.Holder;import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
Holder<EntityStore> entityHolder = /* entity holder */;
// Add from world (adjusts position relative to selection)selection.addEntityFromWorld(entityHolder);
// Add raw holder (position already relative)selection.addEntityHolderRaw(entityHolder);Iterating Content
Section titled “Iterating Content”// Iterate blocksselection.forEachBlock((x, y, z, block) -> { int blockId = block.blockId(); int rotation = block.rotation(); int filler = block.filler(); int supportValue = block.supportValue(); Holder<ChunkStore> state = block.holder(); // Process block});
// Iterate fluidsselection.forEachFluid((x, y, z, fluidId, fluidLevel) -> { // Process fluid});
// Iterate entitiesselection.forEachEntity(entityHolder -> { // Process entity});Querying Content
Section titled “Querying Content”// Check if block existsboolean hasBlock = selection.hasBlockAtWorldPos(x, y, z);boolean hasLocalBlock = selection.hasBlockAtLocalPos(localX, localY, localZ);
// Get block IDint blockId = selection.getBlockAtWorldPos(x, y, z);
// Get fluidint fluidId = selection.getFluidAtWorldPos(x, y, z);byte fluidLevel = selection.getFluidLevelAtWorldPos(x, y, z);
// Get block stateHolder<ChunkStore> state = selection.getStateAtWorldPos(x, y, z);
// Get countsint blockCount = selection.getBlockCount();int fluidCount = selection.getFluidCount();int entityCount = selection.getEntityCount();Placing Prefabs
Section titled “Placing Prefabs”Basic Placement
Section titled “Basic Placement”import com.hypixel.hytale.server.core.universe.world.World;import com.hypixel.hytale.component.ComponentAccessor;import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
World world = /* ... */;ComponentAccessor<EntityStore> accessor = /* ... */;
// Place at selection's positionselection.placeNoReturn(world, Vector3i.ZERO, accessor);
// Place at specific positionselection.placeNoReturn(world, new Vector3i(100, 64, 200), accessor);Placement with Undo
Section titled “Placement with Undo”The place() method returns a BlockSelection containing the original blocks that were replaced:
// Place and get previous state for undoBlockSelection previousState = selection.place(feedback, world);
// Place at positionBlockSelection previousState = selection.place(feedback, world, position, blockMask);
// Undo the placementpreviousState.place(feedback, world);Block Masks
Section titled “Block Masks”Use block masks to filter which blocks are affected during placement:
import com.hypixel.hytale.server.core.prefab.selection.mask.BlockMask;
BlockMask mask = /* ... */;BlockSelection result = selection.place(feedback, world, position, mask);Entity Placement Callback
Section titled “Entity Placement Callback”import com.hypixel.hytale.component.Ref;
selection.place(feedback, world, position, blockMask, entityRef -> { // Called for each entity placed Ref<EntityStore> ref = entityRef; // Track or modify spawned entities});PrefabRotation
Section titled “PrefabRotation”The PrefabRotation enum handles rotation transformations for prefabs:
import com.hypixel.hytale.server.core.prefab.PrefabRotation;import com.hypixel.hytale.math.vector.Vector3i;import com.hypixel.hytale.math.vector.Vector3d;
// Available rotationsPrefabRotation rot0 = PrefabRotation.ROTATION_0; // 0 degreesPrefabRotation rot90 = PrefabRotation.ROTATION_90; // 90 degreesPrefabRotation rot180 = PrefabRotation.ROTATION_180; // 180 degreesPrefabRotation rot270 = PrefabRotation.ROTATION_270; // 270 degrees
// Get all rotationsPrefabRotation[] all = PrefabRotation.VALUES;
// Rotate vectorsVector3i intVec = new Vector3i(5, 0, 3);rot90.rotate(intVec); // Modifies in place
Vector3d doubleVec = new Vector3d(5.0, 0.0, 3.0);rot90.rotate(doubleVec);
// Get rotated coordinatesint newX = rot90.getX(originalX, originalZ);int newZ = rot90.getZ(originalX, originalZ);
// Add rotations togetherPrefabRotation combined = rot90.add(rot180); // Results in ROTATION_270
// Get yaw in radiansfloat yaw = rot90.getYaw();
// Convert from Rotation enumimport com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;PrefabRotation fromRotation = PrefabRotation.fromRotation(Rotation.Ninety);Transforming Selections
Section titled “Transforming Selections”import com.hypixel.hytale.math.Axis;
// Rotate around Y axisBlockSelection rotated = selection.rotate(Axis.Y, 90);
// Rotate around custom originVector3f origin = new Vector3f(5, 0, 5);BlockSelection rotatedOrigin = selection.rotate(Axis.Y, 90, origin);
// Arbitrary rotation (yaw, pitch, roll in degrees)BlockSelection arbitrary = selection.rotateArbitrary(45f, 0f, 0f);
// Flip along axisBlockSelection flipped = selection.flip(Axis.X);
// Relativize (adjust coordinates relative to anchor)BlockSelection relativized = selection.relativize();BlockSelection relativizedOrigin = selection.relativize(originX, originY, originZ);
// Clone selectionBlockSelection clone = selection.cloneSelection();PrefabWeights
Section titled “PrefabWeights”The PrefabWeights class handles weighted random selection of prefabs:
import com.hypixel.hytale.server.core.prefab.PrefabWeights;import java.util.Random;
// Create weightsPrefabWeights weights = new PrefabWeights();
// Set individual weightsweights.setWeight("oak_tree", 3.0);weights.setWeight("birch_tree", 2.0);weights.setWeight("pine_tree", 1.0);
// Set default weight for unlisted prefabsweights.setDefaultWeight(1.0);
// Get weight for specific prefabdouble oakWeight = weights.getWeight("oak_tree"); // 3.0double unlisted = weights.getWeight("maple_tree"); // 1.0 (default)
// Remove specific weight (falls back to default)weights.removeWeight("oak_tree");
// Select from array using weightsString[] prefabNames = {"oak_tree", "birch_tree", "pine_tree"};Random random = new Random();
String selected = weights.get( prefabNames, name -> name, // Name extraction function random);
// Parse from string formatPrefabWeights parsed = PrefabWeights.parse("oak=3.0, birch=2.0, pine=1.0");
// Get string representationString mappingString = weights.getMappingString(); // "oak=3.0, birch=2.0, pine=1.0"Weights Codec
Section titled “Weights Codec”For serialization in asset configurations:
import com.hypixel.hytale.codec.Codec;
// Use the built-in codecCodec<PrefabWeights> codec = PrefabWeights.CODEC;
// Example JSON structure:// {// "Default": 1.0,// "Weights": {// "oak_tree": 3.0,// "birch_tree": 2.0// }// }PrefabCopyableComponent
Section titled “PrefabCopyableComponent”Mark entities as copyable when saving prefabs:
import com.hypixel.hytale.server.core.prefab.PrefabCopyableComponent;import com.hypixel.hytale.component.ComponentType;import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
// Get the component typeComponentType<EntityStore, PrefabCopyableComponent> componentType = PrefabCopyableComponent.getComponentType();
// Get singleton instancePrefabCopyableComponent instance = PrefabCopyableComponent.get();
// Add to entity to make it copyable in prefabsentityHolder.addComponent(componentType, instance);PrefabEntry
Section titled “PrefabEntry”The PrefabEntry record provides metadata about a prefab file:
import com.hypixel.hytale.server.core.prefab.PrefabEntry;import com.hypixel.hytale.assetstore.AssetPack;import java.nio.file.Path;
// Create entryPath path = Path.of("/assets/prefabs/house.prefab.json");Path relativePath = Path.of("house.prefab.json");AssetPack pack = /* ... */;
PrefabEntry entry = new PrefabEntry(path, relativePath, pack);
// Access propertiesPath fullPath = entry.path();Path relative = entry.relativePath();AssetPack sourcePack = entry.pack();String displayName = entry.displayName();
// Check sourceboolean isFromBasePack = entry.isFromBasePack();boolean isFromAssetPack = entry.isFromAssetPack();String packName = entry.getPackName();
// Get file nameString fileName = entry.getFileName();String displayWithPack = entry.getDisplayNameWithPack(); // "[PackName] house.prefab.json"IPrefabBuffer
Section titled “IPrefabBuffer”The IPrefabBuffer interface provides efficient iteration over prefab data:
import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer;import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferCall;
IPrefabBuffer buffer = /* ... */;
// Get boundsint minX = buffer.getMinX();int minY = buffer.getMinY();int minZ = buffer.getMinZ();int maxX = buffer.getMaxX();int maxY = buffer.getMaxY();int maxZ = buffer.getMaxZ();
// Get bounds with rotationint minXRotated = buffer.getMinX(PrefabRotation.ROTATION_90);
// Get anchorint anchorX = buffer.getAnchorX();int anchorY = buffer.getAnchorY();int anchorZ = buffer.getAnchorZ();
// Get column countint columns = buffer.getColumnCount();
// Get maximum extent (for any rotation)int maxExtent = buffer.getMaximumExtend();
// Get block data at positionint blockId = buffer.getBlockId(x, y, z);int filler = buffer.getFiller(x, y, z);int rotationIndex = buffer.getRotationIndex(x, y, z);
// Release buffer when donebuffer.release();Iterating with PrefabBufferCall
Section titled “Iterating with PrefabBufferCall”import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferCall;
// Create call contextPrefabBufferCall call = new PrefabBufferCall();call.rotation = PrefabRotation.ROTATION_0;call.random = new Random();
// Iterate all blocksbuffer.forEach( IPrefabBuffer.iterateAllColumns(), (x, y, z, blockId, holder, supportValue, rotation, filler, ctx, fluidId, fluidLevel) -> { // Process each block }, (x, z, entityHolders, ctx) -> { // Process entities in column }, (x, y, z, path, fitHeightmap, inheritSeed, inheritHeightCondition, weights, rotation, ctx) -> { // Process child prefab references }, call);PrefabLoader
Section titled “PrefabLoader”Load prefabs using wildcard patterns:
import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabLoader;import java.nio.file.Path;
Path rootFolder = PrefabStore.get().getServerPrefabsPath();PrefabLoader loader = new PrefabLoader(rootFolder);
// Load single prefabloader.resolvePrefabs("structures.house", path -> { // path: structures/house.prefab.json});
// Load all prefabs in folder (wildcard)loader.resolvePrefabs("structures.*", path -> { // Called for each .prefab.json in structures/});
// Static method variantPrefabLoader.resolvePrefabs(rootFolder, "dungeons.*", path -> { // Process each dungeon prefab});Prefab Events
Section titled “Prefab Events”PrefabPasteEvent
Section titled “PrefabPasteEvent”Fired when a prefab paste operation begins or ends:
import com.hypixel.hytale.server.core.prefab.event.PrefabPasteEvent;
getEventRegistry().register(PrefabPasteEvent.class, event -> { int prefabId = event.getPrefabId(); boolean isStart = event.isPasteStart();
if (event.isPasteStart()) { // Paste starting } else { // Paste completed }
// Cancel to prevent paste event.setCancelled(true);});PrefabPlaceEntityEvent
Section titled “PrefabPlaceEntityEvent”Fired for each entity placed from a prefab:
import com.hypixel.hytale.server.core.prefab.event.PrefabPlaceEntityEvent;
getEventRegistry().register(PrefabPlaceEntityEvent.class, event -> { int prefabId = event.getPrefabId(); Holder<EntityStore> entityHolder = event.getHolder();
// Modify entity before spawn // Note: This event is not cancellable});SelectionManager
Section titled “SelectionManager”The SelectionManager provides access to the current selection provider:
import com.hypixel.hytale.server.core.prefab.selection.SelectionManager;import com.hypixel.hytale.server.core.prefab.selection.SelectionProvider;
// Get current providerSelectionProvider provider = SelectionManager.getSelectionProvider();
// Set custom providerSelectionManager.setSelectionProvider(myProvider);SelectionProvider Interface
Section titled “SelectionProvider Interface”import com.hypixel.hytale.server.core.prefab.selection.SelectionProvider;import com.hypixel.hytale.server.core.entity.entities.Player;
public interface SelectionProvider { <T extends Throwable> void computeSelectionCopy( Ref<EntityStore> entityRef, Player player, ThrowableConsumer<BlockSelection, T> consumer, ComponentAccessor<EntityStore> accessor );}World Generation Integration
Section titled “World Generation Integration”Prefabs integrate with world generation through the PrefabPopulator:
import com.hypixel.hytale.server.worldgen.chunk.populator.PrefabPopulator;
// Prefabs are automatically placed during world generation based on:// - Biome configuration// - Noise density conditions// - Height conditions// - Parent block conditions// - Priority-based conflict resolutionChild Prefabs
Section titled “Child Prefabs”Prefabs can contain references to other prefabs (child prefabs):
import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer;
PrefabBuffer.ChildPrefab[] children = buffer.getChildPrefabs();
for (PrefabBuffer.ChildPrefab child : children) { int x = child.getX(); int y = child.getY(); int z = child.getZ(); String path = child.getPath(); boolean fitHeightmap = child.isFitHeightmap(); boolean inheritSeed = child.isInheritSeed(); boolean inheritHeightCondition = child.isInheritHeightCondition(); PrefabWeights weights = child.getWeights(); PrefabRotation rotation = child.getRotation();}Serialization
Section titled “Serialization”Prefabs are serialized using BSON format with the SelectionPrefabSerializer:
import com.hypixel.hytale.server.core.prefab.config.SelectionPrefabSerializer;import org.bson.BsonDocument;
// Serialize to BSONBsonDocument doc = SelectionPrefabSerializer.serialize(selection);
// Deserialize from BSONBlockSelection loaded = SelectionPrefabSerializer.deserialize(doc);The prefab format includes:
- Version number (current: 8)
- Block ID version for migration
- Anchor position
- Blocks array with position, name, rotation, filler, support, and components
- Fluids array with position, name, and level
- Entities array with full component data
Best Practices
Section titled “Best Practices”- Cache loaded prefabs - The
PrefabStorecaches automatically, but avoid repeated lookups - Release buffers - Call
release()onIPrefabBufferwhen done - Use appropriate placement method - Use
placeNoReturn()for performance when undo is not needed - Set anchor points correctly - The anchor determines the placement origin
- Handle rotation consistently - Use
PrefabRotationfor all rotation operations - Use weights for variety - Configure
PrefabWeightsfor natural-looking world generation - Mark entities copyable - Add
PrefabCopyableComponentto entities that should be saved - Validate paths - Use the prefab file pattern
.prefab.jsonfor compatibility - Handle exceptions - Catch
PrefabLoadExceptionandPrefabSaveExceptionappropriately - Use child prefabs - For modular, composable structures