PrefabProp.java
package com.hypixel.hytale.builtin.hytalegenerator.props.prefab;
import com.hypixel.hytale.builtin.hytalegenerator.BlockMask;
import com.hypixel.hytale.builtin.hytalegenerator.bounds.Bounds3i;
import com.hypixel.hytale.builtin.hytalegenerator.bounds.SpaceSize;
import com.hypixel.hytale.builtin.hytalegenerator.conveyor.stagedconveyor.ContextDependency;
import com.hypixel.hytale.builtin.hytalegenerator.datastructures.WeightedMap;
import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.ArrayVoxelSpace;
import com.hypixel.hytale.builtin.hytalegenerator.datastructures.voxelspace.VoxelSpace;
import com.hypixel.hytale.builtin.hytalegenerator.framework.math.Calculator;
import com.hypixel.hytale.builtin.hytalegenerator.framework.math.SeedGenerator;
import com.hypixel.hytale.builtin.hytalegenerator.material.FluidMaterial;
import com.hypixel.hytale.builtin.hytalegenerator.material.Material;
import com.hypixel.hytale.builtin.hytalegenerator.material.MaterialCache;
import com.hypixel.hytale.builtin.hytalegenerator.material.SolidMaterial;
import com.hypixel.hytale.builtin.hytalegenerator.newsystem.views.EntityContainer;
import com.hypixel.hytale.builtin.hytalegenerator.patterns.Pattern;
import com.hypixel.hytale.builtin.hytalegenerator.props.Prop;
import com.hypixel.hytale.builtin.hytalegenerator.props.ScanResult;
import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.Directionality;
import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.RotatedPosition;
import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.RotatedPositionsScanResult;
import com.hypixel.hytale.builtin.hytalegenerator.props.directionality.StaticDirectionality;
import com.hypixel.hytale.builtin.hytalegenerator.props.entity.EntityPlacementData;
import com.hypixel.hytale.builtin.hytalegenerator.scanners.OriginScanner;
import com.hypixel.hytale.builtin.hytalegenerator.scanners.Scanner;
import com.hypixel.hytale.builtin.hytalegenerator.seed.SeedBox;
import com.hypixel.hytale.builtin.hytalegenerator.threadindexer.WorkerIndexer;
import com.hypixel.hytale.common.util.ExceptionUtil;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.prefab.PrefabRotation;
import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferCall;
import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer;
import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.PrefabBuffer;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Function;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PrefabProp extends Prop {
private final WeightedMap<List<PrefabBuffer>> prefabPool;
private final Scanner scanner;
private ContextDependency contextDependency;
private final MaterialCache materialCache;
private final SeedGenerator seedGenerator;
private final BlockMask materialMask;
private final Directionality directionality;
private final Bounds3i writeBounds_voxelGrid;
private final Bounds3i prefabBounds_voxelGrid;
private final List<PrefabProp> childProps;
private final List<RotatedPosition> childPositions;
private final Function<String, List<PrefabBuffer>> childPrefabLoader;
private final Scanner moldingScanner;
private final Pattern moldingPattern;
private final MoldingDirection moldingDirection;
private final boolean moldChildren;
private final int prefabId = this.hashCode();
private boolean loadEntities;
public PrefabProp(@Nonnull WeightedMap<List<PrefabBuffer>> prefabPool, @Nonnull Scanner scanner, @Nonnull Directionality directionality, @Nonnull MaterialCache materialCache, @Nonnull BlockMask materialMask, @Nonnull PrefabMoldingConfiguration prefabMoldingConfiguration, @Nullable Function<String, List<PrefabBuffer>> childPrefabLoader, @Nonnull SeedBox seedBox, boolean loadEntities) {
this.prefabPool = prefabPool;
this.scanner = scanner;
this.directionality = directionality;
this.materialCache = materialCache;
this.seedGenerator = new SeedGenerator((long)(Integer)seedBox.createSupplier().get());
this.materialMask = materialMask;
this.loadEntities = loadEntities;
this.childProps = new ArrayList();
this.childPositions = new ArrayList();
this.childPrefabLoader = childPrefabLoader == null ? (s) -> null : childPrefabLoader;
this.moldingScanner = prefabMoldingConfiguration.moldingScanner;
this.moldingPattern = prefabMoldingConfiguration.moldingPattern;
this.moldingDirection = prefabMoldingConfiguration.moldingDirection;
this.moldChildren = prefabMoldingConfiguration.moldChildren;
this.contextDependency = new ContextDependency();
Vector3i readRange = directionality.getReadRangeWith(scanner);
for(List<PrefabBuffer> prefabList : prefabPool.allElements()) {
if (prefabList.isEmpty()) {
throw new IllegalArgumentException("prefab pool contains empty list");
}
for(PrefabBuffer prefab : prefabList) {
if (prefab == null) {
throw new IllegalArgumentException("prefab pool contains list with null element");
}
PrefabBuffer.PrefabBufferAccessor prefabAccess = prefab.newAccess();
PrefabBuffer.ChildPrefab[] childPrefabs = prefabAccess.getChildPrefabs();
int childId = 0;
for(PrefabBuffer.ChildPrefab child : childPrefabs) {
RotatedPosition childPosition = new RotatedPosition(child.getX(), child.getY(), child.getZ(), child.getRotation());
String childPath = child.getPath().replace('.', '/');
childPath = childPath.replace("*", "");
List<PrefabBuffer> childPrefabBuffers = (List)this.childPrefabLoader.apply(childPath);
WeightedMap<List<PrefabBuffer>> weightedChildPrefabs = new WeightedMap<List<PrefabBuffer>>();
weightedChildPrefabs.add(childPrefabBuffers, 1.0);
StaticDirectionality childDirectionality = new StaticDirectionality(child.getRotation(), Pattern.yesPattern());
PrefabProp childProp = new PrefabProp(weightedChildPrefabs, OriginScanner.getInstance(), childDirectionality, materialCache, materialMask, this.moldChildren ? prefabMoldingConfiguration : PrefabMoldingConfiguration.none(), childPrefabLoader, seedBox.child(String.valueOf(childId++)), loadEntities);
this.childProps.add(childProp);
this.childPositions.add(childPosition);
}
Vector3i writeRange = this.getWriteRange(prefabAccess);
for(int i = 0; i < this.childPositions.size(); ++i) {
PrefabProp child = (PrefabProp)this.childProps.get(i);
Vector3i position = ((RotatedPosition)this.childPositions.get(i)).toVector3i();
Vector3i childWriteRange = child.getContextDependency().getWriteRange();
int maxRange = Calculator.max(position.x, position.y, position.z);
maxRange += Calculator.max(childWriteRange.x, childWriteRange.y, childWriteRange.z);
writeRange.x = Math.max(writeRange.x, maxRange);
writeRange.y = Math.max(writeRange.y, maxRange);
writeRange.z = Math.max(writeRange.z, maxRange);
}
ContextDependency contextDependency = new ContextDependency(readRange, writeRange);
this.contextDependency = ContextDependency.mostOf(this.contextDependency, contextDependency);
prefabAccess.release();
}
}
this.writeBounds_voxelGrid = this.contextDependency.getTotalPropBounds_voxelGrid();
this.prefabBounds_voxelGrid = new Bounds3i();
this.prefabBounds_voxelGrid.min.assign(this.contextDependency.getWriteRange()).scale(-1);
this.prefabBounds_voxelGrid.max.assign(this.contextDependency.getWriteRange()).add(Vector3i.ALL_ONES);
}
private Vector3i getWriteRange(PrefabBuffer.PrefabBufferAccessor prefabAccess) {
SpaceSize space = new SpaceSize();
for(PrefabRotation rotation : this.directionality.getPossibleRotations()) {
Vector3i max = PropPrefabUtil.getMax(prefabAccess, rotation);
max.add(1, 1, 1);
Vector3i min = PropPrefabUtil.getMin(prefabAccess, rotation);
space = SpaceSize.merge(space, new SpaceSize(min, max));
}
space = SpaceSize.stack(space, this.scanner.readSpaceWith(this.directionality.getGeneralPattern()));
return space.getRange();
}
public ScanResult scan(@Nonnull Vector3i position, @Nonnull VoxelSpace<Material> materialSpace, @Nonnull WorkerIndexer.Id id) {
Scanner.Context scannerContext = new Scanner.Context(position, this.directionality.getGeneralPattern(), materialSpace, id);
List<Vector3i> validPositions = this.scanner.scan(scannerContext);
Vector3i patternPosition = new Vector3i();
Pattern.Context patternContext = new Pattern.Context(patternPosition, materialSpace, id);
RotatedPositionsScanResult scanResult = new RotatedPositionsScanResult(new ArrayList());
for(Vector3i validPosition : validPositions) {
patternPosition.assign(validPosition);
PrefabRotation rotation = this.directionality.getRotationAt(patternContext);
if (rotation != null) {
scanResult.positions.add(new RotatedPosition(validPosition.x, validPosition.y, validPosition.z, rotation));
}
}
return scanResult;
}
public void place(@Nonnull Prop.Context context) {
if (this.prefabPool.size() != 0) {
List<RotatedPosition> positions = RotatedPositionsScanResult.cast(context.scanResult).positions;
if (positions != null) {
Bounds3i writeSpaceBounds_voxelGrid = context.materialSpace.getBounds();
for(RotatedPosition position : positions) {
Bounds3i localPrefabWriteBounds_voxelGrid = this.prefabBounds_voxelGrid.clone().offset(position.toVector3i());
if (localPrefabWriteBounds_voxelGrid.intersects(writeSpaceBounds_voxelGrid)) {
this.place(position, context.materialSpace, context.entityBuffer, context.workerId);
}
}
}
}
}
private PrefabBuffer pickPrefab(Random rand) {
List<PrefabBuffer> list = this.prefabPool.pick(rand);
int randomIndex = rand.nextInt(list.size());
return (PrefabBuffer)list.get(randomIndex);
}
private void place(RotatedPosition position, @Nonnull VoxelSpace<Material> materialSpace, @Nonnull EntityContainer entityBuffer, @Nonnull WorkerIndexer.Id id) {
Random random = new Random(this.seedGenerator.seedAt((long)position.x, (long)position.y, (long)position.z));
PrefabBufferCall callInstance = new PrefabBufferCall(random, position.rotation);
PrefabBuffer prefab = this.pickPrefab(random);
PrefabBuffer.PrefabBufferAccessor prefabAccess = prefab.newAccess();
VoxelSpace<Integer> moldingOffsets = null;
if (this.moldingDirection != MoldingDirection.NONE) {
int prefabMinX = prefabAccess.getMinX(position.rotation);
int prefabMinZ = prefabAccess.getMinZ(position.rotation);
int prefabMaxX = prefabAccess.getMaxX(position.rotation);
int prefabMaxZ = prefabAccess.getMaxZ(position.rotation);
int prefabSizeX = prefabMaxX - prefabMinX;
int prefabSizeZ = prefabMaxZ - prefabMinZ;
moldingOffsets = new ArrayVoxelSpace<Integer>(prefabSizeX, 1, prefabSizeZ);
moldingOffsets.setOrigin(-position.x - prefabMinX, 0, -position.z - prefabMinZ);
if (this.moldingDirection == MoldingDirection.DOWN || this.moldingDirection == MoldingDirection.UP) {
Vector3i pointer = new Vector3i(0, position.y, 0);
Scanner.Context scannerContext = new Scanner.Context(pointer, this.moldingPattern, materialSpace, id);
for(pointer.x = moldingOffsets.minX(); pointer.x < moldingOffsets.maxX(); ++pointer.x) {
for(pointer.z = moldingOffsets.minZ(); pointer.z < moldingOffsets.maxZ(); ++pointer.z) {
List<Vector3i> scanResult = this.moldingScanner.scan(scannerContext);
Integer offset = scanResult.isEmpty() ? null : ((Vector3i)scanResult.getFirst()).y - position.y;
if (offset != null && this.moldingDirection == MoldingDirection.UP) {
offset = offset - 1;
}
moldingOffsets.set(offset, pointer.x, 0, pointer.z);
}
}
}
}
try {
Vector3i prefabPositionVector = position.toVector3i();
prefabAccess.forEach(IPrefabBuffer.iterateAllColumns(), (x, yx, z, blockId, holder, support, rotation, filler, call, fluidId, fluidLevel) -> {
int worldX = position.x + x;
int worldY = position.y + yx;
int worldZ = position.z + z;
if (materialSpace.isInsideSpace(worldX, worldY, worldZ)) {
SolidMaterial solid = this.materialCache.getSolidMaterial(blockId, support, rotation, filler, holder != null ? holder.clone() : null);
FluidMaterial fluid = this.materialCache.getFluidMaterial(fluidId, (byte)fluidLevel);
Material material = this.materialCache.getMaterial(solid, fluid);
int materialHash = material.hashMaterialIds();
if (this.materialMask.canPlace(materialHash)) {
if (this.moldingDirection == MoldingDirection.DOWN || this.moldingDirection == MoldingDirection.UP) {
Integer offset = null;
if (moldingOffsets.isInsideSpace(worldX, 0, worldZ)) {
offset = moldingOffsets.getContent(worldX, 0, worldZ);
}
if (offset == null) {
return;
}
worldY += offset;
}
Material worldMaterial = materialSpace.getContent(worldX, worldY, worldZ);
int worldMaterialHash = worldMaterial.hashMaterialIds();
if (this.materialMask.canReplace(materialHash, worldMaterialHash)) {
materialSpace.set(material, worldX, worldY, worldZ);
}
}
}
}, (cx, cz, entityWrappers, buffer) -> {
if (this.loadEntities) {
if (entityWrappers != null) {
for(int i = 0; i < entityWrappers.length; ++i) {
TransformComponent transformComp = (TransformComponent)entityWrappers[i].getComponent(TransformComponent.getComponentType());
if (transformComp != null) {
Vector3d entityPosition = transformComp.getPosition().clone();
buffer.rotation.rotate(entityPosition);
Vector3d entityWorldPosition = entityPosition.add(prefabPositionVector);
if (entityBuffer.isInsideBuffer((int)entityWorldPosition.x, (int)entityWorldPosition.y, (int)entityWorldPosition.z)) {
Holder<EntityStore> entityClone = entityWrappers[i].clone();
transformComp = (TransformComponent)entityClone.getComponent(TransformComponent.getComponentType());
if (transformComp != null) {
entityPosition = transformComp.getPosition();
entityPosition.x = entityWorldPosition.x;
entityPosition.y = entityWorldPosition.y;
entityPosition.z = entityWorldPosition.z;
if (!materialSpace.isInsideSpace((int)Math.floor(entityPosition.x), (int)Math.floor(entityPosition.y), (int)Math.floor(entityPosition.z))) {
return;
}
EntityPlacementData placementData = new EntityPlacementData(new Vector3i(), PrefabRotation.ROTATION_0, entityClone, this.prefabId);
entityBuffer.addEntity(placementData);
}
}
}
}
}
}
}, (x, yx, z, path, fitHeightmap, inheritSeed, inheritHeightCondition, weights, rotation, t) -> {
}, callInstance);
} catch (Exception e) {
String msg = "Couldn't place prefab prop.";
msg = msg + "\n";
msg = msg + ExceptionUtil.toStringWithStack(e);
((HytaleLogger.Api)HytaleLogger.getLogger().atWarning()).log(msg);
} finally {
prefabAccess.release();
}
for(int i = 0; i < this.childProps.size(); ++i) {
PrefabProp prop = (PrefabProp)this.childProps.get(i);
RotatedPosition childPosition = ((RotatedPosition)this.childPositions.get(i)).getRelativeTo(position);
Vector3i rotatedChildPositionVec = new Vector3i(childPosition.x, childPosition.y, childPosition.z);
position.rotation.rotate(rotatedChildPositionVec);
if (moldingOffsets != null && moldingOffsets.isInsideSpace(childPosition.x, 0, childPosition.z)) {
Integer offset = moldingOffsets.getContent(childPosition.x, 0, childPosition.z);
if (offset == null) {
continue;
}
int y = childPosition.y + offset;
childPosition = new RotatedPosition(childPosition.x, y, childPosition.z, childPosition.rotation);
}
prop.place(childPosition, materialSpace, entityBuffer, id);
}
}
public ContextDependency getContextDependency() {
return this.contextDependency.clone();
}
@Nonnull
public Bounds3i getWriteBounds() {
return this.writeBounds_voxelGrid;
}
}