Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make world generation thread safe #2318

Open
wants to merge 7 commits into
base: 1.21.1
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/main/java/twilightforest/ASMHooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import net.minecraft.world.item.*;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeResolver;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
Expand All @@ -51,6 +52,7 @@
import twilightforest.init.TFDataComponents;
import twilightforest.init.custom.ChunkBlanketProcessors;
import twilightforest.util.WorldUtil;
import twilightforest.world.components.biomesources.TFBiomeProvider;
import twilightforest.world.components.structures.CustomDensitySource;
import twilightforest.world.components.structures.util.CustomStructureData;

Expand Down Expand Up @@ -166,6 +168,20 @@ public static void chunkBlanketing(ChunkAccess chunkAccess, WorldGenRegion world
ChunkBlanketProcessors.chunkBlanketing(chunkAccess, worldGenRegion);
}

/**
* {@link twilightforest.asm.transformers.chunk.NoiseBasedChunkGeneratorTransformer}
*
* Injection Point:<br/>
* {@link net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator#doCreateBiomes}
*/
public static BiomeResolver tryRecreateBiomeResolver(BiomeResolver resolver) {
if (resolver instanceof TFBiomeProvider tfBiomeProvider) {
return tfBiomeProvider.recreate();
} else {
return resolver;
}
}

// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cloud
// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ public BiomeDensitySource getBiomeTerrain() {
return this.biomeTerrainDataHolder.value();
}

public TFBiomeProvider recreate() {
return new TFBiomeProvider(new Holder.Direct<>(this.biomeTerrainDataHolder.value().recreate()));
}

@Override
public void addDebugInfo(List<String> info, BlockPos cameraPos, Climate.Sampler sampler) {
super.addDebugInfo(info, cameraPos, sampler);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public DensityFunction baseOffset() {
@Override // NoiseChunk is the only class to ever call this, and it's typically a new chunk each time
public DensityFunction mapAll(Visitor visitor) {
return visitor.apply(new ChunkCachedDensityRouter(
this.biomeDensitySourceHolder,
new Holder.Direct<>(this.biomeDensitySourceHolder.value().recreate()), // isolate LazyArea cache between chunks
visitor.visitNoise(this.noise),
this.lowerDensityBound,
this.upperDensityBound,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.DirectionalBlock;
import net.minecraft.world.level.block.HugeMushroomBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.AbstractHugeMushroomFeature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
import net.minecraft.world.level.levelgen.feature.configurations.HugeMushroomFeatureConfiguration;
import org.apache.commons.lang3.mutable.MutableInt;
import twilightforest.init.TFBlocks;
import twilightforest.util.features.FeatureLogic;
import twilightforest.util.iterators.VoxelBresenhamIterator;
Expand All @@ -19,8 +21,6 @@

@ParametersAreNonnullByDefault
public abstract class CanopyMushroomFeature extends AbstractHugeMushroomFeature {
private int bugsLeft;

public CanopyMushroomFeature(Codec<HugeMushroomFeatureConfiguration> featureConfigurationCodec) {
super(featureConfigurationCodec);
}
Expand All @@ -35,12 +35,16 @@ protected int getTreeRadiusForHeight(int i, int i1, int foliageRadius, int treeH

@Override
protected void placeTrunk(LevelAccessor levelAccessor, RandomSource random, BlockPos pos, HugeMushroomFeatureConfiguration featureConfiguration, int height, BlockPos.MutableBlockPos mutableBlockPos) {
throw new UnsupportedOperationException();
}

protected void placeTrunk(LevelAccessor levelAccessor, RandomSource random, BlockPos pos, HugeMushroomFeatureConfiguration featureConfiguration, int height, BlockPos.MutableBlockPos mutableBlockPos, MutableInt bugsLeft) {
for (int i = 0; i < height; ++i) {
mutableBlockPos.set(pos).move(Direction.UP, i);
if (!levelAccessor.getBlockState(mutableBlockPos).isSolidRender(levelAccessor, mutableBlockPos)) {
this.setBlock(levelAccessor, mutableBlockPos, featureConfiguration.stemProvider.getState(random, pos));

if (this.bugsLeft > 0 && i > height / 2 && random.nextInt(10) == 9) addFirefly(levelAccessor, mutableBlockPos, random);
if (bugsLeft.intValue() > 0 && i > height / 2 && random.nextInt(10) == 9) addFirefly(levelAccessor, mutableBlockPos, random, bugsLeft);
} else {
height = i;
break;
Expand All @@ -50,22 +54,22 @@ protected void placeTrunk(LevelAccessor levelAccessor, RandomSource random, Bloc
int numBranches = this.getBranches(random);
float offset = random.nextFloat();
for (int b = 0; b < numBranches; b++) {
buildABranch(levelAccessor, pos, height - 6 + b, this.getLength(random), 0.3 * b + offset, random, new HugeMushroomFeatureConfiguration(featureConfiguration.capProvider, featureConfiguration.stemProvider, featureConfiguration.foliageRadius - 1));
buildABranch(levelAccessor, pos, height - 6 + b, this.getLength(random), 0.3 * b + offset, random, new HugeMushroomFeatureConfiguration(featureConfiguration.capProvider, featureConfiguration.stemProvider, featureConfiguration.foliageRadius - 1), bugsLeft);
}
}

/**
* Add a firefly on a RandomSource face of a block
*/
protected void addFirefly(LevelAccessor levelAccessor, BlockPos pos, RandomSource random) {
protected void addFirefly(LevelAccessor levelAccessor, BlockPos pos, RandomSource random, MutableInt bugsLeft) {
Direction direction = Direction.getRandom(random);
if (direction.getAxis() != Direction.Axis.Y) {
BlockPos.MutableBlockPos bugPos = new BlockPos.MutableBlockPos();
bugPos.set(pos).move(direction);
if (!levelAccessor.getBlockState(bugPos).isSolidRender(levelAccessor, bugPos)) {
BlockState bugState = TFBlocks.FIREFLY.get().defaultBlockState().setValue(DirectionalBlock.FACING, direction);
this.setBlock(levelAccessor, bugPos, bugState);
this.bugsLeft--;
bugsLeft.decrement();
}
}
}
Expand All @@ -79,7 +83,7 @@ protected int getTreeHeight(RandomSource random) {

protected abstract double getLength(RandomSource random);

private void buildABranch(LevelAccessor levelAccessor, BlockPos pos, int height, double length, double angle, RandomSource random, HugeMushroomFeatureConfiguration featureConfiguration) {
private void buildABranch(LevelAccessor levelAccessor, BlockPos pos, int height, double length, double angle, RandomSource random, HugeMushroomFeatureConfiguration featureConfiguration, MutableInt bugsLeft) {
BlockPos src = pos.above(height);
BlockPos dest = FeatureLogic.translate(src, length, angle, 0.2);

Expand All @@ -106,7 +110,7 @@ private void buildABranch(LevelAccessor levelAccessor, BlockPos pos, int height,

this.setBlock(levelAccessor, blockPos, blockstate);

if (this.bugsLeft > 0 && i > Math.min(src.getY(), dest.getY()) / 2 && random.nextInt(20) == 0) addFirefly(levelAccessor, blockPos, random);
if (bugsLeft.intValue() > 0 && i > Math.min(src.getY(), dest.getY()) / 2 && random.nextInt(20) == 0) addFirefly(levelAccessor, blockPos, random, bugsLeft);
}

this.makeCap(levelAccessor, random, dest, 1, new BlockPos.MutableBlockPos(), featureConfiguration);//Branches need caps as well, height in this case is set to 1
Expand All @@ -132,7 +136,21 @@ protected void makeCap(LevelAccessor levelAccessor, RandomSource random, BlockPo

@Override
public boolean place(FeaturePlaceContext<HugeMushroomFeatureConfiguration> context) {
this.bugsLeft = Math.max(0, context.random().nextInt(10) - 4) / 2; //Weird math, I know, but I like the odds (and weird math, sue me)
return super.place(context);
MutableInt bugsLeft = new MutableInt(Math.max(0, context.random().nextInt(10) - 4) / 2); //Weird math, I know, but I like the odds (and weird math, sue me)

// [VanillaCopy] AbstractHugeMushroomFeature.place, passing bugsLeft as parameter
WorldGenLevel worldgenlevel = context.level();
BlockPos blockpos = context.origin();
RandomSource randomsource = context.random();
HugeMushroomFeatureConfiguration hugemushroomfeatureconfiguration = context.config();
int i = this.getTreeHeight(randomsource);
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();
if (!this.isValidPosition(worldgenlevel, blockpos, i, blockpos$mutableblockpos, hugemushroomfeatureconfiguration)) {
return false;
} else {
this.makeCap(worldgenlevel, randomsource, blockpos, i, blockpos$mutableblockpos, hugemushroomfeatureconfiguration);
this.placeTrunk(worldgenlevel, randomsource, blockpos, hugemushroomfeatureconfiguration, i, blockpos$mutableblockpos, bugsLeft);
return true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public BiomeDensitySource(Map<ResourceKey<Biome>, TerrainColumn> list, Holder<Bi
this.biomeList = list;
}

public BiomeDensitySource recreate() {
return new BiomeDensitySource(this.biomeList, this.genBiomeConfig);
}

private Holder<BiomeLayerFactory> getBiomeConfig() {
return this.genBiomeConfig;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*
* @author Ben
*/
public class TFMaze {
public class TFMaze implements Cloneable {

public final int width; // cells wide (x)
public final int depth; // cells deep (z)
Expand Down Expand Up @@ -63,7 +63,7 @@ public class TFMaze {
public static final int ROOM = 5;
public static final int DOOR = 6;

public final RandomSource rand;
public RandomSource rand;

public TFMaze(int cellsWidth, int cellsDepth, RandomSource random) {
// default values
Expand Down Expand Up @@ -576,4 +576,15 @@ public void rbGen(int sx, int sz) {
rbGen(sx, sz);
rbGen(sx, sz);
}

@Override
public TFMaze clone() {
try {
TFMaze clone = (TFMaze) super.clone();
clone.rand = RandomSource.create();
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class FinalCastleMuralComponent extends TFStructureComponentOld {
private int width;

// we will model the mural in this byte array
private byte[][] mural;
private volatile byte[][] mural;

public FinalCastleMuralComponent(StructurePieceSerializationContext ctx, CompoundTag nbt) {
super(TFStructurePieceTypes.TFFCMur.get(), nbt);
Expand All @@ -37,34 +37,40 @@ public FinalCastleMuralComponent(int i, int x, int y, int z, int width, int heig

@Override
public void postProcess(WorldGenLevel world, StructureManager manager, ChunkGenerator generator, RandomSource rand, BoundingBox sbb, ChunkPos chunkPosIn, BlockPos blockPos) {
this.height = this.boundingBox.getYSpan();
this.width = (this.getOrientation() == Direction.SOUTH || this.getOrientation() == Direction.NORTH) ? this.boundingBox.getZSpan() : this.boundingBox.getXSpan();

RandomSource decoRNG = RandomSource.create(world.getSeed() + (this.boundingBox.minX() * 321534781L) ^ (this.boundingBox.minZ() * 756839L));

if (mural == null) {
// only make it once
mural = new byte[width][height];
if (this.mural == null) {
synchronized (this) {
if (this.mural == null) {
this.height = this.boundingBox.getYSpan();
this.width = (this.getOrientation() == Direction.SOUTH || this.getOrientation() == Direction.NORTH) ? this.boundingBox.getZSpan() : this.boundingBox.getXSpan();

int startX = width / 2 - 1;
int startY = 2;
// only make it once
byte[][] mural = new byte[width][height];

// make mural, fill in dot by start
for (int x = -1; x < 2; x++) {
for (int y = -1; y < 2; y++) {
mural[startX + x][startY + y] = 1;
}
}
int startX = width / 2 - 1;
int startY = 2;

// side branches
makeHorizontalTree(decoRNG, mural, startX + 1, startY, decoRNG.nextInt(width / 6) + width / 6, true);
makeHorizontalTree(decoRNG, mural, startX - 1, startY, decoRNG.nextInt(width / 6) + width / 6, false);
// make mural, fill in dot by start
for (int x = -1; x < 2; x++) {
for (int y = -1; y < 2; y++) {
mural[startX + x][startY + y] = 1;
}
}

// main tree
makeVerticalTree(decoRNG, mural, startX, startY + 1, decoRNG.nextInt(height / 6) + height / 6, true);
// side branches
makeHorizontalTree(decoRNG, mural, startX + 1, startY, decoRNG.nextInt(width / 6) + width / 6, true);
makeHorizontalTree(decoRNG, mural, startX - 1, startY, decoRNG.nextInt(width / 6) + width / 6, false);

// stripes
makeStripes(decoRNG);
// main tree
makeVerticalTree(decoRNG, mural, startX, startY + 1, decoRNG.nextInt(height / 6) + height / 6, true);

this.mural = mural;

// stripes
makeStripes(decoRNG);
}
}
}

final BlockState castleMagic = TFBlocks.YELLOW_CASTLE_RUNE_BRICK.get().defaultBlockState();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public MinotaurMazeComponent(StructurePieceSerializationContext ctx, CompoundTag

// recreate maze object
maze = new TFMaze(getMazeSize(), getMazeSize(), RandomSource.create());
setFixedMazeSeed();
setFixedMazeSeed(maze);

// blank out rcoords above 1 so that the room generation works properly
//TODO: re-do this. :)
Expand All @@ -62,7 +62,7 @@ public MinotaurMazeComponent(int index, int x, int y, int z, int entranceX, int
maze = new TFMaze(getMazeSize(), getMazeSize(), random);

// set the seed to a fixed value based on this maze's x and z
setFixedMazeSeed();
setFixedMazeSeed(maze);

// rooms
int nrooms = 7;
Expand Down Expand Up @@ -95,7 +95,7 @@ private void addRoomsToMaze(int entranceX, int entranceZ, int nrooms) {
}
}

private void setFixedMazeSeed() {
private void setFixedMazeSeed(TFMaze maze) {
maze.setSeed(this.boundingBox.minX() * 90342903L + this.boundingBox.minY() * 90342903L ^ this.boundingBox.minZ());
}

Expand Down Expand Up @@ -282,18 +282,21 @@ public void postProcess(WorldGenLevel world, StructureManager manager, ChunkGene
generateBox(world, sbb, 1, 5, 1, getDiameter() + 1, 5, getDiameter() + 1, TFBlocks.MAZESTONE.get().defaultBlockState(), stone, onlyReplaceCeiling);
generateBox(world, sbb, 1, 0, 1, getDiameter() + 1, 0, getDiameter() + 1, TFBlocks.MAZESTONE_MOSAIC.get().defaultBlockState(), stone, false);

maze.headBlockState = TFBlocks.DECORATIVE_MAZESTONE.get().defaultBlockState();
maze.wallBlockState = TFBlocks.MAZESTONE_BRICK.get().defaultBlockState();
maze.rootBlockState = TFBlocks.DECORATIVE_MAZESTONE.get().defaultBlockState();
maze.pillarBlockState = TFBlocks.CUT_MAZESTONE.get().defaultBlockState();
maze.wallBlocks = new MazestoneProcessor();
maze.torchRarity = 0.05F;
maze.tall = 2;
maze.head = 1;
maze.roots = 1;
maze.oddBias = 4;

maze.copyToStructure(world, manager, generator, 1, 2, 1, this, sbb);
TFMaze maze1 = maze.clone();
setFixedMazeSeed(maze1);

maze1.headBlockState = TFBlocks.DECORATIVE_MAZESTONE.get().defaultBlockState();
maze1.wallBlockState = TFBlocks.MAZESTONE_BRICK.get().defaultBlockState();
maze1.rootBlockState = TFBlocks.DECORATIVE_MAZESTONE.get().defaultBlockState();
maze1.pillarBlockState = TFBlocks.CUT_MAZESTONE.get().defaultBlockState();
maze1.wallBlocks = new MazestoneProcessor();
maze1.torchRarity = 0.05F;
maze1.tall = 2;
maze1.head = 1;
maze1.roots = 1;
maze1.oddBias = 4;

maze1.copyToStructure(world, manager, generator, 1, 2, 1, this, sbb);
}

public int getMazeSize() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/**
* Based off StructureStrongholdPieceWeight
*/
public class StrongholdPieceWeight {
public class StrongholdPieceWeight implements Cloneable {

public final Factory<? extends KnightStrongholdComponent> factory;
public final int pieceWeight;
Expand Down Expand Up @@ -36,4 +36,14 @@ public boolean canSpawnMoreStructures() {
return this.instancesLimit == 0 || this.instancesSpawned < this.instancesLimit;
}

@Override
public StrongholdPieceWeight clone() {
try {
StrongholdPieceWeight clone = (StrongholdPieceWeight) super.clone();
clone.instancesSpawned = 0;
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ public void prepareStructurePieces() {
pieceList = new ArrayList<>();

for (StrongholdPieceWeight piece : pieceWeightArray) {
piece.instancesSpawned = 0;
pieceList.add(piece);
StrongholdPieceWeight cloned = piece.clone();
cloned.instancesSpawned = 0;
pieceList.add(cloned);
}
}

Expand Down
Loading