diff --git a/src/main/java/me/jellysquid/mods/lithium/common/config/LithiumConfig.java b/src/main/java/me/jellysquid/mods/lithium/common/config/LithiumConfig.java index da78584ad..d3979b745 100644 --- a/src/main/java/me/jellysquid/mods/lithium/common/config/LithiumConfig.java +++ b/src/main/java/me/jellysquid/mods/lithium/common/config/LithiumConfig.java @@ -144,6 +144,7 @@ private LithiumConfig() { this.addMixinRule("world.chunk_access", true); this.addMixinRule("world.chunk_tickets", true); this.addMixinRule("world.chunk_ticking", true); + this.addMixinRule("world.combined_heightmap_update", true); this.addMixinRule("world.explosions", true); this.addMixinRule("world.inline_block_access", true); this.addMixinRule("world.inline_height", true); diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/heightmap/CombinedHeightmapUpdate.java b/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/heightmap/CombinedHeightmapUpdate.java new file mode 100644 index 000000000..eac4e2628 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/heightmap/CombinedHeightmapUpdate.java @@ -0,0 +1,139 @@ +package me.jellysquid.mods.lithium.common.world.chunk.heightmap; + +import me.jellysquid.mods.lithium.mixin.world.combined_heightmap_update.HeightmapAccessor; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.Heightmap; +import net.minecraft.world.chunk.WorldChunk; + +import java.util.Objects; +import java.util.function.Predicate; + +public class CombinedHeightmapUpdate { + public static void updateHeightmaps(Heightmap heightmap0, Heightmap heightmap1, Heightmap heightmap2, Heightmap heightmap3, WorldChunk worldChunk, final int x, final int y, final int z, BlockState state) { + final int height0 = heightmap0.get(x, z); + final int height1 = heightmap1.get(x, z); + final int height2 = heightmap2.get(x, z); + final int height3 = heightmap3.get(x, z); + int heightmapsToUpdate = 4; + if (y + 2 <= height0) { + heightmap0 = null; + heightmapsToUpdate--; + } + if (y + 2 <= height1) { + heightmap1 = null; + heightmapsToUpdate--; + } + if (y + 2 <= height2) { + heightmap2 = null; + heightmapsToUpdate--; + } + if (y + 2 <= height3) { + heightmap3 = null; + heightmapsToUpdate--; + } + + if (heightmapsToUpdate == 0) { + return; + } + + Predicate blockPredicate0 = heightmap0 == null ? null : Objects.requireNonNull(((HeightmapAccessor) heightmap0).getBlockPredicate()); + Predicate blockPredicate1 = heightmap1 == null ? null : Objects.requireNonNull(((HeightmapAccessor) heightmap1).getBlockPredicate()); + Predicate blockPredicate2 = heightmap2 == null ? null : Objects.requireNonNull(((HeightmapAccessor) heightmap2).getBlockPredicate()); + Predicate blockPredicate3 = heightmap3 == null ? null : Objects.requireNonNull(((HeightmapAccessor) heightmap3).getBlockPredicate()); + + if (heightmap0 != null) { + if (blockPredicate0.test(state)) { + if (y >= height0) { + ((HeightmapAccessor) heightmap0).setHeight(x, z, y + 1); + } + heightmap0 = null; + heightmapsToUpdate--; + } else if (height0 != y + 1) { + heightmap0 = null; + heightmapsToUpdate--; + } + } + if (heightmap1 != null) { + if (blockPredicate1.test(state)) { + if (y >= height1) { + ((HeightmapAccessor) heightmap1).setHeight(x, z, y + 1); + } + heightmap1 = null; + heightmapsToUpdate--; + } else if (height1 != y + 1) { + heightmap1 = null; + heightmapsToUpdate--; + } + } + if (heightmap2 != null) { + if (blockPredicate2.test(state)) { + if (y >= height2) { + ((HeightmapAccessor) heightmap2).setHeight(x, z, y + 1); + } + heightmap2 = null; + heightmapsToUpdate--; + } else if (height2 != y + 1) { + heightmap2 = null; + heightmapsToUpdate--; + } + } + if (heightmap3 != null) { + if (blockPredicate3.test(state)) { + if (y >= height3) { + ((HeightmapAccessor) heightmap3).setHeight(x, z, y + 1); + } + heightmap3 = null; + heightmapsToUpdate--; + } else if (height3 != y + 1) { + heightmap3 = null; + heightmapsToUpdate--; + } + } + + + if (heightmapsToUpdate == 0) { + return; + } + + BlockPos.Mutable mutable = new BlockPos.Mutable(); + int bottomY = worldChunk.getBottomY(); + + for (int searchY = y - 1; searchY >= bottomY && heightmapsToUpdate > 0; --searchY) { + mutable.set(x, searchY, z); + BlockState blockState = worldChunk.getBlockState(mutable); + if (heightmap0 != null && blockPredicate0.test(blockState)) { + ((HeightmapAccessor) heightmap0).setHeight(x, z, searchY + 1); + heightmap0 = null; + heightmapsToUpdate--; + } + if (heightmap1 != null && blockPredicate1.test(blockState)) { + ((HeightmapAccessor) heightmap1).setHeight(x, z, searchY + 1); + heightmap1 = null; + heightmapsToUpdate--; + } + if (heightmap2 != null && blockPredicate2.test(blockState)) { + ((HeightmapAccessor) heightmap2).setHeight(x, z, searchY + 1); + heightmap2 = null; + heightmapsToUpdate--; + } + if (heightmap3 != null && blockPredicate3.test(blockState)) { + ((HeightmapAccessor) heightmap3).setHeight(x, z, searchY + 1); + heightmap3 = null; + heightmapsToUpdate--; + } + } + if (heightmap0 != null) { + ((HeightmapAccessor) heightmap0).setHeight(x, z, bottomY); + } + if (heightmap1 != null) { + ((HeightmapAccessor) heightmap1).setHeight(x, z, bottomY); + } + if (heightmap2 != null) { + ((HeightmapAccessor) heightmap2).setHeight(x, z, bottomY); + } + if (heightmap3 != null) { + ((HeightmapAccessor) heightmap3).setHeight(x, z, bottomY); + } + } +} diff --git a/src/main/java/me/jellysquid/mods/lithium/mixin/world/combined_heightmap_update/HeightmapAccessor.java b/src/main/java/me/jellysquid/mods/lithium/mixin/world/combined_heightmap_update/HeightmapAccessor.java new file mode 100644 index 000000000..09b5d7257 --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/mixin/world/combined_heightmap_update/HeightmapAccessor.java @@ -0,0 +1,17 @@ +package me.jellysquid.mods.lithium.mixin.world.combined_heightmap_update; + +import net.minecraft.block.BlockState; +import net.minecraft.world.Heightmap; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.function.Predicate; + +@Mixin(Heightmap.class) +public interface HeightmapAccessor { + @Invoker("set") + void setHeight(int x, int z, int height); + @Accessor("blockPredicate") + Predicate getBlockPredicate(); +} diff --git a/src/main/java/me/jellysquid/mods/lithium/mixin/world/combined_heightmap_update/WorldChunkMixin.java b/src/main/java/me/jellysquid/mods/lithium/mixin/world/combined_heightmap_update/WorldChunkMixin.java new file mode 100644 index 000000000..ffe578c5b --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/mixin/world/combined_heightmap_update/WorldChunkMixin.java @@ -0,0 +1,72 @@ +package me.jellysquid.mods.lithium.mixin.world.combined_heightmap_update; + +import me.jellysquid.mods.lithium.common.world.chunk.heightmap.CombinedHeightmapUpdate; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.HeightLimitView; +import net.minecraft.world.Heightmap; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.ChunkSection; +import net.minecraft.world.chunk.UpgradeData; +import net.minecraft.world.chunk.WorldChunk; +import net.minecraft.world.gen.chunk.BlendingData; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.Map; + +@Mixin(WorldChunk.class) +public abstract class WorldChunkMixin extends Chunk { + public WorldChunkMixin(ChunkPos pos, UpgradeData upgradeData, HeightLimitView heightLimitView, Registry biome, long inhabitedTime, @Nullable ChunkSection[] sectionArrayInitializer, @Nullable BlendingData blendingData) { + super(pos, upgradeData, heightLimitView, biome, inhabitedTime, sectionArrayInitializer, blendingData); + } + + @Redirect( + method = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)Lnet/minecraft/block/BlockState;", + at = @At(value = "INVOKE", target = "Ljava/util/Map;get(Ljava/lang/Object;)Ljava/lang/Object;") + ) + private V skipGetHeightmap(Map heightmaps, K heightmapType) { + if (heightmapType == Heightmap.Type.MOTION_BLOCKING || heightmapType == Heightmap.Type.MOTION_BLOCKING_NO_LEAVES || heightmapType == Heightmap.Type.OCEAN_FLOOR || heightmapType == Heightmap.Type.WORLD_SURFACE) { + return null; + } + return heightmaps.get(heightmapType); + } + + @Redirect( + method = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)Lnet/minecraft/block/BlockState;", + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/Heightmap;trackUpdate(IIILnet/minecraft/block/BlockState;)Z") + ) + private boolean skipHeightmapUpdate(Heightmap instance, int x, int y, int z, BlockState state) { + if (instance == null) { + return false; + } + return instance.trackUpdate(x, y, z, state); + } + + @Inject( + method = "setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;Z)Lnet/minecraft/block/BlockState;", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/Heightmap;trackUpdate(IIILnet/minecraft/block/BlockState;)Z", + shift = At.Shift.BEFORE, + ordinal = 0 + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void updateHeightmapsCombined(BlockPos pos, BlockState state, boolean moved, CallbackInfoReturnable cir, int i, ChunkSection chunkSection, boolean bl, int x, int y, int z, BlockState blockState, Block block) { + Heightmap heightmap0 = this.heightmaps.get(Heightmap.Type.MOTION_BLOCKING); + Heightmap heightmap1 = this.heightmaps.get(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES); + Heightmap heightmap2 = this.heightmaps.get(Heightmap.Type.OCEAN_FLOOR); + Heightmap heightmap3 = this.heightmaps.get(Heightmap.Type.WORLD_SURFACE); + CombinedHeightmapUpdate.updateHeightmaps(heightmap0, heightmap1, heightmap2, heightmap3, (WorldChunk) (Chunk) this, x, y, z, state); + } +} diff --git a/src/main/resources/lithium.mixins.json b/src/main/resources/lithium.mixins.json index b8b9d0da0..55cc3497c 100644 --- a/src/main/resources/lithium.mixins.json +++ b/src/main/resources/lithium.mixins.json @@ -183,6 +183,8 @@ "world.chunk_access.ServerChunkManagerMixin", "world.chunk_access.WorldMixin", "world.chunk_tickets.SortedArraySetMixin", + "world.combined_heightmap_update.HeightmapAccessor", + "world.combined_heightmap_update.WorldChunkMixin", "world.explosions.ExplosionMixin", "world.inline_block_access.WorldChunkMixin", "world.inline_block_access.WorldMixin",