classpublicPriority 3
PrefabFarmingStageData
com.hypixel.hytale.builtin.adventure.farming.config.stages.PrefabFarmingStageData
extends FarmingStageData
5
Methods
5
Public Methods
3
Fields
1
Constructors
Constants
PrefabFarmingStageData.PrefabStage[]EMPTY_ARRAY= new PrefabFarmingStageData.PrefabStage[0]
String[]EMPTY_REPLACE_MASK= new String[0]
floatMAX_BROKEN_PARTICLE_RATE= 0.75F
floatMAX_VOLUME_PREFAB= 1000.0F
floatMIN_BROKEN_PARTICLE_RATE= 0.25F
floatMIN_VOLUME_PREFAB= 125.0F
Constructors
public
PrefabFarmingStageData()Methods
Public Methods (5)
public
IWeightedMap<PrefabFarmingStageData.PrefabStage> getPrefabStages()public
Path getResolvedPath()@Nonnull
public
double getWeight()@Override
public
void remove(ComponentAccessor<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, Ref<ChunkStore> blockRef, int x, int y, int z)@Override
public
String toString()@Override
Fields
Protected Fields (1)
protected
IWeightedMap<PrefabFarmingStageData.PrefabStage> prefabStagesPrivate/Package Fields (2)
private
int[] replaceMaskTagIndicesprivate
String[] replaceMaskTagsInheritance
Parent
Current
Interface
Child
Use mouse wheel to zoom, drag to pan. Click nodes to navigate.
Related Classes
Used By
AssetPackAssetRegistryBlockTypeAssetMapFarmingBlockCodecKeyedCodecBuilderCodecArrayCodecValidatorsIWeightedElementIWeightedMapComponentAccessorRefChunkUtilFastRandomHashUtilMathUtilVector3iBlockMaterialAssetModuleBlockTypeRotationRotationTupleFarmingStageDataWeightedMapCodecPrefabRotationPrefabStorePrefabBufferCallPrefabBufferUtilIPrefabBufferWorldLocalCachedChunkAccessorWorldChunkBlockSectionChunkSectionChunkStorePrefabUtil
Source Code
package com.hypixel.hytale.builtin.adventure.farming.config.stages;
import com.hypixel.hytale.assetstore.AssetPack;
import com.hypixel.hytale.assetstore.AssetRegistry;
import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
import com.hypixel.hytale.codec.validation.Validators;
import com.hypixel.hytale.common.map.IWeightedElement;
import com.hypixel.hytale.common.map.IWeightedMap;
import com.hypixel.hytale.component.ComponentAccessor;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.math.util.FastRandom;
import com.hypixel.hytale.math.util.HashUtil;
import com.hypixel.hytale.math.util.MathUtil;
import com.hypixel.hytale.math.vector.Vector3i;
import com.hypixel.hytale.protocol.BlockMaterial;
import com.hypixel.hytale.server.core.asset.AssetModule;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingStageData;
import com.hypixel.hytale.server.core.codec.WeightedMapCodec;
import com.hypixel.hytale.server.core.prefab.PrefabRotation;
import com.hypixel.hytale.server.core.prefab.PrefabStore;
import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferCall;
import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferUtil;
import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.core.util.PrefabUtil;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PrefabFarmingStageData extends FarmingStageData {
public static final float MIN_VOLUME_PREFAB = 125.0F;
public static final float MAX_VOLUME_PREFAB = 1000.0F;
public static final float MIN_BROKEN_PARTICLE_RATE = 0.25F;
public static final float MAX_BROKEN_PARTICLE_RATE = 0.75F;
private static final String[] EMPTY_REPLACE_MASK = new String[0];
@Nonnull
public static BuilderCodec<PrefabFarmingStageData> CODEC = BuilderCodec.builder(
PrefabFarmingStageData.class, PrefabFarmingStageData::new, FarmingStageData.BASE_CODEC
)
.append(
new KeyedCodec<>("Prefabs", new WeightedMapCodec<>(PrefabFarmingStageData.PrefabStage.CODEC, PrefabFarmingStageData.PrefabStage.EMPTY_ARRAY)),
(stage, prefabStages) -> stage.prefabStages = prefabStages,
stage -> stage.prefabStages
)
.add()
.append(
new KeyedCodec<>("ReplaceMaskTags", new ArrayCodec<>(Codec.STRING, String[]::new)),
(stage, replaceMask) -> stage.replaceMaskTags = replaceMask,
stage -> stage.replaceMaskTags
)
.add()
.afterDecode(PrefabFarmingStageData::processConfig)
.build();
protected IWeightedMap<PrefabFarmingStageData.PrefabStage> prefabStages;
private String[] replaceMaskTags = EMPTY_REPLACE_MASK;
private int[] replaceMaskTagIndices;
public PrefabFarmingStageData() {
}
private static double computeParticlesRate(@Nonnull IPrefabBuffer prefab) {
double xLength = (double)(prefab.getMaxX() - prefab.getMinX());
double yLength = (double)(prefab.getMaxY() - prefab.getMinY());
double zLength = (double)(prefab.getMaxZ() - prefab.getMinZ());
double volume = xLength * yLength * zLength;
double ratio = -5.714285653084517E-4;
double rate = (volume - 125.0) * ratio;
return MathUtil.clamp(rate + 0.75, 0.25, 0.75);
}
private static boolean isPrefabBlockIntact(
LocalCachedChunkAccessor chunkAccessor,
int worldX,
int worldY,
int worldZ,
int blockX,
int blockY,
int blockZ,
int blockId,
int rotation,
PrefabRotation prefabRotation
) {
int globalX = prefabRotation.getX(blockX, blockZ) + worldX;
int globalY = blockY + worldY;
int globalZ = prefabRotation.getZ(blockX, blockZ) + worldZ;
BlockType block = BlockType.getAssetMap().getAsset(blockId);
if (block.getMaterial() == BlockMaterial.Empty) {
return true;
} else {
WorldChunk chunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(globalX, globalZ));
if (chunk == null) {
return false;
} else {
int worldBlockId = chunk.getBlock(globalX, globalY, globalZ);
if (worldBlockId != blockId) {
return false;
} else {
int expectedRotation = prefabRotation.getRotation(rotation);
int worldRotation = chunk.getRotationIndex(globalX, globalY, globalZ);
return worldRotation == expectedRotation;
}
}
}
}
private static boolean isPrefabIntact(
IPrefabBuffer prefabBuffer, LocalCachedChunkAccessor chunkAccessor, int worldX, int worldY, int worldZ, PrefabRotation prefabRotation, FastRandom random
) {
return prefabBuffer.forEachRaw(
IPrefabBuffer.iterateAllColumns(),
(blockX, blockY, blockZ, blockId, chance, holder, supportValue, rotation, filler, t) -> isPrefabBlockIntact(
chunkAccessor, worldX, worldY, worldZ, blockX, blockY, blockZ, blockId, rotation, prefabRotation
),
(fluidX, fluidY, fluidZ, fluidId, level, o) -> true,
null,
new PrefabBufferCall(random, prefabRotation)
);
}
public IWeightedMap<PrefabFarmingStageData.PrefabStage> getPrefabStages() {
return this.prefabStages;
}
@Override
public void apply(
ComponentAccessor<ChunkStore> commandBuffer,
Ref<ChunkStore> sectionRef,
Ref<ChunkStore> blockRef,
int x,
int y,
int z,
@Nullable FarmingStageData previousStage
) {
FarmingBlock farming = commandBuffer.getComponent(blockRef, FarmingBlock.getComponentType());
IPrefabBuffer prefabBuffer = this.getCachedPrefab(x, y, z, farming.getGeneration());
BlockSection blockSection = commandBuffer.getComponent(sectionRef, BlockSection.getComponentType());
int randomRotation = HashUtil.randomInt((long)x, (long)y, (long)z, Rotation.VALUES.length);
RotationTuple yaw = RotationTuple.of(Rotation.VALUES[randomRotation], Rotation.None);
World world = commandBuffer.getExternalData().getWorld();
ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
int worldX = ChunkUtil.worldCoordFromLocalCoord(section.getX(), x);
int worldY = ChunkUtil.worldCoordFromLocalCoord(section.getY(), y);
int worldZ = ChunkUtil.worldCoordFromLocalCoord(section.getZ(), z);
if (farming.getPreviousBlockType() == null) {
farming.setPreviousBlockType(BlockType.getAssetMap().getAsset(blockSection.get(x, y, z)).getId());
}
double xLength = (double)(prefabBuffer.getMaxX() - prefabBuffer.getMinX());
double zLength = (double)(prefabBuffer.getMaxZ() - prefabBuffer.getMinZ());
int prefabRadius = (int)MathUtil.fastFloor(0.5 * Math.sqrt(xLength * xLength + zLength * zLength));
LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, x, z, prefabRadius);
FastRandom random = new FastRandom();
PrefabRotation prefabRotation = PrefabRotation.fromRotation(yaw.yaw());
BlockTypeAssetMap<String, BlockType> blockTypeMap = BlockType.getAssetMap();
if (previousStage instanceof PrefabFarmingStageData oldPrefab) {
IPrefabBuffer oldPrefabBuffer = oldPrefab.getCachedPrefab(worldX, worldY, worldZ, farming.getGeneration() - 1);
double brokenParticlesRate = computeParticlesRate(prefabBuffer);
world.execute(
() -> {
boolean isIntact = isPrefabIntact(oldPrefabBuffer, chunkAccessor, worldX, worldY, worldZ, prefabRotation, random);
if (isIntact) {
boolean isUnobstructed = prefabBuffer.compare(
(px, py, pz, blockId, stateWrapper, chance, rotation, filler, secondBlockId, secondStateWrapper, secondChance, secondRotation, secondFiller, prefabBufferCall) -> {
int bx = worldX + px;
int by = worldY + py;
int bz = worldZ + pz;
if ((secondBlockId == 0 || secondBlockId == -2147483648) && blockId != 0 && blockId != -2147483648) {
WorldChunk nonTickingChunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz));
int worldBlock = nonTickingChunk.getBlock(bx, by, bz);
return !this.doesBlockObstruct(blockId, worldBlock);
} else {
return true;
}
},
new PrefabBufferCall(random, prefabRotation),
oldPrefabBuffer
);
if (isUnobstructed) {
prefabBuffer.compare(
(px, py, pz, blockId, stateWrapper, chance, rotation, filler, secondBlockId, secondStateWrapper, secondChance, secondRotation, secondFiller, prefabBufferCall) -> {
int bx = worldX + px;
int by = worldY + py;
int bz = worldZ + pz;
WorldChunk nonTickingChunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz));
int updatedSetBlockSettings = 2;
if (random.nextDouble() > brokenParticlesRate) {
updatedSetBlockSettings |= 4;
}
if (blockId != 0 && blockId != -2147483648) {
BlockType block = blockTypeMap.getAsset(blockId);
if (filler != 0) {
return true;
}
int worldBlock = nonTickingChunk.getBlock(bx, by, bz);
if ((secondBlockId == 0 || secondBlockId == -2147483648) && !this.canReplace(worldBlock, blockTypeMap)) {
return true;
}
nonTickingChunk.setBlock(bx, by, bz, blockId, block, rotation, filler, updatedSetBlockSettings);
if (stateWrapper != null) {
nonTickingChunk.setState(bx, by, bz, stateWrapper.clone());
}
} else if (secondBlockId != 0 && secondBlockId != -2147483648) {
nonTickingChunk.breakBlock(bx, by, bz, updatedSetBlockSettings);
}
return true;
},
new PrefabBufferCall(random, prefabRotation),
oldPrefabBuffer
);
}
}
}
);
} else {
super.apply(commandBuffer, sectionRef, blockRef, x, y, z, previousStage);
world.execute(
() -> {
boolean isUnObstructed = prefabBuffer.forEachRaw(
IPrefabBuffer.iterateAllColumns(), (blockX, blockY, blockZ, blockId, chance, holder, supportValue, rotation, filler, t) -> {
int bx = worldX + prefabRotation.getX(blockX, blockZ);
int by = worldY + blockY;
int bz = worldZ + prefabRotation.getX(blockZ, blockX);
if (blockId != 0 && blockId != -2147483648) {
int worldBlock = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz)).getBlock(bx, by, bz);
return !this.doesBlockObstruct(blockId, worldBlock);
} else {
return true;
}
}, (fluidX, fluidY, fluidZ, fluidId, level, o) -> true, null, new PrefabBufferCall(random, prefabRotation)
);
if (isUnObstructed) {
prefabBuffer.forEach(
IPrefabBuffer.iterateAllColumns(), (blockX, blockY, blockZ, blockId, holder, supportValue, rotation, filler, t, fluidId, fluidLevel) -> {
int bx = worldX + blockX;
int by = worldY + blockY;
int bz = worldZ + blockZ;
WorldChunk nonTickingChunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz));
int updatedSetBlockSettings = 2;
if (blockId != 0 && blockId != -2147483648) {
BlockType block = blockTypeMap.getAsset(blockId);
if (filler != 0) {
return;
}
int worldBlock = nonTickingChunk.getBlock(bx, by, bz);
if (!this.canReplace(worldBlock, blockTypeMap)) {
return;
}
nonTickingChunk.setBlock(bx, by, bz, blockId, block, rotation, filler, updatedSetBlockSettings);
if (holder != null) {
nonTickingChunk.setState(bx, by, bz, holder.clone());
}
}
}, null, null, new PrefabBufferCall(random, prefabRotation)
);
}
}
);
}
}
private boolean doesBlockObstruct(int blockId, int worldBlockId) {
BlockTypeAssetMap<String, BlockType> assetMap = BlockType.getAssetMap();
BlockType blockType = assetMap.getAsset(blockId);
return blockType != null && blockType.getMaterial() != BlockMaterial.Empty ? !this.canReplace(worldBlockId, assetMap) : false;
}
private boolean canReplace(int worldBlockId, BlockTypeAssetMap<String, BlockType> assetMap) {
BlockType worldBlockType = assetMap.getAsset(worldBlockId);
if (worldBlockType != null && worldBlockType.getMaterial() != BlockMaterial.Empty) {
for (int tagIndex : this.replaceMaskTagIndices) {
if (assetMap.getIndexesForTag(tagIndex).contains(worldBlockId)) {
return true;
}
}
return false;
} else {
return true;
}
}
@Override
public void remove(ComponentAccessor<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, Ref<ChunkStore> blockRef, int x, int y, int z) {
super.remove(commandBuffer, sectionRef, blockRef, x, y, z);
ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
int worldX = ChunkUtil.worldCoordFromLocalCoord(section.getX(), x);
int worldY = ChunkUtil.worldCoordFromLocalCoord(section.getY(), y);
int worldZ = ChunkUtil.worldCoordFromLocalCoord(section.getZ(), z);
FarmingBlock farming = commandBuffer.getComponent(blockRef, FarmingBlock.getComponentType());
IPrefabBuffer prefab = this.getCachedPrefab(worldX, worldY, worldZ, farming.getGeneration() - 1);
RotationTuple rotation = commandBuffer.getComponent(sectionRef, BlockSection.getComponentType()).getRotation(x, y, z);
double rate = computeParticlesRate(prefab);
World world = commandBuffer.getExternalData().getWorld();
world.execute(() -> PrefabUtil.remove(prefab, world, new Vector3i(worldX, worldY, worldZ), rotation.yaw(), true, new FastRandom(), 2, rate));
}
@Nonnull
private IPrefabBuffer getCachedPrefab(int x, int y, int z, int generation) {
return PrefabBufferUtil.getCached(this.prefabStages.get(HashUtil.random((long)x, (long)y, (long)z, (long)generation)).getResolvedPath());
}
private void processConfig() {
this.replaceMaskTagIndices = new int[this.replaceMaskTags.length];
for (int i = 0; i < this.replaceMaskTags.length; i++) {
this.replaceMaskTagIndices[i] = AssetRegistry.getOrCreateTagIndex(this.replaceMaskTags[i]);
}
}
@Override
public String toString() {
return "PrefabFarmingStageData{replaceMaskTags=" + Arrays.toString((Object[])this.replaceMaskTags) + ", prefabStages=" + this.prefabStages + "}";
}
public static class PrefabStage implements IWeightedElement {
public static final PrefabFarmingStageData.PrefabStage[] EMPTY_ARRAY = new PrefabFarmingStageData.PrefabStage[0];
@Nonnull
public static Codec<PrefabFarmingStageData.PrefabStage> CODEC = BuilderCodec.builder(
PrefabFarmingStageData.PrefabStage.class, PrefabFarmingStageData.PrefabStage::new
)
.append(new KeyedCodec<>("Weight", Codec.INTEGER), (prefabStage, integer) -> prefabStage.weight = integer, prefabStage -> prefabStage.weight)
.addValidator(Validators.greaterThanOrEqual(1))
.add()
.<String>append(new KeyedCodec<>("Path", Codec.STRING), (prefabStage, s) -> prefabStage.path = s, prefabStage -> prefabStage.path)
.addValidator(Validators.nonNull())
.add()
.build();
protected int weight = 1;
protected String path;
public PrefabStage() {
}
@Override
public double getWeight() {
return (double)this.weight;
}
@Nonnull
public Path getResolvedPath() {
for (AssetPack pack : AssetModule.get().getAssetPacks()) {
Path assetPath = pack.getRoot().resolve("Server").resolve("Prefabs").resolve(this.path);
if (Files.exists(assetPath)) {
return assetPath;
}
}
return PrefabStore.get().getAssetPrefabsPath().resolve(this.path);
}
@Nonnull
@Override
public String toString() {
return "PrefabStage{weight=" + this.weight + ", path='" + this.path + "'}";
}
}
}