package com.hypixel.hytale.server.worldgen.chunk;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.math.util.ChunkUtil;
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
import com.hypixel.hytale.server.core.asset.type.fluid.Fluid;
import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics;
import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.core.universe.world.worldgen.GeneratedBlockChunk;
import com.hypixel.hytale.server.core.universe.world.worldgen.GeneratedBlockStateChunk;
import com.hypixel.hytale.server.core.universe.world.worldgen.GeneratedEntityChunk;
import com.hypixel.hytale.server.core.universe.world.worldgen.WorldGenTimingsCollector;
import com.hypixel.hytale.server.worldgen.cache.CoreDataCacheEntry;
import com.hypixel.hytale.server.worldgen.chunk.populator.BlockPopulator;
import com.hypixel.hytale.server.worldgen.chunk.populator.CavePopulator;
import com.hypixel.hytale.server.worldgen.chunk.populator.PrefabPopulator;
import com.hypixel.hytale.server.worldgen.chunk.populator.WaterPopulator;
import com.hypixel.hytale.server.worldgen.util.BlockFluidEntry;
import javax.annotation.Nonnull;
public class ChunkGeneratorExecution {
@Nonnull
private final ChunkGenerator chunkGenerator;
private final GeneratedBlockChunk blockChunk;
private final GeneratedBlockStateChunk blockStateChunk;
private final GeneratedEntityChunk entityChunk;
private final Holder<ChunkStore>[] sections;
@Nonnull
private final BlockPriorityChunk priorityChunk;
@Nonnull
private final HeightThresholdInterpolator interpolator;
private BlockPriorityModifier blockPriorityModifier;
public ChunkGeneratorExecution(int seed, @Nonnull ChunkGenerator chunkGenerator, GeneratedBlockChunk blockChunk, GeneratedBlockStateChunk blockStateChunk, GeneratedEntityChunk entityChunk, Holder<ChunkStore>[] sections) {
this.blockPriorityModifier = BlockPriorityModifier.NONE;
this.chunkGenerator = chunkGenerator;
this.blockChunk = blockChunk;
this.blockStateChunk = blockStateChunk;
this.entityChunk = entityChunk;
this.sections = sections;
this.priorityChunk = ChunkGenerator.getResource().priorityChunk.reset();
long start = -System.nanoTime();
this.interpolator = (new HeightThresholdInterpolator(this)).populate(seed);
chunkGenerator.getTimings().reportPrepare(start + System.nanoTime());
}
public void execute(int seed) {
WorldGenTimingsCollector timings = this.chunkGenerator.getTimings();
this.generateTintMapping(seed);
this.generateEnvironmentMapping(seed);
long start = -System.nanoTime();
BlockPopulator.populate(seed, this);
timings.reportBlocksGeneration(start + System.nanoTime());
start = -System.nanoTime();
CavePopulator.populate(seed, this);
timings.reportCaveGeneration(start + System.nanoTime());
start = -System.nanoTime();
PrefabPopulator.populate(seed, this);
timings.reportPrefabGeneration(start + System.nanoTime());
WaterPopulator.populate(seed, this);
}
@Nonnull
public ChunkGenerator getChunkGenerator() {
return this.chunkGenerator;
}
public GeneratedBlockChunk getChunk() {
return this.blockChunk;
}
public GeneratedBlockStateChunk getBlockStateChunk() {
return this.blockStateChunk;
}
public GeneratedEntityChunk getEntityChunk() {
return this.entityChunk;
}
@Nonnull
public BlockPriorityChunk getPriorityChunk() {
return this.priorityChunk;
}
@Nonnull
public HeightThresholdInterpolator getInterpolator() {
return this.interpolator;
}
public Holder<ChunkStore> getSection(int y) {
return this.sections[y];
}
public ZoneBiomeResult zoneBiomeResult(int cx, int cz) {
return this.interpolator.zoneBiomeResult(cx, cz);
}
@Nonnull
public CoreDataCacheEntry[] getCoreDataEntries() {
return this.interpolator.getEntries();
}
public long getIndex() {
return this.blockChunk.getIndex();
}
public int getX() {
return this.blockChunk.getX();
}
public int getZ() {
return this.blockChunk.getZ();
}
public void setPriorityModifier(BlockPriorityModifier blockPriorityModifier) {
this.blockPriorityModifier = blockPriorityModifier;
}
private void generateTintMapping(int seed) {
int radius = 4;
int[] rawTint = new int[(32 + radius * 2) * (32 + radius * 2)];
int m = 32 + radius;
for(int cx = -radius; cx < m; ++cx) {
for(int cz = -radius; cz < m; ++cz) {
rawTint[tintIndexLocal(cx, cz)] = this.zoneBiomeResult(cx, cz).getBiome().getTintContainer().getTintColorAt(seed, this.globalX(cx), this.globalZ(cz));
}
}
m = radius * radius;
for(int cx = 0; cx < 32; ++cx) {
for(int cz = 0; cz < 32; ++cz) {
int r = 0;
int g = 0;
int b = 0;
int counter = 0;
for(int ix = -radius; ix <= radius; ++ix) {
for(int iz = -radius; iz <= radius; ++iz) {
if (ix * ix + iz * iz <= m) {
int c = rawTint[tintIndexLocal(cx + ix, cz + iz)];
r += c >> 16 & 255;
g += c >> 8 & 255;
b += c & 255;
++counter;
}
}
}
if (counter > 0) {
r /= counter;
g /= counter;
b /= counter;
this.blockChunk.setTint(cx, cz, -16777216 | r << 16 | g << 8 | b);
} else {
this.blockChunk.setTint(cx, cz, -65536);
}
}
}
}
public static int tintIndexLocal(int x, int z) {
return (x + 4) * 40 + z + 4;
}
private void generateEnvironmentMapping(int seed) {
for(int cx = 0; cx < 32; ++cx) {
for(int cz = 0; cz < 32; ++cz) {
int envId = this.zoneBiomeResult(cx, cz).getBiome().getEnvironmentContainer().getEnvironmentAt(seed, this.globalX(cx), this.globalZ(cz));
this.blockChunk.setEnvironmentColumn(cx, cz, envId);
}
}
}
public int getBlock(int x, int y, int z) {
return this.blockChunk.getBlock(x, y, z);
}
public int getRotationIndex(int x, int y, int z) {
return this.blockChunk.getRotationIndex(x, y, z);
}
public void setEnvironment(int x, int y, int z, int environment) {
if (environment != -2147483648) {
this.blockChunk.setEnvironment(x, y, z, environment);
}
}
public boolean setBlock(int x, int y, int z, byte type, int block) {
return this.setBlock(x, y, z, type, block, (Holder)null);
}
public boolean setBlock(int x, int y, int z, byte type, BlockFluidEntry entry) {
return this.setBlock(x, y, z, type, entry.blockId(), (Holder)null, -1, entry.rotation(), 0);
}
public boolean setBlock(int x, int y, int z, byte type, int block, int environment) {
if (this.setBlock(x, y, z, type, block, (Holder)null)) {
this.setEnvironment(x, y, z, environment);
return true;
} else {
return false;
}
}
public boolean setBlock(int x, int y, int z, byte type, BlockFluidEntry entry, int environment) {
if (this.setBlock(x, y, z, type, entry.blockId(), (Holder)null, -1, entry.rotation(), 0)) {
this.setEnvironment(x, y, z, environment);
return true;
} else {
return false;
}
}
public boolean setBlock(int x, int y, int z, byte type, int block, Holder<ChunkStore> holder) {
return this.setBlock(x, y, z, type, block, holder, -1, 0, 0);
}
public boolean setBlock(int x, int y, int z, byte type, int block, Holder<ChunkStore> holder, int supportValue, int rotation, int filler) {
if (y >= 0 && y < 320) {
byte newPriority = (byte)(type & 31);
byte newFlags = (byte)(type & -32);
byte oldPriority = this.priorityChunk.get(x, y, z);
byte oldModified = this.blockPriorityModifier.modifyCurrent(oldPriority, newPriority);
if (type == -1) {
newPriority = oldModified;
}
if (newPriority < oldModified) {
return false;
} else {
newPriority = (byte)(this.blockPriorityModifier.modifyTarget(oldPriority, newPriority) | newFlags);
this.priorityChunk.set(x, y, z, newPriority);
this.blockChunk.setBlock(x, y, z, block, rotation, filler);
this.blockStateChunk.setState(x, y, z, holder);
Holder<ChunkStore> section = this.getSection(ChunkUtil.chunkCoordinate(y));
if (supportValue >= 0) {
BlockPhysics.setSupportValue(section, x, y, z, supportValue);
} else {
BlockType blockType = (BlockType)BlockType.getAssetMap().getAsset(block);
if (blockType != null && blockType.hasSupport()) {
BlockPhysics.reset(section, x, y, z);
} else {
BlockPhysics.clear(section, x, y, z);
}
}
return true;
}
} else {
return false;
}
}
public boolean setFluid(int x, int y, int z, byte type, int fluid, int environment) {
if (this.setFluid(x, y, z, type, fluid)) {
this.setEnvironment(x, y, z, environment);
return true;
} else {
return false;
}
}
public boolean setFluid(int x, int y, int z, byte type, int fluid) {
return this.setFluid(x, y, z, type, fluid, (byte)((Fluid)Fluid.getAssetMap().getAsset(fluid)).getMaxFluidLevel());
}
public boolean setFluid(int x, int y, int z, byte type, int fluid, byte fluidLevel) {
if (y >= 0 && y < 320) {
byte newPriority = (byte)(type & 31);
byte newFlags = (byte)(type & -32);
byte oldPriority = this.priorityChunk.get(x, y, z);
byte oldModified = this.blockPriorityModifier.modifyCurrent(oldPriority, newPriority);
if (type == -1) {
newPriority = oldModified;
}
if (newPriority >= oldModified) {
newPriority = (byte)(this.blockPriorityModifier.modifyTarget(oldPriority, newPriority) | newFlags);
this.priorityChunk.set(x, y, z, newPriority);
Holder<ChunkStore> section = this.getSection(ChunkUtil.chunkCoordinate(y));
FluidSection fluidSection = (FluidSection)section.getComponent(FluidSection.getComponentType());
if (fluidSection == null) {
fluidSection = (FluidSection)section.ensureAndGetComponent(FluidSection.getComponentType());
}
fluidSection.setFluid(x, y, z, fluid, fluidLevel);
return true;
} else {
return false;
}
} else {
return false;
}
}
public int getFluid(int x, int y, int z) {
if (y >= 0 && y < 320) {
Holder<ChunkStore> section = this.getSection(ChunkUtil.chunkCoordinate(y));
FluidSection fluidSection = (FluidSection)section.ensureAndGetComponent(FluidSection.getComponentType());
return fluidSection.getFluidId(x, y, z);
} else {
return -2147483648;
}
}
public void overrideBlock(int x, int y, int z, byte type, int block) {
this.overrideBlock(x, y, z, type, block, (Holder)null);
}
public void overrideBlock(int x, int y, int z, byte type, int block, Holder<ChunkStore> holder) {
this.overrideBlock(x, y, z, type, block, holder, 0, 0);
}
public void overrideBlock(int x, int y, int z, byte type, BlockFluidEntry entry) {
this.overrideBlock(x, y, z, type, entry.blockId(), (Holder)null, entry.rotation(), 0);
}
public void overrideBlock(int x, int y, int z, byte type, int block, Holder<ChunkStore> holder, int rotation, int filler) {
this.priorityChunk.set(x, y, z, type);
this.blockChunk.setBlock(x, y, z, block, rotation, filler);
this.blockStateChunk.setState(x, y, z, holder);
Holder<ChunkStore> section = this.getSection(ChunkUtil.chunkCoordinate(y));
BlockType blockType = (BlockType)BlockType.getAssetMap().getAsset(block);
if (blockType != null && blockType.hasSupport()) {
BlockPhysics.reset(section, x, y, z);
} else {
BlockPhysics.clear(section, x, y, z);
}
}
public void overrideFluid(int x, int y, int z, byte type, int fluid) {
if (y >= 0 && y < 320) {
this.priorityChunk.set(x, y, z, type);
Holder<ChunkStore> section = this.getSection(ChunkUtil.chunkCoordinate(y));
FluidSection fluidSection = (FluidSection)section.ensureAndGetComponent(FluidSection.getComponentType());
fluidSection.setFluid(x, y, z, fluid, (byte)((Fluid)Fluid.getAssetMap().getAsset(fluid)).getMaxFluidLevel());
}
}
protected int localX(int x) {
return x - this.blockChunk.getX() * 32;
}
protected int localZ(int z) {
return z - this.blockChunk.getZ() * 32;
}
public int globalX(int localX) {
return ChunkUtil.minBlock(this.blockChunk.getX()) + localX;
}
public int globalZ(int localZ) {
return ChunkUtil.minBlock(this.blockChunk.getZ()) + localZ;
}
}