From 590028cb807cf8b0a010f0ba47837621263f8c39 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Tue, 5 Nov 2024 22:37:02 -0500 Subject: [PATCH 1/5] Pull fast chunk sending --- .../hodgepodge/config/SpeedupsConfig.java | 12 ++ .../mitchej123/hodgepodge/mixins/Mixins.java | 7 + .../fastload/MixinEntityPlayerMP.java | 125 ++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinEntityPlayerMP.java diff --git a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java index 64a6d5a8..f0434be2 100644 --- a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java +++ b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java @@ -40,6 +40,18 @@ public class SpeedupsConfig { @Config.DefaultBoolean(true) public static boolean speedupChunkProviderClient; + @Config.Comment( + "Removes hard caps on chunk handling speed, and replaces some internals with faster/safer replacements. " + + "Experimental and probably incompatible with hybrid servers!") + @Config.DefaultBoolean(false) + @Config.RequiresMcRestart + public static boolean fastChunkHandling; + + @Config.Comment("The maximum speed of chunkloading per player, in chunks/tick. High values may overload clients! Only active with fastChunkHandling.\n" + + "For reference: Vanilla is 5, or 100 chunks/sec. At 32 render distance = 4225 chunks, loading the world would take 42.25 seconds.") + @Config.DefaultInt(50) + public static int maxSendSpeed; + // Biomes O' Plenty @Config.Comment("Speedup biome fog rendering in BiomesOPlenty") diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java index f9931504..7e879eb8 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java @@ -446,6 +446,13 @@ public enum Mixins { new Builder("Memory fixes").setPhase(Phase.EARLY).setSide(Side.CLIENT).addTargetedMod(TargetedMod.VANILLA) .addMixinClasses("memory.MixinFMLClientHandler").setApplyIf(() -> FixesConfig.enableMemoryFixes)), + FAST_CHUNK_LOADING(new Builder("Invasively accelerates chunk handling").setPhase(Phase.EARLY) + .setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA) + .addMixinClasses( +// "minecraft.fastload.MixinIntCache", + "minecraft.fastload.MixinEntityPlayerMP") + .setApplyIf(() -> SpeedupsConfig.fastChunkHandling)), + MEMORY_FIXES_IC2(new Builder("Removes allocation spam from the Direction.applyTo method").setPhase(Phase.LATE) .setSide(Side.BOTH).addMixinClasses("ic2.MixinDirection_Memory") .setApplyIf(() -> FixesConfig.enableMemoryFixes).addTargetedMod(TargetedMod.IC2)), diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinEntityPlayerMP.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinEntityPlayerMP.java new file mode 100644 index 00000000..e7e51028 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinEntityPlayerMP.java @@ -0,0 +1,125 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; + +import com.mitchej123.hodgepodge.config.SpeedupsConfig; +import com.mojang.authlib.GameProfile; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectImmutableList; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.network.play.server.S26PacketMapChunkBulk; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; +import net.minecraft.world.chunk.Chunk; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.ChunkWatchEvent; +import org.apache.logging.log4j.Logger; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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.CallbackInfo; + +@Mixin(EntityPlayerMP.class) +public abstract class MixinEntityPlayerMP extends EntityPlayer { + + @Shadow + @Final + public List loadedChunks; + + @Shadow + public NetHandlerPlayServer playerNetServerHandler; + + @Shadow + protected abstract void func_147097_b(TileEntity te); + + // Cache these lists so they don't have to be reallocated every onUpdate + @Unique + private final ObjectArrayList> hodgepodge$chunkSends = new ObjectArrayList<>(); + @Unique + private final ObjectArrayList hodgepodge$rollingChunks = new ObjectArrayList<>(); + @Unique + private final ObjectArrayList hodgepodge$rollingTEs = new ObjectArrayList<>(); + + @Shadow + public abstract WorldServer getServerForPlayer(); + + @Shadow @Final private static Logger logger; + + public MixinEntityPlayerMP(World world, GameProfile profile) { + super(world, profile); + } + + /** + * This injects just before where vanilla handles chunk sending and returns before then. This will mess up any + * mixins that target the tail of this function, but other solutions are similarly invasive. + */ + @Inject(method = "onUpdate", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/player/EntityPlayerMP;loadedChunks:Ljava/util/List;", shift = At.Shift.BEFORE, ordinal = 0), cancellable = true) + private void hodgepodge$replaceChunkSending(CallbackInfo ci) { + if (loadedChunks.isEmpty()) { ci.cancel(); return; } + + int numChunks = 0; + final Iterator allChunks = loadedChunks.iterator(); + final int chunksPerPacket = S26PacketMapChunkBulk.func_149258_c(); + + // Gather chunks to send, unload chunks outside of range + while (allChunks.hasNext() && numChunks < SpeedupsConfig.maxSendSpeed) { + final ChunkCoordIntPair ccip = allChunks.next(); + if (ccip == null) { + allChunks.remove(); continue; + } + + if (!worldObj.blockExists(ccip.chunkXPos << 4, 0, ccip.chunkZPos << 4)) { + continue; + } + + final Chunk chunk = worldObj.getChunkFromChunkCoords(ccip.chunkXPos, ccip.chunkZPos); + if (!chunk.func_150802_k()) { // chunk.isNotEmpty() + continue; + } + + // If there's enough to fill a packet, overflow it + if (hodgepodge$rollingChunks.size() == chunksPerPacket) { + hodgepodge$chunkSends.add(new ObjectImmutableList<>(hodgepodge$rollingChunks)); + hodgepodge$rollingChunks.clear(); + } + hodgepodge$rollingChunks.add(chunk); + hodgepodge$rollingTEs.addAll(((WorldServer) worldObj).func_147486_a(ccip.chunkXPos * 16, 0, ccip.chunkZPos * 16, ccip.chunkXPos * 16 + 15, 256, ccip.chunkZPos * 16 + 15)); + allChunks.remove(); + numChunks++; + } + + // Since it only gets cleared when it's full, rollingChunks always holds the last ones + hodgepodge$chunkSends.add(new ObjectImmutableList<>(hodgepodge$rollingChunks)); + + if (numChunks > 0) { + for (int i = 0; i < hodgepodge$chunkSends.size(); ++i) { + playerNetServerHandler.sendPacket(new S26PacketMapChunkBulk(hodgepodge$chunkSends.get(i))); + } + + for (TileEntity tileentity : hodgepodge$rollingTEs) { + func_147097_b(tileentity); + } + + for (int i = 0; i < numChunks; ++i) { + final int div = i / chunksPerPacket; + final int rem = i % chunksPerPacket; + final Chunk chunk = hodgepodge$chunkSends.get(div).get(rem); + getServerForPlayer().getEntityTracker().func_85172_a((EntityPlayerMP) (Object) this, chunk); + MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.Watch(chunk.getChunkCoordIntPair(), (EntityPlayerMP) (Object) this)); + } + } + hodgepodge$chunkSends.clear(); + hodgepodge$rollingChunks.clear(); + hodgepodge$rollingTEs.clear(); + ci.cancel(); + } +} From 60a9689073df66dbd29826febddfb11635db6477 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Tue, 5 Nov 2024 22:38:20 -0500 Subject: [PATCH 2/5] Spotless --- .../hodgepodge/config/SpeedupsConfig.java | 9 ++-- .../mitchej123/hodgepodge/mixins/Mixins.java | 7 ++- .../fastload/MixinEntityPlayerMP.java | 46 +++++++++++++------ 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java index f0434be2..f59d9560 100644 --- a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java +++ b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java @@ -40,15 +40,14 @@ public class SpeedupsConfig { @Config.DefaultBoolean(true) public static boolean speedupChunkProviderClient; - @Config.Comment( - "Removes hard caps on chunk handling speed, and replaces some internals with faster/safer replacements. " + - "Experimental and probably incompatible with hybrid servers!") + @Config.Comment("Removes hard caps on chunk handling speed, and replaces some internals with faster/safer replacements. " + + "Experimental and probably incompatible with hybrid servers!") @Config.DefaultBoolean(false) @Config.RequiresMcRestart public static boolean fastChunkHandling; - @Config.Comment("The maximum speed of chunkloading per player, in chunks/tick. High values may overload clients! Only active with fastChunkHandling.\n" + - "For reference: Vanilla is 5, or 100 chunks/sec. At 32 render distance = 4225 chunks, loading the world would take 42.25 seconds.") + @Config.Comment("The maximum speed of chunkloading per player, in chunks/tick. High values may overload clients! Only active with fastChunkHandling.\n" + + "For reference: Vanilla is 5, or 100 chunks/sec. At 32 render distance = 4225 chunks, loading the world would take 42.25 seconds.") @Config.DefaultInt(50) public static int maxSendSpeed; diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java index 7e879eb8..57c4c0b6 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java @@ -446,10 +446,9 @@ public enum Mixins { new Builder("Memory fixes").setPhase(Phase.EARLY).setSide(Side.CLIENT).addTargetedMod(TargetedMod.VANILLA) .addMixinClasses("memory.MixinFMLClientHandler").setApplyIf(() -> FixesConfig.enableMemoryFixes)), - FAST_CHUNK_LOADING(new Builder("Invasively accelerates chunk handling").setPhase(Phase.EARLY) - .setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA) - .addMixinClasses( -// "minecraft.fastload.MixinIntCache", + FAST_CHUNK_LOADING(new Builder("Invasively accelerates chunk handling").setPhase(Phase.EARLY).setSide(Side.BOTH) + .addTargetedMod(TargetedMod.VANILLA).addMixinClasses( + // "minecraft.fastload.MixinIntCache", "minecraft.fastload.MixinEntityPlayerMP") .setApplyIf(() -> SpeedupsConfig.fastChunkHandling)), diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinEntityPlayerMP.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinEntityPlayerMP.java index e7e51028..458d34d2 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinEntityPlayerMP.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinEntityPlayerMP.java @@ -1,13 +1,8 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import com.mitchej123.hodgepodge.config.SpeedupsConfig; -import com.mojang.authlib.GameProfile; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectImmutableList; -import java.util.ArrayList; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; + import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.NetHandlerPlayServer; @@ -19,7 +14,7 @@ import net.minecraft.world.chunk.Chunk; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.ChunkWatchEvent; -import org.apache.logging.log4j.Logger; + import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -28,6 +23,12 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.mitchej123.hodgepodge.config.SpeedupsConfig; +import com.mojang.authlib.GameProfile; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectImmutableList; + @Mixin(EntityPlayerMP.class) public abstract class MixinEntityPlayerMP extends EntityPlayer { @@ -52,8 +53,6 @@ public abstract class MixinEntityPlayerMP extends EntityPlayer { @Shadow public abstract WorldServer getServerForPlayer(); - @Shadow @Final private static Logger logger; - public MixinEntityPlayerMP(World world, GameProfile profile) { super(world, profile); } @@ -62,9 +61,19 @@ public MixinEntityPlayerMP(World world, GameProfile profile) { * This injects just before where vanilla handles chunk sending and returns before then. This will mess up any * mixins that target the tail of this function, but other solutions are similarly invasive. */ - @Inject(method = "onUpdate", at = @At(value = "FIELD", target = "Lnet/minecraft/entity/player/EntityPlayerMP;loadedChunks:Ljava/util/List;", shift = At.Shift.BEFORE, ordinal = 0), cancellable = true) + @Inject( + method = "onUpdate", + at = @At( + value = "FIELD", + target = "Lnet/minecraft/entity/player/EntityPlayerMP;loadedChunks:Ljava/util/List;", + shift = At.Shift.BEFORE, + ordinal = 0), + cancellable = true) private void hodgepodge$replaceChunkSending(CallbackInfo ci) { - if (loadedChunks.isEmpty()) { ci.cancel(); return; } + if (loadedChunks.isEmpty()) { + ci.cancel(); + return; + } int numChunks = 0; final Iterator allChunks = loadedChunks.iterator(); @@ -74,7 +83,8 @@ public MixinEntityPlayerMP(World world, GameProfile profile) { while (allChunks.hasNext() && numChunks < SpeedupsConfig.maxSendSpeed) { final ChunkCoordIntPair ccip = allChunks.next(); if (ccip == null) { - allChunks.remove(); continue; + allChunks.remove(); + continue; } if (!worldObj.blockExists(ccip.chunkXPos << 4, 0, ccip.chunkZPos << 4)) { @@ -92,7 +102,14 @@ public MixinEntityPlayerMP(World world, GameProfile profile) { hodgepodge$rollingChunks.clear(); } hodgepodge$rollingChunks.add(chunk); - hodgepodge$rollingTEs.addAll(((WorldServer) worldObj).func_147486_a(ccip.chunkXPos * 16, 0, ccip.chunkZPos * 16, ccip.chunkXPos * 16 + 15, 256, ccip.chunkZPos * 16 + 15)); + hodgepodge$rollingTEs.addAll( + ((WorldServer) worldObj).func_147486_a( + ccip.chunkXPos * 16, + 0, + ccip.chunkZPos * 16, + ccip.chunkXPos * 16 + 15, + 256, + ccip.chunkZPos * 16 + 15)); allChunks.remove(); numChunks++; } @@ -114,7 +131,8 @@ public MixinEntityPlayerMP(World world, GameProfile profile) { final int rem = i % chunksPerPacket; final Chunk chunk = hodgepodge$chunkSends.get(div).get(rem); getServerForPlayer().getEntityTracker().func_85172_a((EntityPlayerMP) (Object) this, chunk); - MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.Watch(chunk.getChunkCoordIntPair(), (EntityPlayerMP) (Object) this)); + MinecraftForge.EVENT_BUS + .post(new ChunkWatchEvent.Watch(chunk.getChunkCoordIntPair(), (EntityPlayerMP) (Object) this)); } } hodgepodge$chunkSends.clear(); From 848ab1adcc291d39638aec8e8f3b575312b44eb5 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Tue, 5 Nov 2024 23:21:24 -0500 Subject: [PATCH 3/5] Pull int cache changes This does *not* pull in False's edits yet, I'm waiting on approval first rather than sorting it out later. Much cleaner. --- .../hodgepodge/config/SpeedupsConfig.java | 8 +- .../hodgepodge/hax/FastIntCache.java | 32 +++ .../mitchej123/hodgepodge/mixins/Mixins.java | 9 +- .../minecraft/fastload/MixinIntCache.java | 22 +++ .../fastload/MixinWorldChunkManager.java | 187 ++++++++++++++++++ src/main/resources/META-INF/hodgepodge_at.cfg | 2 + 6 files changed, 255 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/mitchej123/hodgepodge/hax/FastIntCache.java create mode 100644 src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinIntCache.java create mode 100644 src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldChunkManager.java diff --git a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java index f59d9560..5919aaa0 100644 --- a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java +++ b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java @@ -40,8 +40,12 @@ public class SpeedupsConfig { @Config.DefaultBoolean(true) public static boolean speedupChunkProviderClient; - @Config.Comment("Removes hard caps on chunk handling speed, and replaces some internals with faster/safer replacements. " - + "Experimental and probably incompatible with hybrid servers!") + @Config.Comment("Rewrites internal cache methods to be safer and faster. Experimental, use at your own risk!") + @Config.DefaultBoolean(false) + @Config.RequiresMcRestart + public static boolean fastIntCache; + + @Config.Comment("Removes hard caps on chunk handling speed. Experimental and probably incompatible with hybrid servers!") @Config.DefaultBoolean(false) @Config.RequiresMcRestart public static boolean fastChunkHandling; diff --git a/src/main/java/com/mitchej123/hodgepodge/hax/FastIntCache.java b/src/main/java/com/mitchej123/hodgepodge/hax/FastIntCache.java new file mode 100644 index 00000000..62ffd6fd --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/hax/FastIntCache.java @@ -0,0 +1,32 @@ +package com.mitchej123.hodgepodge.hax; + +import java.util.List; +import java.util.Map; + +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +public class FastIntCache { + + private static final int SMALLEST = 256; + private static final int MIN_LEVEL = 32 - Integer.numberOfLeadingZeros(SMALLEST - 1); + + private static final Map> cachedObjects = new Int2ObjectOpenHashMap<>(); + + public static synchronized int[] getCache(int size) { + + // Get the smallest power of two larger than or equal to the number + final int level = (size <= SMALLEST) ? MIN_LEVEL : 32 - Integer.numberOfLeadingZeros(size - 1); + + final List caches = cachedObjects.computeIfAbsent(level, i -> new ObjectArrayList<>()); + + if (caches.isEmpty()) return new int[2 << (level - 1)]; + return caches.remove(caches.size() - 1); + } + + public static synchronized void releaseCache(int[] cache) { + + final int level = (cache.length <= SMALLEST) ? MIN_LEVEL : 32 - Integer.numberOfLeadingZeros(cache.length - 1); + cachedObjects.computeIfAbsent(level, i -> new ObjectArrayList<>()).add(cache); + } +} diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java index 57c4c0b6..4a791f81 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java @@ -446,10 +446,13 @@ public enum Mixins { new Builder("Memory fixes").setPhase(Phase.EARLY).setSide(Side.CLIENT).addTargetedMod(TargetedMod.VANILLA) .addMixinClasses("memory.MixinFMLClientHandler").setApplyIf(() -> FixesConfig.enableMemoryFixes)), + FAST_INT_CACHE(new Builder("Rewrite internal caching methods to be safer and faster").setPhase(Phase.EARLY) + .setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA) + .addMixinClasses("minecraft.fastload.MixinIntCache", "minecraft.fastload.MixinWorldChunkManager") + .setApplyIf(() -> SpeedupsConfig.fastIntCache)), + FAST_CHUNK_LOADING(new Builder("Invasively accelerates chunk handling").setPhase(Phase.EARLY).setSide(Side.BOTH) - .addTargetedMod(TargetedMod.VANILLA).addMixinClasses( - // "minecraft.fastload.MixinIntCache", - "minecraft.fastload.MixinEntityPlayerMP") + .addTargetedMod(TargetedMod.VANILLA).addMixinClasses("minecraft.fastload.MixinEntityPlayerMP") .setApplyIf(() -> SpeedupsConfig.fastChunkHandling)), MEMORY_FIXES_IC2(new Builder("Removes allocation spam from the Direction.applyTo method").setPhase(Phase.LATE) diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinIntCache.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinIntCache.java new file mode 100644 index 00000000..b9a2cee7 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinIntCache.java @@ -0,0 +1,22 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; + +import net.minecraft.world.gen.layer.IntCache; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import com.mitchej123.hodgepodge.hax.FastIntCache; + +@Mixin(IntCache.class) +public class MixinIntCache { + + /** + * @author ah-OOG-ah + * @reason The old methods are non-threadsafe and barely safe at all - they recycle all allocated instances instead + * of explicitly releasing them. + */ + @Overwrite + public static synchronized int[] getIntCache(int size) { + return FastIntCache.getCache(size); + } +} diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldChunkManager.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldChunkManager.java new file mode 100644 index 00000000..3c48a448 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldChunkManager.java @@ -0,0 +1,187 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; + +import java.util.List; +import java.util.Random; + +import net.minecraft.crash.CrashReport; +import net.minecraft.crash.CrashReportCategory; +import net.minecraft.util.ReportedException; +import net.minecraft.world.ChunkPosition; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraft.world.biome.WorldChunkManager; +import net.minecraft.world.gen.layer.GenLayer; + +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.hax.FastIntCache; + +@Mixin(WorldChunkManager.class) +public class MixinWorldChunkManager { + + @Shadow + private GenLayer genBiomes; + + @Redirect( + method = { "getRainfall", "getBiomesForGeneration", "areBiomesViable", + "getBiomeGenAt([Lnet/minecraft/world/biome/BiomeGenBase;IIIIZ)[Lnet/minecraft/world/biome/BiomeGenBase;", + "findBiomePosition" }, + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/gen/layer/IntCache;resetIntCache()V")) + private void hodgepodge$nukeIntCache() {} + + @Inject( + method = "getRainfall", + at = @At( + value = "INVOKE_ASSIGN", + target = "Lnet/minecraft/world/gen/layer/GenLayer;getInts(IIII)[I", + shift = At.Shift.AFTER), + cancellable = true) + private void hodgepodge$recycleCacheRain(float[] downfalls, int x, int z, int width, int height, + CallbackInfoReturnable cir, @Local(name = "aint") int[] ints) { + + for (int i = 0; i < width * height; ++i) { + try { + float f = (float) BiomeGenBase.getBiome(ints[i]).getIntRainfall() / 65536.0F; + + if (f > 1.0F) { + f = 1.0F; + } + + downfalls[i] = f; + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Invalid Biome id"); + CrashReportCategory crashreportcategory = crashreport.makeCategory("DownfallBlock"); + crashreportcategory.addCrashSection("biome id", i); + crashreportcategory.addCrashSection("downfalls[] size", downfalls.length); + crashreportcategory.addCrashSection("x", x); + crashreportcategory.addCrashSection("z", z); + crashreportcategory.addCrashSection("w", width); + crashreportcategory.addCrashSection("h", height); + throw new ReportedException(crashreport); + } + } + + FastIntCache.releaseCache(ints); + cir.setReturnValue(downfalls); + } + + @Inject( + method = "getBiomesForGeneration", + at = @At( + value = "INVOKE_ASSIGN", + target = "Lnet/minecraft/world/gen/layer/GenLayer;getInts(IIII)[I", + shift = At.Shift.AFTER), + cancellable = true) + private void hodgepodge$recycleCacheBiomes(BiomeGenBase[] biomes, int x, int z, int width, int height, + CallbackInfoReturnable cir, @Local(name = "aint") int[] ints) { + + try { + for (int i = 0; i < width * height; ++i) { + biomes[i] = BiomeGenBase.getBiome(ints[i]); + } + + FastIntCache.releaseCache(ints); + cir.setReturnValue(biomes); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Invalid Biome id"); + CrashReportCategory crashreportcategory = crashreport.makeCategory("RawBiomeBlock"); + crashreportcategory.addCrashSection("biomes[] size", biomes.length); + crashreportcategory.addCrashSection("x", x); + crashreportcategory.addCrashSection("z", z); + crashreportcategory.addCrashSection("w", width); + crashreportcategory.addCrashSection("h", height); + throw new ReportedException(crashreport); + } + } + + @Inject( + method = "getBiomeGenAt([Lnet/minecraft/world/biome/BiomeGenBase;IIIIZ)[Lnet/minecraft/world/biome/BiomeGenBase;", + at = @At( + value = "INVOKE_ASSIGN", + target = "Lnet/minecraft/world/gen/layer/GenLayer;getInts(IIII)[I", + shift = At.Shift.AFTER), + cancellable = true) + private void hodgepodge$recycleCacheBiomeAt(BiomeGenBase[] biomes, int p_76931_2_, int p_76931_3_, int width, + int height, boolean p_76931_6_, CallbackInfoReturnable cir, + @Local(name = "aint") int[] ints) { + + for (int i = 0; i < width * height; ++i) { + biomes[i] = BiomeGenBase.getBiome(ints[i]); + } + + FastIntCache.releaseCache(ints); + cir.setReturnValue(biomes); + } + + @Inject( + method = "areBiomesViable", + at = @At( + value = "INVOKE_ASSIGN", + target = "Lnet/minecraft/world/gen/layer/GenLayer;getInts(IIII)[I", + shift = At.Shift.AFTER), + cancellable = true) + private void hodgepodge$recycleCacheViable(int x, int z, int radius, List allowed, + CallbackInfoReturnable cir, @Local(name = "l1") int areaWidth, @Local(name = "i2") int areaHeight, + @Local(ordinal = 0) int[] cache) { + + try { + for (int i = 0; i < areaWidth * areaHeight; ++i) { + BiomeGenBase biomegenbase = BiomeGenBase.getBiome(cache[i]); + + if (!allowed.contains(biomegenbase)) { + + FastIntCache.releaseCache(cache); + cir.setReturnValue(false); + return; + } + } + + FastIntCache.releaseCache(cache); + cir.setReturnValue(true); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Invalid Biome id"); + CrashReportCategory crashreportcategory = crashreport.makeCategory("Layer"); + crashreportcategory.addCrashSection("Layer", this.genBiomes.toString()); + crashreportcategory.addCrashSection("x", x); + crashreportcategory.addCrashSection("z", z); + crashreportcategory.addCrashSection("radius", radius); + crashreportcategory.addCrashSection("allowed", allowed); + throw new ReportedException(crashreport); + } + } + + @Inject( + method = "findBiomePosition", + at = @At( + value = "INVOKE_ASSIGN", + target = "Lnet/minecraft/world/gen/layer/GenLayer;getInts(IIII)[I", + shift = At.Shift.AFTER), + cancellable = true) + private void hodgepodge$recycleCacheFindBiome(int x, int z, int radius, List p_150795_4_, + Random p_150795_5_, CallbackInfoReturnable cir, @Local(name = "l1") int l1, + @Local(name = "i2") int i2, @Local(name = "l") int l, @Local(name = "i1") int i1, + @Local(ordinal = 0) int[] ints) { + + ChunkPosition chunkposition = null; + int j2 = 0; + + for (int i = 0; i < l1 * i2; ++i) { + int l2 = l + i % l1 << 2; + int i3 = i1 + i / l1 << 2; + BiomeGenBase biomegenbase = BiomeGenBase.getBiome(ints[i]); + + if (p_150795_4_.contains(biomegenbase) && (chunkposition == null || p_150795_5_.nextInt(j2 + 1) == 0)) { + chunkposition = new ChunkPosition(l2, 0, i3); + ++j2; + } + } + + FastIntCache.releaseCache(ints); + cir.setReturnValue(chunkposition); + } +} diff --git a/src/main/resources/META-INF/hodgepodge_at.cfg b/src/main/resources/META-INF/hodgepodge_at.cfg index f1bc32a1..529105e2 100644 --- a/src/main/resources/META-INF/hodgepodge_at.cfg +++ b/src/main/resources/META-INF/hodgepodge_at.cfg @@ -21,3 +21,5 @@ public net.minecraft.client.resources.FileResourcePack field_110600_d # resource public net.minecraft.client.resources.SimpleReloadableResourceManager field_110548_a # domainResourceManagers public net.minecraft.entity.EntityList field_75624_e # classToIDMapping public net.minecraft.world.WorldServer func_73053_d()V # wakeAllPlayers() + +public net.minecraft.server.management.PlayerManager$PlayerInstance From 79e937d0ec4db08fbefb72ce94299304ee9011dd Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Tue, 5 Nov 2024 23:46:46 -0500 Subject: [PATCH 4/5] Pull fast chunk discarding Probably not super needed, but might as well --- .../hodgepodge/config/SpeedupsConfig.java | 5 +++++ .../mitchej123/hodgepodge/mixins/Mixins.java | 3 ++- .../fastload/MixinChunkProviderServer.java | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinChunkProviderServer.java diff --git a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java index 5919aaa0..cc2755b6 100644 --- a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java +++ b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java @@ -55,6 +55,11 @@ public class SpeedupsConfig { @Config.DefaultInt(50) public static int maxSendSpeed; + @Config.Comment("The maximum speed of chunk unloading, in chunks/tick. High values may overload servers! Only active with fastChunkHandling.\n" + + "For reference: Vanilla is 100, or 2000 chunks/sec. At 32 render distance = 4225 chunks, unloading the world would take 2.1125 seconds.") + @Config.DefaultInt(100) + public static int maxUnloadSpeed; + // Biomes O' Plenty @Config.Comment("Speedup biome fog rendering in BiomesOPlenty") diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java index 4a791f81..e88cc115 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java @@ -452,7 +452,8 @@ public enum Mixins { .setApplyIf(() -> SpeedupsConfig.fastIntCache)), FAST_CHUNK_LOADING(new Builder("Invasively accelerates chunk handling").setPhase(Phase.EARLY).setSide(Side.BOTH) - .addTargetedMod(TargetedMod.VANILLA).addMixinClasses("minecraft.fastload.MixinEntityPlayerMP") + .addTargetedMod(TargetedMod.VANILLA) + .addMixinClasses("minecraft.fastload.MixinEntityPlayerMP", "minecraft.fastload.MixinChunkProviderServer") .setApplyIf(() -> SpeedupsConfig.fastChunkHandling)), MEMORY_FIXES_IC2(new Builder("Removes allocation spam from the Direction.applyTo method").setPhase(Phase.LATE) diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinChunkProviderServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinChunkProviderServer.java new file mode 100644 index 00000000..b0667fb9 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinChunkProviderServer.java @@ -0,0 +1,18 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; + +import net.minecraft.world.gen.ChunkProviderServer; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +import com.mitchej123.hodgepodge.config.SpeedupsConfig; + +@Mixin(ChunkProviderServer.class) +public class MixinChunkProviderServer { + + @ModifyConstant(method = "unloadQueuedChunks", constant = @Constant(intValue = 100, ordinal = 0)) + private int hodgepodge$fastChunkDiscard(int original) { + return SpeedupsConfig.maxUnloadSpeed; + } +} From 986f52723640dfe168b2a16353343dd57e31aa7f Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Tue, 5 Nov 2024 23:50:06 -0500 Subject: [PATCH 5/5] Add a minimum There's no reason to go lower than vanilla for these. --- .../java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java index cc2755b6..f751abd9 100644 --- a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java +++ b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java @@ -53,11 +53,13 @@ public class SpeedupsConfig { @Config.Comment("The maximum speed of chunkloading per player, in chunks/tick. High values may overload clients! Only active with fastChunkHandling.\n" + "For reference: Vanilla is 5, or 100 chunks/sec. At 32 render distance = 4225 chunks, loading the world would take 42.25 seconds.") @Config.DefaultInt(50) + @Config.RangeInt(min = 5) public static int maxSendSpeed; @Config.Comment("The maximum speed of chunk unloading, in chunks/tick. High values may overload servers! Only active with fastChunkHandling.\n" + "For reference: Vanilla is 100, or 2000 chunks/sec. At 32 render distance = 4225 chunks, unloading the world would take 2.1125 seconds.") - @Config.DefaultInt(100) + @Config.DefaultInt(220) + @Config.RangeInt(min = 100) public static int maxUnloadSpeed; // Biomes O' Plenty