From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> Date: Tue, 4 Feb 2025 19:52:24 +0300 Subject: [PATCH] Optimize Structure Generation diff --git a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java index 63143ceec98f7a84ec4064d05e8f88c11200172f..02f67af9c312f3d9213af1e410986165dcf618a4 100644 --- a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java +++ b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java @@ -4,6 +4,8 @@ import com.google.common.collect.Lists; import com.mojang.logging.LogUtils; import java.util.List; import java.util.Optional; +import java.util.ArrayList; +import java.util.LinkedHashSet; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; @@ -292,6 +294,108 @@ public class JigsawPlacement { this.random = random; } + // DivineMC start - Optimize Structure Generation + private boolean structureLayoutOptimizer$optimizeJigsawConnecting(StructureTemplate.JigsawBlockInfo jigsaw1, StructureTemplate.JigsawBlockInfo jigsaw2) { + if (!org.bxteam.divinemc.DivineConfig.enableStructureLayoutOptimizer) { + return JigsawBlock.canAttach(jigsaw1, jigsaw2); + } + return org.bxteam.divinemc.util.structure.GeneralUtils.canJigsawsAttach(jigsaw1, jigsaw2); + } + + private void structureLayoutOptimizer$replaceVoxelShape3(MutableObject instance, BoundingBox pieceBounds) { + org.bxteam.divinemc.util.structure.TrojanVoxelShape trojanVoxelShape = new org.bxteam.divinemc.util.structure.TrojanVoxelShape(new org.bxteam.divinemc.util.structure.BoxOctree(AABB.of(pieceBounds))); + instance.setValue(trojanVoxelShape); + } + + private void structureLayoutOptimizer$replaceVoxelShape4(MutableObject instance, BoundingBox pieceBounds) { + if (instance.getValue() instanceof org.bxteam.divinemc.util.structure.TrojanVoxelShape trojanVoxelShape) { + trojanVoxelShape.boxOctree.addBox(AABB.of(pieceBounds)); + } + } + + private List structureLayoutOptimizer$removeDuplicateTemplatePoolElementLists(StructureTemplatePool instance, RandomSource random) { + if (!org.bxteam.divinemc.DivineConfig.enableStructureLayoutOptimizer || !org.bxteam.divinemc.DivineConfig.deduplicateShuffledTemplatePoolElementList) { + return instance.getShuffledTemplates(random); + } + + // Linked hashset keeps order of elements. + LinkedHashSet uniquePieces = new LinkedHashSet<>((instance).rawTemplates.size()); + + // Don't use addAll. Want to keep it simple in case of inefficiency in collection's addAll. + // Set will ignore duplicates after first appearance of an element. + for (StructurePoolElement piece : instance.getShuffledTemplates(random)) { + //noinspection UseBulkOperation + uniquePieces.add(piece); + } + + // Move the elements from set to the list in the same order. + int uniquePiecesFound = uniquePieces.size(); + List deduplicatedListOfPieces = new ArrayList<>(uniquePiecesFound); + for (int i = 0; i < uniquePiecesFound; i++) { + deduplicatedListOfPieces.add(uniquePieces.removeFirst()); + } + + return deduplicatedListOfPieces; + } + + private ArrayList structureLayoutOptimizer$skipDuplicateTemplatePoolElementLists1() { + // Swap with trojan list, so we can record what pieces we visited + return org.bxteam.divinemc.DivineConfig.deduplicateShuffledTemplatePoolElementList ? Lists.newArrayList() : new org.bxteam.divinemc.util.structure.TrojanArrayList<>(); + } + + private List structureLayoutOptimizer$skipBlockedJigsaws( + List original, + boolean useExpansionHack, + MutableObject voxelShapeMutableObject, + StructurePoolElement structurePoolElement, + StructureTemplate.StructureBlockInfo parentJigsawBlockInfo, + BlockPos parentTargetPosition) + { + if (!org.bxteam.divinemc.DivineConfig.enableStructureLayoutOptimizer) { + return original; + } + if (voxelShapeMutableObject.getValue() instanceof org.bxteam.divinemc.util.structure.TrojanVoxelShape trojanVoxelShape) { + // If rigid and target position is already an invalid spot, do not run rest of logic. + StructureTemplatePool.Projection candidatePlacementBehavior = structurePoolElement.getProjection(); + boolean isCandidateRigid = candidatePlacementBehavior == StructureTemplatePool.Projection.RIGID; + if (isCandidateRigid && (!trojanVoxelShape.boxOctree.boundaryContains(parentTargetPosition) || trojanVoxelShape.boxOctree.withinAnyBox(parentTargetPosition))) { + return new ArrayList<>(); + } + } + return original; + } + + private List structureLayoutOptimizer$skipDuplicateTemplatePoolElementLists2(List original, + List list, + StructurePoolElement structurepoolelement1) + { + if (!org.bxteam.divinemc.DivineConfig.enableStructureLayoutOptimizer) { + return original; + } + if (!org.bxteam.divinemc.DivineConfig.deduplicateShuffledTemplatePoolElementList && list instanceof org.bxteam.divinemc.util.structure.TrojanArrayList trojanArrayList) { + // Do not run this piece's logic since we already checked its 4 rotations in the past. + if (trojanArrayList.elementsAlreadyParsed.contains(structurepoolelement1)) { + + // Prime the random with the random calls we would've skipped. + // Maintains vanilla compat. + for (Rotation rotation1 : original) { + structurepoolelement1.getShuffledJigsawBlocks(this.structureTemplateManager, BlockPos.ZERO, rotation1, this.random); + } + + // Short circuit the Rotation loop + return new ArrayList<>(); + } + // Record piece as it will go through the 4 rotation checks for spawning. + else { + trojanArrayList.elementsAlreadyParsed.add(structurepoolelement1); + } + } + + // Allow the vanilla code to run normally. + return original; + } + // DivineMC end - Optimize Structure Generation + void tryPlacingChildren( PoolElementStructurePiece piece, MutableObject free, @@ -349,9 +453,9 @@ public class JigsawPlacement { mutableObject1 = free; } - List list = Lists.newArrayList(); + List list = structureLayoutOptimizer$skipDuplicateTemplatePoolElementLists1(); // DivineMC - Optimize Structure Generation if (depth != this.maxDepth) { - list.addAll(holder.value().getShuffledTemplates(this.random)); + list.addAll(structureLayoutOptimizer$removeDuplicateTemplatePoolElementLists(holder.value(), this.random)); // DivineMC - Optimize Structure Generation } list.addAll(fallback.value().getShuffledTemplates(this.random)); @@ -362,10 +466,14 @@ public class JigsawPlacement { break; } - for (Rotation rotation1 : Rotation.getShuffled(this.random)) { - List shuffledJigsawBlocks = structurePoolElement.getShuffledJigsawBlocks( + // DivineMC start - Optimize Structure Generation + for (Rotation rotation1 : structureLayoutOptimizer$skipDuplicateTemplatePoolElementLists2(Rotation.getShuffled(this.random), list, structurePoolElement)) { + List shuffledJigsawBlocks = structureLayoutOptimizer$skipBlockedJigsaws( + structurePoolElement.getShuffledJigsawBlocks( this.structureTemplateManager, BlockPos.ZERO, rotation1, this.random + ), useExpansionHack, mutableObject1, structurePoolElement, structureBlockInfo, blockPos1 ); + // DivineMC end - Optimize Structure Generation BoundingBox boundingBox1 = structurePoolElement.getBoundingBox(this.structureTemplateManager, BlockPos.ZERO, rotation1); int i2; if (useExpansionHack && boundingBox1.getYSpan() <= 16) { @@ -398,7 +506,7 @@ public class JigsawPlacement { } for (StructureTemplate.JigsawBlockInfo jigsawBlockInfo1 : shuffledJigsawBlocks) { - if (JigsawBlock.canAttach(jigsawBlockInfo, jigsawBlockInfo1)) { + if (structureLayoutOptimizer$optimizeJigsawConnecting(jigsawBlockInfo, jigsawBlockInfo1)) { // DivineMC - Optimize Structure Generation BlockPos blockPos2 = jigsawBlockInfo1.info().pos(); BlockPos blockPos3 = blockPos1.subtract(blockPos2); BoundingBox boundingBox2 = structurePoolElement.getBoundingBox(this.structureTemplateManager, blockPos3, rotation1); @@ -427,9 +535,26 @@ public class JigsawPlacement { boundingBox3.encapsulate(new BlockPos(boundingBox3.minX(), boundingBox3.minY() + max, boundingBox3.minZ())); } - if (!Shapes.joinIsNotEmpty( - mutableObject1.getValue(), Shapes.create(AABB.of(boundingBox3).deflate(0.25)), BooleanOp.ONLY_SECOND - )) { + // DivineMC start - Optimize Structure Generation + boolean internal$joinIsNotEmpty; + VoxelShape parentBounds = mutableObject1.getValue(); + java.util.function.Supplier original = () -> Shapes.joinIsNotEmpty( + parentBounds, Shapes.create(AABB.of(boundingBox3).deflate(0.25)), BooleanOp.ONLY_SECOND + ); + if (org.bxteam.divinemc.DivineConfig.enableStructureLayoutOptimizer) { + if (parentBounds instanceof org.bxteam.divinemc.util.structure.TrojanVoxelShape trojanVoxelShape) { + AABB pieceAABB = AABB.of(boundingBox3).deflate(0.25D); + + // Have to inverse because of an ! outside our wrap + internal$joinIsNotEmpty = !trojanVoxelShape.boxOctree.withinBoundsButNotIntersectingChildren(pieceAABB); + } else { + internal$joinIsNotEmpty = original.get(); + } + } else { + internal$joinIsNotEmpty = original.get(); + } + if (!internal$joinIsNotEmpty) { + // DivineMC end - Optimize Structure Generation mutableObject1.setValue( Shapes.joinUnoptimized( mutableObject1.getValue(), Shapes.create(AABB.of(boundingBox3)), BooleanOp.ONLY_FIRST diff --git a/net/minecraft/world/level/levelgen/structure/pools/SinglePoolElement.java b/net/minecraft/world/level/levelgen/structure/pools/SinglePoolElement.java index 4a6da3648c513a6cce16cf71246937d2d0ad014d..6af4c9026e814dee1ed4c7593ad00b5f8af9b979 100644 --- a/net/minecraft/world/level/levelgen/structure/pools/SinglePoolElement.java +++ b/net/minecraft/world/level/levelgen/structure/pools/SinglePoolElement.java @@ -119,8 +119,16 @@ public class SinglePoolElement extends StructurePoolElement { StructureTemplateManager structureTemplateManager, BlockPos pos, Rotation rotation, RandomSource random ) { List jigsaws = this.getTemplate(structureTemplateManager).getJigsaws(pos, rotation); - Util.shuffle(jigsaws, random); - sortBySelectionPriority(jigsaws); + // DivineMC start - Optimize Structure Generation + if (org.bxteam.divinemc.DivineConfig.enableStructureLayoutOptimizer) { + structureLayoutOptimizer$fasterJigsawListShuffling1(jigsaws, random); + structureLayoutOptimizer$fasterJigsawListShuffling2(jigsaws); + } else { + Util.shuffle(jigsaws, random); + sortBySelectionPriority(jigsaws); + } + // DivineMC end - Optimize Structure Generation + return jigsaws; } @@ -191,4 +199,12 @@ public class SinglePoolElement extends StructurePoolElement { public String toString() { return "Single[" + this.template + "]"; } + + // DivineMC start - Optimize Structure Generation + private void structureLayoutOptimizer$fasterJigsawListShuffling1(List list, RandomSource randomSource) { + org.bxteam.divinemc.util.structure.GeneralUtils.shuffleAndPrioritize(list, randomSource); + } + + private void structureLayoutOptimizer$fasterJigsawListShuffling2(List structureBlockInfos) { } + // Quiil end - Optimize Structure Generation } diff --git a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java index a37eb2e29b4577ebc711e8ef7b47fbbc3bc66897..45d6fbeeaac577bb35adb69fd344b9486953ea03 100644 --- a/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java +++ b/net/minecraft/world/level/levelgen/structure/templatesystem/StructureTemplate.java @@ -249,6 +249,12 @@ public class StructureTemplate { return transform(pos, decorator.getMirror(), decorator.getRotation(), decorator.getRotationPivot()); } + // DivineMC start - Optimize Structure Generation + private List structureLayoutOptimizer$shrinkStructureTemplateBlocksList(StructureTemplate.Palette palette, BlockPos offset, StructurePlaceSettings settings) { + return org.bxteam.divinemc.util.structure.StructureTemplateOptimizer.getStructureBlockInfosInBounds(palette, offset, settings); + } + // DivineMC end - Optimize Structure Generation + public boolean placeInWorld(ServerLevelAccessor serverLevel, BlockPos offset, BlockPos pos, StructurePlaceSettings settings, RandomSource random, int flags) { if (this.palettes.isEmpty()) { return false; @@ -266,7 +272,11 @@ public class StructureTemplate { } } // CraftBukkit end - List list = settings.getRandomPalette(this.palettes, offset).blocks(); + // DivineMC start - Optimize Structure Generation + List list = org.bxteam.divinemc.DivineConfig.enableStructureLayoutOptimizer + ? structureLayoutOptimizer$shrinkStructureTemplateBlocksList(settings.getRandomPalette(this.palettes, offset), offset, settings) + : settings.getRandomPalette(this.palettes, offset).blocks(); + // DivineMC end - Optimize Structure Generation if ((!list.isEmpty() || !settings.isIgnoreEntities() && !this.entityInfoList.isEmpty()) && this.size.getX() >= 1 && this.size.getY() >= 1 @@ -890,7 +900,11 @@ public class StructureTemplate { private List cachedJigsaws; Palette(List blocks) { - this.blocks = blocks; + // DivineMC start - Optimize Structure Generation + this.blocks = org.bxteam.divinemc.DivineConfig.enableStructureLayoutOptimizer + ? new org.bxteam.divinemc.util.structure.PalettedStructureBlockInfoList(blocks) + : blocks; + // DivineMC end - Optimize Structure Generation } public List jigsaws() {