-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fast-path exit end portal search by counting bedrock blocks
Useful during ender dragon respawning when placing the last end crystal #618
- Loading branch information
Showing
9 changed files
with
169 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
...net/caffeinemc/mods/lithium/common/world/block_pattern_matching/BlockPatternExtended.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package net.caffeinemc.mods.lithium.common.world.block_pattern_matching; | ||
|
||
import net.minecraft.world.level.block.Block; | ||
|
||
public interface BlockPatternExtended { | ||
|
||
void lithium$setRequiredBlock(Block block, int count); | ||
} |
70 changes: 70 additions & 0 deletions
70
...ain/java/net/caffeinemc/mods/lithium/common/world/block_pattern_matching/BlockSearch.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package net.caffeinemc.mods.lithium.common.world.block_pattern_matching; | ||
|
||
import net.caffeinemc.mods.lithium.common.util.Pos; | ||
import net.minecraft.core.BlockBox; | ||
import net.minecraft.core.SectionPos; | ||
import net.minecraft.world.level.LevelReader; | ||
import net.minecraft.world.level.block.Block; | ||
import net.minecraft.world.level.block.Blocks; | ||
import net.minecraft.world.level.block.state.BlockState; | ||
import net.minecraft.world.level.chunk.ChunkAccess; | ||
import net.minecraft.world.level.chunk.LevelChunkSection; | ||
|
||
import java.util.function.Predicate; | ||
|
||
public class BlockSearch { | ||
|
||
public static boolean hasAtLeast(LevelReader levelReader, BlockBox searchBox, Block requiredBlock, int requiredBlockCount) { | ||
Predicate<BlockState> predicate = blockState -> blockState.is(requiredBlock); | ||
for (int chunkX = SectionPos.blockToSectionCoord(searchBox.min().getX()); chunkX <= SectionPos.blockToSectionCoord(searchBox.max().getX()); chunkX++) { | ||
for (int chunkZ = SectionPos.blockToSectionCoord(searchBox.min().getZ()); chunkZ <= SectionPos.blockToSectionCoord(searchBox.max().getZ()); chunkZ++) { | ||
|
||
ChunkAccess chunk = levelReader.getChunk(chunkX, chunkZ); | ||
int minSectionYIndex = Pos.SectionYIndex.fromBlockCoord(levelReader, searchBox.min().getY()); | ||
int maxSectionYIndex = Pos.SectionYIndex.fromBlockCoord(levelReader, searchBox.max().getY()); | ||
for (int sectionYIndex = minSectionYIndex; sectionYIndex <= maxSectionYIndex; sectionYIndex++) { | ||
if (sectionYIndex >= 0 && sectionYIndex <= chunk.getSectionsCount()) { | ||
LevelChunkSection section = chunk.getSection(sectionYIndex); | ||
if (section.maybeHas(predicate)) { | ||
int sectionYCoord = Pos.SectionYCoord.fromSectionIndex(levelReader, sectionYIndex); | ||
requiredBlockCount -= countBlocksInBoxInSection( | ||
section, | ||
Math.max(searchBox.min().getX(), chunkX << 4), | ||
Math.max(searchBox.min().getY(), sectionYCoord << 4), | ||
Math.max(searchBox.min().getZ(), chunkZ << 4), | ||
Math.min(searchBox.max().getX(), (chunkX << 4) + 15), | ||
Math.min(searchBox.max().getY(), (sectionYCoord << 4) + 15), | ||
Math.min(searchBox.max().getZ(), (chunkZ << 4) + 15), | ||
requiredBlock, requiredBlockCount | ||
); | ||
if (requiredBlockCount <= 0) { | ||
return true; | ||
} | ||
} | ||
} else if (requiredBlock == Blocks.VOID_AIR) { | ||
return true; //Handle VOID_AIR somewhat correctly. We should count the volume, but noone uses void air anyway | ||
} | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
public static int countBlocksInBoxInSection(LevelChunkSection section, int minX, int minY, int minZ, int maxX, int maxY, int maxZ, Block requiredBlock, int findMax) { | ||
int found = 0; | ||
//Optimized iteration order xzy | ||
for (int y = minY; y <= maxY; y++) { | ||
for (int z = minZ; z <= maxZ; z++) { | ||
for (int x = minX; x <= maxX; x++) { | ||
if (section.getBlockState(x & 15, y & 15, z & 15).is(requiredBlock)) { | ||
found++; | ||
if (found >= findMax) { | ||
return found; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return found; | ||
} | ||
} |
46 changes: 46 additions & 0 deletions
46
...main/java/net/caffeinemc/mods/lithium/mixin/block_pattern_matching/BlockPatternMixin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package net.caffeinemc.mods.lithium.mixin.block_pattern_matching; | ||
|
||
import com.llamalad7.mixinextras.sugar.Local; | ||
import net.caffeinemc.mods.lithium.common.world.block_pattern_matching.BlockPatternExtended; | ||
import net.caffeinemc.mods.lithium.common.world.block_pattern_matching.BlockSearch; | ||
import net.minecraft.core.BlockBox; | ||
import net.minecraft.core.BlockPos; | ||
import net.minecraft.world.level.LevelReader; | ||
import net.minecraft.world.level.block.Block; | ||
import net.minecraft.world.level.block.state.pattern.BlockPattern; | ||
import org.spongepowered.asm.mixin.Mixin; | ||
import org.spongepowered.asm.mixin.Unique; | ||
import org.spongepowered.asm.mixin.injection.At; | ||
import org.spongepowered.asm.mixin.injection.Inject; | ||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; | ||
|
||
@Mixin(BlockPattern.class) | ||
public class BlockPatternMixin implements BlockPatternExtended { | ||
|
||
|
||
@Unique | ||
private Block requiredBlock; | ||
@Unique | ||
private int requiredBlockCount; | ||
|
||
@Inject( | ||
method = "find", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/BlockPos;offset(III)Lnet/minecraft/core/BlockPos;"), cancellable = true | ||
) | ||
private void countRequiredBlocksBeforeExpensiveSearch(LevelReader levelReader, BlockPos blockPos, CallbackInfoReturnable<BlockPattern.BlockPatternMatch> cir, @Local int size) { | ||
if (this.requiredBlock != null) { | ||
BlockPos maxCorner = blockPos.offset(2 * size - 1, 2 * size - 1, 2 * size - 1); | ||
BlockPos minCorner = blockPos.offset(-size, -size, -size); | ||
|
||
BlockBox searchBox = BlockBox.of(minCorner, maxCorner); | ||
if (!BlockSearch.hasAtLeast(levelReader, searchBox, this.requiredBlock, this.requiredBlockCount)) { | ||
cir.setReturnValue(null); | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public void lithium$setRequiredBlock(Block block, int count) { | ||
this.requiredBlock = block; | ||
this.requiredBlockCount = count; | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
...in/java/net/caffeinemc/mods/lithium/mixin/block_pattern_matching/EndDragonFightMixin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package net.caffeinemc.mods.lithium.mixin.block_pattern_matching; | ||
|
||
import net.caffeinemc.mods.lithium.common.world.block_pattern_matching.BlockPatternExtended; | ||
import net.minecraft.core.BlockPos; | ||
import net.minecraft.server.level.ServerLevel; | ||
import net.minecraft.world.level.block.Blocks; | ||
import net.minecraft.world.level.block.state.pattern.BlockPattern; | ||
import net.minecraft.world.level.dimension.end.EndDragonFight; | ||
import org.spongepowered.asm.mixin.Final; | ||
import org.spongepowered.asm.mixin.Mixin; | ||
import org.spongepowered.asm.mixin.Shadow; | ||
import org.spongepowered.asm.mixin.injection.At; | ||
import org.spongepowered.asm.mixin.injection.Inject; | ||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; | ||
|
||
@Mixin(EndDragonFight.class) | ||
public class EndDragonFightMixin { | ||
|
||
@Shadow | ||
@Final | ||
private BlockPattern exitPortalPattern; | ||
|
||
@Inject( | ||
method = "<init>(Lnet/minecraft/server/level/ServerLevel;JLnet/minecraft/world/level/dimension/end/EndDragonFight$Data;Lnet/minecraft/core/BlockPos;)V", at = @At("RETURN") | ||
) | ||
private void setPatternToDragonPattern(ServerLevel serverLevel, long l, EndDragonFight.Data data, BlockPos blockPos, CallbackInfo ci) { | ||
//Small todo: Find a way to not hardcode this, as this breaks mod compatibility when modifying the exit portal pattern | ||
((BlockPatternExtended) this.exitPortalPattern).lithium$setRequiredBlock(Blocks.BEDROCK, 41); | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
.../src/main/java/net/caffeinemc/mods/lithium/mixin/block_pattern_matching/package-info.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
@MixinConfigOption(description = "Fast-path exit end portal search by counting nearby bedrock blocks. Reduces lag when placing the last end crystal when respawning the ender dragon.") | ||
package net.caffeinemc.mods.lithium.mixin.block_pattern_matching; | ||
|
||
import net.caffeinemc.gradle.MixinConfigOption; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters