Skip to content

Commit

Permalink
new: world.combined_heightmap_update: update the four heightmaps toge…
Browse files Browse the repository at this point in the history
…ther when placing/breaking blocks

This feature removes the duplicate downward block search when breaking a block that is high up. Instead the vanilla heightmaps are updated together in one search.
  • Loading branch information
2No2Name committed Nov 29, 2022
1 parent 0213572 commit 069d920
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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<BlockState> blockPredicate0 = heightmap0 == null ? null : Objects.requireNonNull(((HeightmapAccessor) heightmap0).getBlockPredicate());
Predicate<BlockState> blockPredicate1 = heightmap1 == null ? null : Objects.requireNonNull(((HeightmapAccessor) heightmap1).getBlockPredicate());
Predicate<BlockState> blockPredicate2 = heightmap2 == null ? null : Objects.requireNonNull(((HeightmapAccessor) heightmap2).getBlockPredicate());
Predicate<BlockState> 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<BlockState> getBlockPredicate();
}
Original file line number Diff line number Diff line change
@@ -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> 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 <K, V> V skipGetHeightmap(Map<K, V> 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<BlockState> 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);
}
}
2 changes: 2 additions & 0 deletions src/main/resources/lithium.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down

0 comments on commit 069d920

Please sign in to comment.