From cc8a227c2ecc6e00e7d332437a2fab22d284f132 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Fri, 15 Mar 2024 05:39:11 -0400 Subject: [PATCH 01/18] Add light chunk threading --- gradle.properties | 2 +- .../hodgepodge/client/chat/ChatHandler.java | 3 +- .../hodgepodge/config/SpeedupsConfig.java | 5 + .../mitchej123/hodgepodge/mixins/Mixins.java | 7 + .../minecraft/fastload/MixinIntCache.java | 20 + .../fastload/MixinWorldChunkManager.java | 194 ++++++ .../minecraft/fastload/MixinWorldServer.java | 92 +++ .../hodgepodge/server/ChunkAndNbt.java | 18 + .../mitchej123/hodgepodge/server/FastCPS.java | 595 ++++++++++++++++++ .../hodgepodge/server/NewIntCache.java | 32 + src/main/resources/META-INF/hodgepodge_at.cfg | 1 + 11 files changed, 966 insertions(+), 3 deletions(-) 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 create mode 100644 src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java create mode 100644 src/main/java/com/mitchej123/hodgepodge/server/ChunkAndNbt.java create mode 100644 src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java create mode 100644 src/main/java/com/mitchej123/hodgepodge/server/NewIntCache.java diff --git a/gradle.properties b/gradle.properties index 5130f1e5..6191763f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -45,7 +45,7 @@ enableModernJavaSyntax = true # Enables injecting missing generics into the decompiled source code for a better coding experience. # Turns most publicly visible List, Map, etc. into proper List, Map types. -enableGenericInjection = false +enableGenericInjection = true # Generate a class with a String field for the mod version named as defined below. # If generateGradleTokenClass is empty or not missing, no such class will be generated. diff --git a/src/main/java/com/mitchej123/hodgepodge/client/chat/ChatHandler.java b/src/main/java/com/mitchej123/hodgepodge/client/chat/ChatHandler.java index 0cd35671..2c83a249 100644 --- a/src/main/java/com/mitchej123/hodgepodge/client/chat/ChatHandler.java +++ b/src/main/java/com/mitchej123/hodgepodge/client/chat/ChatHandler.java @@ -23,7 +23,6 @@ public static boolean tryCompactMessage(IChatComponent imsg, List chat return false; } - @SuppressWarnings("unchecked") private static boolean areMessagesIdentical(IChatComponent imsg, IChatComponent prevMsg) { final int size1 = imsg.getSiblings().size(); final int size2 = prevMsg.getSiblings().size(); @@ -38,7 +37,7 @@ private static boolean areMessagesIdentical(IChatComponent imsg, IChatComponent if (!(prevMsg.getSiblings().get(size2 - 1) instanceof ChatComponentCount)) { return false; } - final Object removed = prevMsg.getSiblings().remove(size2 - 1); + final IChatComponent removed = prevMsg.getSiblings().remove(size2 - 1); final boolean equals = imsg.equals(prevMsg); prevMsg.getSiblings().add(removed); if (equals) { diff --git a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java index f70a18e7..5ec59943 100644 --- a/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java +++ b/src/main/java/com/mitchej123/hodgepodge/config/SpeedupsConfig.java @@ -42,6 +42,11 @@ public class SpeedupsConfig { @Config.RequiresMcRestart public static boolean tcpNoDelay; + @Config.Comment("Lightly threads chunk generation, loading, and discarding. Experimental, use at your own risk!") + @Config.DefaultBoolean(false) + @Config.RequiresMcRestart + public static boolean fastChunkHandling; + // 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 be4ab6bf..00c09b71 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java @@ -376,6 +376,13 @@ public enum Mixins { .setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA).addMixinClasses("minecraft.MixinBlock_LighterWater") .setApplyIf(() -> TweaksConfig.useLighterWater)), + FAST_CHUNK_LOADING(new Builder("Lightly threads chunk generation and loading").setPhase(Phase.EARLY) + .setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA).addMixinClasses( + "minecraft.fastload.MixinIntCache", + "minecraft.fastload.MixinWorldChunkManager", + "minecraft.fastload.MixinWorldServer") + .setApplyIf(() -> SpeedupsConfig.fastChunkHandling)), + // Ic2 adjustments IC2_UNPROTECTED_GET_BLOCK_FIX(new Builder("IC2 Kinetic Fix").setPhase(Phase.EARLY).setSide(Side.BOTH) .addMixinClasses("ic2.MixinIc2WaterKinetic").setApplyIf(() -> FixesConfig.fixIc2UnprotectedGetBlock) 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..c4f0e426 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinIntCache.java @@ -0,0 +1,20 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; + +import com.mitchej123.hodgepodge.server.NewIntCache; +import net.minecraft.world.gen.layer.IntCache; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +@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 NewIntCache.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..ce4f894f --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldChunkManager.java @@ -0,0 +1,194 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; + +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.server.NewIntCache; +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 java.util.List; +import java.util.Random; + +@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); + } + } + + NewIntCache.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]); + } + + NewIntCache.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]); + } + + NewIntCache.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)) { + + NewIntCache.releaseCache(cache); + cir.setReturnValue(false); + return; + } + } + + NewIntCache.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; + } + } + + NewIntCache.releaseCache(ints); + cir.setReturnValue(chunkposition); + } +} diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java new file mode 100644 index 00000000..8b0f6d3d --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java @@ -0,0 +1,92 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.server.FastCPS; +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.profiler.Profiler; +import net.minecraft.world.*; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IChunkProvider; +import net.minecraft.world.chunk.storage.AnvilChunkLoader; +import net.minecraft.world.chunk.storage.IChunkLoader; +import net.minecraft.world.gen.ChunkProviderServer; +import net.minecraft.world.storage.ISaveHandler; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +@Mixin(WorldServer.class) +public abstract class MixinWorldServer extends World { + + @Shadow + public ChunkProviderServer theChunkProviderServer; + @Unique + private final Object2ObjectOpenHashMap> hodgepodge$chunksToLoad = new Object2ObjectOpenHashMap<>(); + @Unique + private final Object2ObjectOpenHashMap hodgepodge$chunks = new Object2ObjectOpenHashMap<>(); + + @WrapOperation(method = "createChunkProvider", at = @At(value = "NEW", target = "(Lnet/minecraft/world/WorldServer;Lnet/minecraft/world/chunk/storage/IChunkLoader;Lnet/minecraft/world/chunk/IChunkProvider;)Lnet/minecraft/world/gen/ChunkProviderServer;")) + private ChunkProviderServer hodgepodge$replaceChunkProvider(WorldServer server, IChunkLoader loader, IChunkProvider backingCP, Operation original) { + return new FastCPS(server, (AnvilChunkLoader) loader, backingCP); + } + + @Inject(method = "func_147456_g", at = @At(value = "INVOKE", target = "Ljava/util/Set;iterator()Ljava/util/Iterator;")) + private void hodgepodge$threadChunkGen(CallbackInfo ci) { + + this.hodgepodge$chunksToLoad.clear(); + + // Queue chunks on worker threads or main + final ChunkProviderServer cps = this.theChunkProviderServer; + final AnvilChunkLoader acl = (AnvilChunkLoader) cps.currentChunkLoader; + for (ChunkCoordIntPair c : this.activeChunkSet) { + + final long key = ChunkCoordIntPair.chunkXZ2Int(c.chunkXPos, c.chunkZPos); + final CompletableFuture cf = new CompletableFuture<>(); + + // If already loaded, just return it + if (cps.loadedChunkHashMap.containsItem(key)) { + + cf.complete((Chunk) cps.loadedChunkHashMap.getValueByKey(key)); + } else if (acl.chunkExists(this, c.chunkXPos, c.chunkZPos)) { + + // The chunk exists on disk, but needs to be loaded. Trivially threaded, forge already has functions for that. + ((FastCPS) this.theChunkProviderServer).queueDiskLoad(c.chunkXPos, c.chunkZPos, key, cf); + } else { + + // These chunks need to be generated; let's try that + ((FastCPS) this.theChunkProviderServer).queueGenerate(c.chunkXPos, c.chunkZPos, key, cf); + } + + this.hodgepodge$chunksToLoad.put(c, cf); + } + + // All mChunk updates need to finish before the tick ends, to avoid CMEs... at least for now. + // Guarantee that no chunks are still processing before moving on + Object2ObjectMaps.fastForEach(this.hodgepodge$chunksToLoad, e -> { + try { + this.hodgepodge$chunks.put(e.getKey(), e.getValue().get()); + } catch (InterruptedException | ExecutionException ex) { throw new RuntimeException(ex); } + }); + } + + @Redirect(method = "func_147456_g", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/WorldServer;getChunkFromChunkCoords(II)Lnet/minecraft/world/chunk/Chunk;")) + private Chunk hodgepodge$threadChunkGen(WorldServer instance, int cx, int cz, @Local(ordinal = 0) ChunkCoordIntPair c) { + + final Chunk ch = this.hodgepodge$chunks.get(c); + return ch != null ? ch : this.chunkProvider.provideChunk(cx, cz); + } + + public MixinWorldServer(ISaveHandler p_i45368_1_, String p_i45368_2_, WorldProvider p_i45368_3_, WorldSettings p_i45368_4_, Profiler p_i45368_5_) { + super(p_i45368_1_, p_i45368_2_, p_i45368_3_, p_i45368_4_, p_i45368_5_); + } +} diff --git a/src/main/java/com/mitchej123/hodgepodge/server/ChunkAndNbt.java b/src/main/java/com/mitchej123/hodgepodge/server/ChunkAndNbt.java new file mode 100644 index 00000000..787cc7dd --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/server/ChunkAndNbt.java @@ -0,0 +1,18 @@ +package com.mitchej123.hodgepodge.server; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.chunk.Chunk; + +public class ChunkAndNbt { + private final Chunk chunk; + private final NBTTagCompound nbt; + + public ChunkAndNbt(Chunk chunk, NBTTagCompound nbt) { + this.chunk = chunk; + this.nbt = nbt; + } + + public Chunk getChunk() { return this.chunk; } + + public NBTTagCompound getNbt() { return this.nbt; } +} diff --git a/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java new file mode 100644 index 00000000..12f7e921 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java @@ -0,0 +1,595 @@ +package com.mitchej123.hodgepodge.server; + +import com.mitchej123.hodgepodge.Common; +import cpw.mods.fml.common.registry.GameRegistry; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.block.Block; +import net.minecraft.crash.CrashReport; +import net.minecraft.crash.CrashReportCategory; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityList; +import net.minecraft.entity.EnumCreatureType; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.IProgressUpdate; +import net.minecraft.util.ReportedException; +import net.minecraft.world.*; +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.EmptyChunk; +import net.minecraft.world.chunk.IChunkProvider; +import net.minecraft.world.chunk.storage.AnvilChunkLoader; +import net.minecraft.world.gen.ChunkProviderServer; +import net.minecraftforge.common.DimensionManager; +import net.minecraftforge.common.ForgeChunkManager; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.ChunkDataEvent; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static com.mitchej123.hodgepodge.Common.log; + +public class FastCPS extends ChunkProviderServer { + + /** + * used by unload100OldestChunks to iterate the loadedChunkHashMap for unload (underlying assumption, first in, + * first out) + */ + private final LongOpenHashSet chunksToUnload = new LongOpenHashSet(); + private final LongOpenHashSet loadingChunks = new LongOpenHashSet(); + private final Chunk empty; + private int maxUnloadsPerTick = 500; + + private final ThreadLocal isMChunk = ThreadLocal.withInitial(() -> false); + private final ExecutorService mChunk = Executors.newSingleThreadExecutor(); + private final ExecutorService mPregen = Executors.newSingleThreadExecutor(); + private final int maxWorkers = 6; + private final ExecutorService workers = Executors.newFixedThreadPool(maxWorkers); + // A thread-local copy of the backing world generator + private final ThreadLocal localProvider = + ThreadLocal.withInitial(() -> this.worldObj.provider.createChunkGenerator()); + + public FastCPS(WorldServer worldObj, AnvilChunkLoader loader, IChunkProvider backingCP) { + super(worldObj, loader, backingCP); + + this.loadedChunks = new ObjectArrayList<>(); + + this.empty = new EmptyChunk(worldObj, 0, 0); + this.mChunk.execute(() -> this.isMChunk.set(true)); + } + + public void queueDiskLoad(int cx, int cz, long key, CompletableFuture chunkf) { + this.workers.execute(() -> { + final ChunkAndNbt cnbt = this.loadChunkFromDisk(cx, cz); + this.mChunk.execute(() -> chunkf.complete(this.finishChunkFromDisk(cnbt, cx, cz, key))); + }); + } + + public void queueGenerate(int cx, int cz, long key, CompletableFuture cf) { + + this.mPregen.execute(() -> { + final Chunk c = this.generateUndecoratedChunk(cx, cz, key); + this.mChunk.execute(() -> cf.complete(this.decorateChunk(c, cx, cz, key))); + }); + } + + /** + * Attempt to generate a chunk. Queues pregeneration to keep it on a single thread. + */ + public Chunk generateChunk(int cx, int cz, long key) { + final CompletableFuture cf = new CompletableFuture<>(); + this.mPregen.execute(() -> cf.complete(this.generateUndecoratedChunk(cx, cz, key))); + + try { + return this.decorateChunk(cf.get(), cx, cz, key); + } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } + } + + /** + * Attempt to generate an undecorated chunk, meant to run async. Does no safety checks, be careful! + */ + public Chunk generateUndecoratedChunk(int cx, int cz, long key) { + final Chunk chunk; + + if (this.currentChunkProvider == null) { + chunk = this.empty; + } else { + try { + chunk = this.localProvider.get().provideChunk(cx, cz); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Exception generating new chunk"); + CrashReportCategory crashreportcategory = crashreport.makeCategory("Chunk to be generated"); + crashreportcategory.addCrashSection("Location", String.format("%d,%d", cx, cz)); + crashreportcategory.addCrashSection("Position hash", key); + crashreportcategory.addCrashSection("Generator", this.localProvider.get().makeString()); + throw new ReportedException(crashreport); + } + } + + return chunk; + } + + /** + * Attempt to decorate a generated chunk, meant to run on only one thread at a time (but that doesn't have to be + * the main thread). Does no safety checks, be careful! + */ + public Chunk decorateChunk(Chunk chunk, int cx, int cz, long key) { + + this.loadedChunkHashMap.add(key, chunk); + this.loadedChunks.add(chunk); + loadingChunks.remove(key); + chunk.onChunkLoad(); + chunk.populateChunk(this, this, cx, cz); + return chunk; + } + + /** + * Attempt to load a chunk from disk. Does not punt to workers, because it'd block anyways. + */ + public Chunk loadChunkFromDisk(int cx, int cz, long key) { + return this.finishChunkFromDisk(this.loadChunkFromDisk(cx, cz), cx, cz, key); + } + + /** + * Attempts to load a chunk from disk, returns null if not possible. Meant to run async, and doesn't return a full + * chunk - run {@link #finishChunkFromDisk(ChunkAndNbt, int, int, long)} to complete it. + */ + public ChunkAndNbt loadChunkFromDisk(int cx, int cz) { + try { + Object[] data = ((AnvilChunkLoader) this.currentChunkLoader).loadChunk__Async(this.worldObj, cx, cz); + if (data == null) { + return null; + } + + final Chunk chunk = (Chunk) data[0]; + final NBTTagCompound nbt = (NBTTagCompound) data[1]; + final ChunkAndNbt cnbt = new ChunkAndNbt(chunk, nbt); + + final NBTTagList entities = nbt.getTagList("Entities", 10); + + if (entities != null) { + for (int i = 0; i < entities.tagCount(); ++i) { + final NBTTagCompound entityTag = entities.getCompoundTagAt(i); + final Entity entity = EntityList.createEntityFromNBT(entityTag, this.worldObj); + chunk.hasEntities = true; + + if (entity != null) { + chunk.addEntity(entity); + Entity riderEntity = entity; + + for (NBTTagCompound tmpEntityTag = entityTag; tmpEntityTag.hasKey("Riding", 10); tmpEntityTag = tmpEntityTag.getCompoundTag("Riding")) { + final Entity riddenEntity = EntityList.createEntityFromNBT(tmpEntityTag.getCompoundTag("Riding"), this.worldObj); + + if (riddenEntity != null) { + + chunk.addEntity(riddenEntity); + riderEntity.mountEntity(riddenEntity); + } + + riderEntity = riddenEntity; + } + } + } + } + + final NBTTagList teTags = nbt.getTagList("TileEntities", 10); + + if (teTags != null) { + for (int i = 0; i < teTags.tagCount(); ++i) { + final NBTTagCompound teTag = teTags.getCompoundTagAt(i); + TileEntity te = TileEntity.createAndLoadEntity(teTag); + + if (te != null) { + chunk.addTileEntity(te); + } + } + } + + return cnbt; + + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Loading a chunk from disk requires some synchronous action, do it here. Despite taking a + * {@link CompletableFuture}, this is NOT meant to be run on multiple threads at a time - but it doesn't have to be + * run on the main thread. + * @param cnbt If this is null, blocks and generates the chunk instead + */ + public Chunk finishChunkFromDisk(ChunkAndNbt cnbt, int cx, int cz, long key) { + + if (cnbt == null) { + return this.generateChunk(cx, cz, key); + } + + final Chunk chunk = cnbt.getChunk(); + final NBTTagCompound nbt = cnbt.getNbt(); + + // Loading tile ticks has to be done synchronously, for now. + if (nbt.hasKey("TileTicks", 9)) { + final NBTTagList tileTicks = nbt.getTagList("TileTicks", 10); + + if (tileTicks != null) { + for (int j1 = 0; j1 < tileTicks.tagCount(); ++j1) { + final NBTTagCompound tickTag = tileTicks.getCompoundTagAt(j1); + this.worldObj.func_147446_b( + tickTag.getInteger("x"), + tickTag.getInteger("y"), + tickTag.getInteger("z"), + Block.getBlockById(tickTag.getInteger("i")), + tickTag.getInteger("t"), + tickTag.getInteger("p")); + } + } + } + + // This section is very similar to decorateChunk. I won't merge them... for now. + // Don't call ChunkDataEvent.Load async + MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, cnbt.getNbt())); + chunk.lastSaveTime = this.worldObj.getTotalWorldTime(); + this.loadedChunkHashMap.add(key, chunk); + this.loadedChunks.add(chunk); + chunk.onChunkLoad(); + + if (this.currentChunkProvider != null) { + this.localProvider.get().recreateStructures(cx, cz); + } + + chunk.populateChunk(this, this, cx, cz); + + return chunk; + } + + @Override + public List func_152380_a() { + return this.loadedChunks; + } + + /** + * marks chunk for unload by "unload100OldestChunks" if there is no spawn point, or if the center of the chunk is + * outside 200 blocks (x or z) of the spawn + */ + @Override + public void unloadChunksIfNotNearSpawn(int cx, int cz) { + if (this.worldObj.provider.canRespawnHere() && DimensionManager.shouldLoadSpawn(this.worldObj.provider.dimensionId)) { + final ChunkCoordinates chunkcoordinates = this.worldObj.getSpawnPoint(); + int xBlocksFromSpawn = cx * 16 + 8 - chunkcoordinates.posX; + int zBlocksFromSpawn = cz * 16 + 8 - chunkcoordinates.posZ; + + if (xBlocksFromSpawn < -128 || xBlocksFromSpawn > 128 || zBlocksFromSpawn < -128 || zBlocksFromSpawn > 128) { + this.chunksToUnload.add(ChunkCoordIntPair.chunkXZ2Int(cx, cz)); + } + } else { + this.chunksToUnload.add(ChunkCoordIntPair.chunkXZ2Int(cx, cz)); + } + } + + /** + * marks all chunks for unload, ignoring those near the spawn + */ + @Override + public void unloadAllChunks() { + for (int i = 0; i < loadedChunks.size(); ++i) { + final Chunk c = loadedChunks.get(i); + this.unloadChunksIfNotNearSpawn(c.xPosition, c.zPosition); + } + } + + /** + * Loads or generates the chunk at the chunk location specified. If generation happens, blocks until it's done. + */ + @Override + @Deprecated + public Chunk loadChunk(int cx, int cz) { + return loadChunk(cx, cz, null); + } + + /** + * Loads the chunk specified. If it doesn't exist on disk, it will be generated. The callback passed will be run + * on loading. Blocks until chunk is ready. This method runs on mServer, and is thus forbidden from decoration. + */ + @Override + public Chunk loadChunk(int cx, int cz, Runnable runnable) { + + long key = ChunkCoordIntPair.chunkXZ2Int(cx, cz); + this.chunksToUnload.remove(key); + + //while (this.loadingChunks.contains(key)) + // LockSupport.parkNanos(1000); + + Chunk chunk = (Chunk) this.loadedChunkHashMap.getValueByKey(key); + AnvilChunkLoader loader = (AnvilChunkLoader) this.currentChunkLoader; + + // If it's not already loaded... + if (chunk == null) { + // If it's already generated... + if (loader != null && loader.chunkExists(this.worldObj, cx, cz)) { + // If they have their own callback... + if (runnable != null) { + + // Queue the load and finish on worker and main threads; when the latter finishes, run the callback. + final CompletableFuture cf = new CompletableFuture<>(); + this.workers.execute(() -> cf.complete(this.loadChunkFromDisk(cx, cz))); + this.mChunk.execute(() -> { + try { + this.finishChunkFromDisk(cf.get(), cx, cz, key); + } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } + runnable.run(); + }); + return null; + } else { + + // Punt to the main worker + if (this.isMChunk.get()) + return this.loadChunkFromDisk(cx, cz, key); + + final CompletableFuture cf = new CompletableFuture<>(); + this.mChunk.execute(() -> cf.complete(this.loadChunkFromDisk(cx, cz, key))); + try { + chunk = cf.get(); + } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } + } + } else { + + // Punt to main generator + if (this.isMChunk.get()) + return this.generateChunk(cx, cz, key); + + final CompletableFuture cf = new CompletableFuture<>(); + this.mChunk.execute(() -> cf.complete(this.generateChunk(cx, cz, key))); + try { + chunk = cf.get(); + } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } + } + } + + // If we didn't load the chunk async and have a callback run it now + if (runnable != null) + runnable.run(); + + return chunk; + } + + /** + * Generate a chunk. Blocks until the chunk is done. + */ + @Override + public Chunk originalLoadChunk(int cx, int cz) { + long key = ChunkCoordIntPair.chunkXZ2Int(cx, cz); + this.chunksToUnload.remove(key); + Chunk chunk = (Chunk) this.loadedChunkHashMap.getValueByKey(key); + + if (chunk != null) + return chunk; + + if (!loadingChunks.add(key)) { + cpw.mods.fml.common.FMLLog.bigWarning("There is an attempt to load a chunk (%d,%d) in dimension %d that is already being loaded. This will cause weird chunk breakages.", cx, cz, worldObj.provider.dimensionId); + } + + chunk = ForgeChunkManager.fetchDormantChunk(key, this.worldObj); + if (chunk == null) { + chunk = this.safeLoadChunk(cx, cz); + } + + final boolean shouldGen = chunk == null; + final CompletableFuture cf = new CompletableFuture<>(); + final Chunk finalChunk = chunk; // java why do I have to do this, this isn't even a deep copy + this.mChunk.execute(() -> cf.complete( + shouldGen + ? this.generateChunk(cx, cz, key) + : this.decorateChunk(finalChunk, cx, cz, key))); + + try { + return cf.get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + /** + * Will return back a chunk, if it doesn't exist and its not a MP client it will generates all the blocks for the + * specified chunk from the map seed and chunk seed + */ + @Override + public Chunk provideChunk(int cx, int cz) { + Chunk chunk = (Chunk) this.loadedChunkHashMap.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(cx, cz)); + return chunk == null ? (!this.worldObj.findingSpawnPoint && !this.loadChunkOnProvideRequest ? this.empty : this.loadChunk(cx, cz)) : chunk; + } + + /** + * Attempts to load a chunk from the save files, if not found returns null. + */ + @Override + public Chunk safeLoadChunk(int cx, int cz) { + if (this.currentChunkLoader == null) { + return null; + } else { + final CompletableFuture cf = new CompletableFuture<>(); + this.mChunk.execute( + () -> cf.complete(this.loadChunkFromDisk(cx, cz, ChunkCoordIntPair.chunkXZ2Int(cx, cz)))); + + try { + return cf.get(); + } catch (Exception exception) { + log.error("Couldn't load chunk", exception); + return null; + } + } + } + + /** + * used by saveChunks, but catches any exceptions if the save fails. + */ + private void safeSaveExtraChunkData(Chunk chunk) { + if (this.currentChunkLoader != null) { + try { + this.currentChunkLoader.saveExtraChunkData(this.worldObj, chunk); + } catch (Exception exception) { + log.error("Couldn't save entities", exception); + } + } + } + + /** + * used by saveChunks, but catches any exceptions if the save fails. + */ + private void safeSaveChunk(Chunk chunk) { + if (this.currentChunkLoader != null) { + try { + chunk.lastSaveTime = this.worldObj.getTotalWorldTime(); + this.currentChunkLoader.saveChunk(this.worldObj, chunk); + } catch (IOException ioexception) { + log.error("Couldn't save chunk", ioexception); + } catch (MinecraftException minecraftexception) { + log.error("Couldn't save chunk; already in use by another instance of Minecraft?", minecraftexception); + } + } + } + + /** + * Populates chunk with ores etc etc + */ + @Override + public void populate(IChunkProvider backingCP, int cx, int cz) { + Chunk chunk = this.provideChunk(cx, cz); + + if (!chunk.isTerrainPopulated) { + chunk.func_150809_p(); + + if (this.currentChunkProvider != null) { + this.localProvider.get().populate(backingCP, cx, cz); + GameRegistry.generateWorld(cx, cz, worldObj, this.localProvider.get(), backingCP); + chunk.setChunkModified(); + } + } + } + + /** + * Two modes of operation: if passed true, save all Chunks in one go. If passed false, save up to two chunks. + * Return true if all chunks have been saved. + */ + @Override + public boolean saveChunks(boolean oneshot, IProgressUpdate p_73151_2_) { + int i = 0; + ObjectArrayList copiedChunks = new ObjectArrayList<>(this.loadedChunks); + + for (int j = 0; j < copiedChunks.size(); ++j) { + Chunk chunk = copiedChunks.get(j); + + if (oneshot) { + this.safeSaveExtraChunkData(chunk); + } + + if (chunk.needsSaving(oneshot)) { + this.safeSaveChunk(chunk); + chunk.isModified = false; + ++i; + + if (i == 24 && !oneshot) { + return false; + } + } + } + + return true; + } + + /** + * Save extra data not associated with any Chunk. Not saved during autosave, only during world unload. Currently + * unimplemented. + */ + @Override + public void saveExtraData() { + if (this.currentChunkLoader != null) { + this.currentChunkLoader.saveExtraData(); + } + } + + /** + * Unloads chunks that are marked to be unloaded. This is not guaranteed to unload every such chunk. + */ + @Override + public boolean unloadQueuedChunks() { + if (!this.worldObj.levelSaving) { + for (ChunkCoordIntPair forced : this.worldObj.getPersistentChunks().keySet()) { + this.chunksToUnload.remove(ChunkCoordIntPair.chunkXZ2Int(forced.chunkXPos, forced.chunkZPos)); + } + + for (int i = 0; i < maxUnloadsPerTick; ++i) { + if (!this.chunksToUnload.isEmpty()) { + long key = this.chunksToUnload.iterator().nextLong(); + Chunk chunk = (Chunk) this.loadedChunkHashMap.getValueByKey(key); + + if (chunk != null) { + chunk.onChunkUnload(); + this.safeSaveChunk(chunk); + this.safeSaveExtraChunkData(chunk); + this.loadedChunks.remove(chunk); + ForgeChunkManager.putDormantChunk(ChunkCoordIntPair.chunkXZ2Int(chunk.xPosition, chunk.zPosition), chunk); + if (loadedChunks.isEmpty() && ForgeChunkManager.getPersistentChunksFor(this.worldObj).isEmpty() && !DimensionManager.shouldLoadSpawn(this.worldObj.provider.dimensionId)){ + DimensionManager.unloadWorld(this.worldObj.provider.dimensionId); + return currentChunkProvider.unloadQueuedChunks(); + } + } + + this.chunksToUnload.remove(key); + this.loadedChunkHashMap.remove(key); + } + } + + if (this.currentChunkLoader != null) { + this.currentChunkLoader.chunkTick(); + } + } + + // This will always return a boolean and do no work on the server, as far as I can tell, so it doesn't matter if + // it's thread-local or not + return this.currentChunkProvider.unloadQueuedChunks(); + } + + /** + * Returns if the IChunkProvider supports saving. + */ + @Override + public boolean canSave() { + return !this.worldObj.levelSaving; + } + + /** + * Converts the instance data to a readable string. + */ + @Override + public String makeString() { + return "ServerChunkCache: " + this.loadedChunkHashMap.getNumHashElements() + " Drop: " + this.chunksToUnload.size(); + } + + /** + * Returns a list of creatures of the specified type that can spawn at the given location. + */ + @Override + public List getPossibleCreatures(EnumCreatureType type, int x, int y, int z) { + return this.localProvider.get().getPossibleCreatures(type, x, y, z); + } + + @Override + public ChunkPosition func_147416_a(World world, String p_147416_2_, int x, int y, int z) { + return this.localProvider.get().func_147416_a(world, p_147416_2_, x, y, z); + } + + @Override + public int getLoadedChunkCount() { + return this.loadedChunkHashMap.getNumHashElements(); + } + +} diff --git a/src/main/java/com/mitchej123/hodgepodge/server/NewIntCache.java b/src/main/java/com/mitchej123/hodgepodge/server/NewIntCache.java new file mode 100644 index 00000000..ade21204 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/server/NewIntCache.java @@ -0,0 +1,32 @@ +package com.mitchej123.hodgepodge.server; + +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +import java.util.List; +import java.util.Map; + +public class NewIntCache { + + 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/resources/META-INF/hodgepodge_at.cfg b/src/main/resources/META-INF/hodgepodge_at.cfg index 26d23123..81bd7193 100644 --- a/src/main/resources/META-INF/hodgepodge_at.cfg +++ b/src/main/resources/META-INF/hodgepodge_at.cfg @@ -19,3 +19,4 @@ public net.minecraft.client.resources.AbstractResourcePack field_110597_b # reso public net.minecraft.client.resources.FallbackResourceManager field_110540_a # resourcePacks public net.minecraft.client.resources.FileResourcePack field_110600_d # resourcePackZipFile public net.minecraft.client.resources.SimpleReloadableResourceManager field_110548_a # domainResourceManagers +public net.minecraft.world.gen.ChunkProviderServer func_73239_e(II)Lnet/minecraft/world/chunk/Chunk; # safeLoadChunk From b305533a15090a591f828bf33bc49e8cf5e32770 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Fri, 15 Mar 2024 05:39:59 -0400 Subject: [PATCH 02/18] Spotless --- .../mitchej123/hodgepodge/mixins/Mixins.java | 3 +- .../minecraft/fastload/MixinIntCache.java | 6 +- .../fastload/MixinWorldChunkManager.java | 129 ++++++++--------- .../minecraft/fastload/MixinWorldServer.java | 51 ++++--- .../hodgepodge/server/ChunkAndNbt.java | 9 +- .../mitchej123/hodgepodge/server/FastCPS.java | 133 ++++++++++-------- .../hodgepodge/server/NewIntCache.java | 6 +- 7 files changed, 188 insertions(+), 149 deletions(-) diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java index 00c09b71..5806b458 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java @@ -377,7 +377,8 @@ public enum Mixins { .setApplyIf(() -> TweaksConfig.useLighterWater)), FAST_CHUNK_LOADING(new Builder("Lightly threads chunk generation and loading").setPhase(Phase.EARLY) - .setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA).addMixinClasses( + .setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA) + .addMixinClasses( "minecraft.fastload.MixinIntCache", "minecraft.fastload.MixinWorldChunkManager", "minecraft.fastload.MixinWorldServer") 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 index c4f0e426..640f863c 100644 --- 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 @@ -1,17 +1,19 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import com.mitchej123.hodgepodge.server.NewIntCache; import net.minecraft.world.gen.layer.IntCache; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; +import com.mitchej123.hodgepodge.server.NewIntCache; + @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. + * of explicitly releasing them. */ @Overwrite public static synchronized int[] getIntCache(int 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 index ce4f894f..cb2c7cea 100644 --- 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 @@ -1,7 +1,8 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import com.llamalad7.mixinextras.sugar.Local; -import com.mitchej123.hodgepodge.server.NewIntCache; +import java.util.List; +import java.util.Random; + import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import net.minecraft.util.ReportedException; @@ -9,6 +10,7 @@ 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; @@ -16,8 +18,8 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import java.util.List; -import java.util.Random; +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.server.NewIntCache; @Mixin(WorldChunkManager.class) public class MixinWorldChunkManager { @@ -25,28 +27,26 @@ 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")) + @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) { + @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; + float f = (float) BiomeGenBase.getBiome(ints[i]).getIntRainfall() / 65536.0F; if (f > 1.0F) { f = 1.0F; @@ -70,15 +70,15 @@ public class MixinWorldChunkManager { 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) { + @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) { @@ -100,19 +100,15 @@ public class MixinWorldChunkManager { } @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) { + 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]); @@ -122,16 +118,16 @@ public class MixinWorldChunkManager { 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) { + @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) { @@ -159,19 +155,17 @@ public class MixinWorldChunkManager { } } - @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) { + @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; @@ -181,8 +175,7 @@ public class MixinWorldChunkManager { 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)) - { + if (p_150795_4_.contains(biomegenbase) && (chunkposition == null || p_150795_5_.nextInt(j2 + 1) == 0)) { chunkposition = new ChunkPosition(l2, 0, i3); ++j2; } diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java index 8b0f6d3d..d268cd52 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java @@ -1,11 +1,8 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; -import com.mitchej123.hodgepodge.server.FastCPS; -import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + import net.minecraft.profiler.Profiler; import net.minecraft.world.*; import net.minecraft.world.chunk.Chunk; @@ -14,6 +11,7 @@ import net.minecraft.world.chunk.storage.IChunkLoader; import net.minecraft.world.gen.ChunkProviderServer; import net.minecraft.world.storage.ISaveHandler; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -22,8 +20,13 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.server.FastCPS; + +import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @Mixin(WorldServer.class) public abstract class MixinWorldServer extends World { @@ -35,12 +38,19 @@ public abstract class MixinWorldServer extends World { @Unique private final Object2ObjectOpenHashMap hodgepodge$chunks = new Object2ObjectOpenHashMap<>(); - @WrapOperation(method = "createChunkProvider", at = @At(value = "NEW", target = "(Lnet/minecraft/world/WorldServer;Lnet/minecraft/world/chunk/storage/IChunkLoader;Lnet/minecraft/world/chunk/IChunkProvider;)Lnet/minecraft/world/gen/ChunkProviderServer;")) - private ChunkProviderServer hodgepodge$replaceChunkProvider(WorldServer server, IChunkLoader loader, IChunkProvider backingCP, Operation original) { + @WrapOperation( + method = "createChunkProvider", + at = @At( + value = "NEW", + target = "(Lnet/minecraft/world/WorldServer;Lnet/minecraft/world/chunk/storage/IChunkLoader;Lnet/minecraft/world/chunk/IChunkProvider;)Lnet/minecraft/world/gen/ChunkProviderServer;")) + private ChunkProviderServer hodgepodge$replaceChunkProvider(WorldServer server, IChunkLoader loader, + IChunkProvider backingCP, Operation original) { return new FastCPS(server, (AnvilChunkLoader) loader, backingCP); } - @Inject(method = "func_147456_g", at = @At(value = "INVOKE", target = "Ljava/util/Set;iterator()Ljava/util/Iterator;")) + @Inject( + method = "func_147456_g", + at = @At(value = "INVOKE", target = "Ljava/util/Set;iterator()Ljava/util/Iterator;")) private void hodgepodge$threadChunkGen(CallbackInfo ci) { this.hodgepodge$chunksToLoad.clear(); @@ -59,7 +69,8 @@ public abstract class MixinWorldServer extends World { cf.complete((Chunk) cps.loadedChunkHashMap.getValueByKey(key)); } else if (acl.chunkExists(this, c.chunkXPos, c.chunkZPos)) { - // The chunk exists on disk, but needs to be loaded. Trivially threaded, forge already has functions for that. + // The chunk exists on disk, but needs to be loaded. Trivially threaded, forge already has functions for + // that. ((FastCPS) this.theChunkProviderServer).queueDiskLoad(c.chunkXPos, c.chunkZPos, key, cf); } else { @@ -75,18 +86,26 @@ public abstract class MixinWorldServer extends World { Object2ObjectMaps.fastForEach(this.hodgepodge$chunksToLoad, e -> { try { this.hodgepodge$chunks.put(e.getKey(), e.getValue().get()); - } catch (InterruptedException | ExecutionException ex) { throw new RuntimeException(ex); } + } catch (InterruptedException | ExecutionException ex) { + throw new RuntimeException(ex); + } }); } - @Redirect(method = "func_147456_g", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/WorldServer;getChunkFromChunkCoords(II)Lnet/minecraft/world/chunk/Chunk;")) - private Chunk hodgepodge$threadChunkGen(WorldServer instance, int cx, int cz, @Local(ordinal = 0) ChunkCoordIntPair c) { + @Redirect( + method = "func_147456_g", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/world/WorldServer;getChunkFromChunkCoords(II)Lnet/minecraft/world/chunk/Chunk;")) + private Chunk hodgepodge$threadChunkGen(WorldServer instance, int cx, int cz, + @Local(ordinal = 0) ChunkCoordIntPair c) { final Chunk ch = this.hodgepodge$chunks.get(c); return ch != null ? ch : this.chunkProvider.provideChunk(cx, cz); } - public MixinWorldServer(ISaveHandler p_i45368_1_, String p_i45368_2_, WorldProvider p_i45368_3_, WorldSettings p_i45368_4_, Profiler p_i45368_5_) { + public MixinWorldServer(ISaveHandler p_i45368_1_, String p_i45368_2_, WorldProvider p_i45368_3_, + WorldSettings p_i45368_4_, Profiler p_i45368_5_) { super(p_i45368_1_, p_i45368_2_, p_i45368_3_, p_i45368_4_, p_i45368_5_); } } diff --git a/src/main/java/com/mitchej123/hodgepodge/server/ChunkAndNbt.java b/src/main/java/com/mitchej123/hodgepodge/server/ChunkAndNbt.java index 787cc7dd..3b76d236 100644 --- a/src/main/java/com/mitchej123/hodgepodge/server/ChunkAndNbt.java +++ b/src/main/java/com/mitchej123/hodgepodge/server/ChunkAndNbt.java @@ -4,6 +4,7 @@ import net.minecraft.world.chunk.Chunk; public class ChunkAndNbt { + private final Chunk chunk; private final NBTTagCompound nbt; @@ -12,7 +13,11 @@ public ChunkAndNbt(Chunk chunk, NBTTagCompound nbt) { this.nbt = nbt; } - public Chunk getChunk() { return this.chunk; } + public Chunk getChunk() { + return this.chunk; + } - public NBTTagCompound getNbt() { return this.nbt; } + public NBTTagCompound getNbt() { + return this.nbt; + } } diff --git a/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java index 12f7e921..34119130 100644 --- a/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java +++ b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java @@ -1,9 +1,14 @@ package com.mitchej123.hodgepodge.server; -import com.mitchej123.hodgepodge.Common; -import cpw.mods.fml.common.registry.GameRegistry; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import static com.mitchej123.hodgepodge.Common.log; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + import net.minecraft.block.Block; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; @@ -28,14 +33,9 @@ import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.ChunkDataEvent; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import static com.mitchej123.hodgepodge.Common.log; +import cpw.mods.fml.common.registry.GameRegistry; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; public class FastCPS extends ChunkProviderServer { @@ -54,8 +54,8 @@ public class FastCPS extends ChunkProviderServer { private final int maxWorkers = 6; private final ExecutorService workers = Executors.newFixedThreadPool(maxWorkers); // A thread-local copy of the backing world generator - private final ThreadLocal localProvider = - ThreadLocal.withInitial(() -> this.worldObj.provider.createChunkGenerator()); + private final ThreadLocal localProvider = ThreadLocal + .withInitial(() -> this.worldObj.provider.createChunkGenerator()); public FastCPS(WorldServer worldObj, AnvilChunkLoader loader, IChunkProvider backingCP) { super(worldObj, loader, backingCP); @@ -90,7 +90,9 @@ public Chunk generateChunk(int cx, int cz, long key) { try { return this.decorateChunk(cf.get(), cx, cz, key); - } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } } /** @@ -118,8 +120,8 @@ public Chunk generateUndecoratedChunk(int cx, int cz, long key) { } /** - * Attempt to decorate a generated chunk, meant to run on only one thread at a time (but that doesn't have to be - * the main thread). Does no safety checks, be careful! + * Attempt to decorate a generated chunk, meant to run on only one thread at a time (but that doesn't have to be the + * main thread). Does no safety checks, be careful! */ public Chunk decorateChunk(Chunk chunk, int cx, int cz, long key) { @@ -165,8 +167,10 @@ public ChunkAndNbt loadChunkFromDisk(int cx, int cz) { chunk.addEntity(entity); Entity riderEntity = entity; - for (NBTTagCompound tmpEntityTag = entityTag; tmpEntityTag.hasKey("Riding", 10); tmpEntityTag = tmpEntityTag.getCompoundTag("Riding")) { - final Entity riddenEntity = EntityList.createEntityFromNBT(tmpEntityTag.getCompoundTag("Riding"), this.worldObj); + for (NBTTagCompound tmpEntityTag = entityTag; tmpEntityTag + .hasKey("Riding", 10); tmpEntityTag = tmpEntityTag.getCompoundTag("Riding")) { + final Entity riddenEntity = EntityList + .createEntityFromNBT(tmpEntityTag.getCompoundTag("Riding"), this.worldObj); if (riddenEntity != null) { @@ -205,6 +209,7 @@ public ChunkAndNbt loadChunkFromDisk(int cx, int cz) { * Loading a chunk from disk requires some synchronous action, do it here. Despite taking a * {@link CompletableFuture}, this is NOT meant to be run on multiple threads at a time - but it doesn't have to be * run on the main thread. + * * @param cnbt If this is null, blocks and generates the chunk instead */ public Chunk finishChunkFromDisk(ChunkAndNbt cnbt, int cx, int cz, long key) { @@ -224,12 +229,12 @@ public Chunk finishChunkFromDisk(ChunkAndNbt cnbt, int cx, int cz, long key) { for (int j1 = 0; j1 < tileTicks.tagCount(); ++j1) { final NBTTagCompound tickTag = tileTicks.getCompoundTagAt(j1); this.worldObj.func_147446_b( - tickTag.getInteger("x"), - tickTag.getInteger("y"), - tickTag.getInteger("z"), - Block.getBlockById(tickTag.getInteger("i")), - tickTag.getInteger("t"), - tickTag.getInteger("p")); + tickTag.getInteger("x"), + tickTag.getInteger("y"), + tickTag.getInteger("z"), + Block.getBlockById(tickTag.getInteger("i")), + tickTag.getInteger("t"), + tickTag.getInteger("p")); } } } @@ -257,17 +262,20 @@ public List func_152380_a() { } /** - * marks chunk for unload by "unload100OldestChunks" if there is no spawn point, or if the center of the chunk is + * marks chunk for unload by "unload100OldestChunks" if there is no spawn point, or if the center of the chunk is * outside 200 blocks (x or z) of the spawn */ @Override public void unloadChunksIfNotNearSpawn(int cx, int cz) { - if (this.worldObj.provider.canRespawnHere() && DimensionManager.shouldLoadSpawn(this.worldObj.provider.dimensionId)) { + if (this.worldObj.provider.canRespawnHere() + && DimensionManager.shouldLoadSpawn(this.worldObj.provider.dimensionId)) { final ChunkCoordinates chunkcoordinates = this.worldObj.getSpawnPoint(); int xBlocksFromSpawn = cx * 16 + 8 - chunkcoordinates.posX; int zBlocksFromSpawn = cz * 16 + 8 - chunkcoordinates.posZ; - if (xBlocksFromSpawn < -128 || xBlocksFromSpawn > 128 || zBlocksFromSpawn < -128 || zBlocksFromSpawn > 128) { + if (xBlocksFromSpawn < -128 || xBlocksFromSpawn > 128 + || zBlocksFromSpawn < -128 + || zBlocksFromSpawn > 128) { this.chunksToUnload.add(ChunkCoordIntPair.chunkXZ2Int(cx, cz)); } } else { @@ -296,8 +304,8 @@ public Chunk loadChunk(int cx, int cz) { } /** - * Loads the chunk specified. If it doesn't exist on disk, it will be generated. The callback passed will be run - * on loading. Blocks until chunk is ready. This method runs on mServer, and is thus forbidden from decoration. + * Loads the chunk specified. If it doesn't exist on disk, it will be generated. The callback passed will be run on + * loading. Blocks until chunk is ready. This method runs on mServer, and is thus forbidden from decoration. */ @Override public Chunk loadChunk(int cx, int cz, Runnable runnable) { @@ -305,8 +313,8 @@ public Chunk loadChunk(int cx, int cz, Runnable runnable) { long key = ChunkCoordIntPair.chunkXZ2Int(cx, cz); this.chunksToUnload.remove(key); - //while (this.loadingChunks.contains(key)) - // LockSupport.parkNanos(1000); + // while (this.loadingChunks.contains(key)) + // LockSupport.parkNanos(1000); Chunk chunk = (Chunk) this.loadedChunkHashMap.getValueByKey(key); AnvilChunkLoader loader = (AnvilChunkLoader) this.currentChunkLoader; @@ -324,39 +332,42 @@ public Chunk loadChunk(int cx, int cz, Runnable runnable) { this.mChunk.execute(() -> { try { this.finishChunkFromDisk(cf.get(), cx, cz, key); - } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } runnable.run(); }); return null; } else { // Punt to the main worker - if (this.isMChunk.get()) - return this.loadChunkFromDisk(cx, cz, key); + if (this.isMChunk.get()) return this.loadChunkFromDisk(cx, cz, key); final CompletableFuture cf = new CompletableFuture<>(); this.mChunk.execute(() -> cf.complete(this.loadChunkFromDisk(cx, cz, key))); try { chunk = cf.get(); - } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } } } else { // Punt to main generator - if (this.isMChunk.get()) - return this.generateChunk(cx, cz, key); + if (this.isMChunk.get()) return this.generateChunk(cx, cz, key); final CompletableFuture cf = new CompletableFuture<>(); this.mChunk.execute(() -> cf.complete(this.generateChunk(cx, cz, key))); try { chunk = cf.get(); - } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } } } // If we didn't load the chunk async and have a callback run it now - if (runnable != null) - runnable.run(); + if (runnable != null) runnable.run(); return chunk; } @@ -370,11 +381,14 @@ public Chunk originalLoadChunk(int cx, int cz) { this.chunksToUnload.remove(key); Chunk chunk = (Chunk) this.loadedChunkHashMap.getValueByKey(key); - if (chunk != null) - return chunk; + if (chunk != null) return chunk; if (!loadingChunks.add(key)) { - cpw.mods.fml.common.FMLLog.bigWarning("There is an attempt to load a chunk (%d,%d) in dimension %d that is already being loaded. This will cause weird chunk breakages.", cx, cz, worldObj.provider.dimensionId); + cpw.mods.fml.common.FMLLog.bigWarning( + "There is an attempt to load a chunk (%d,%d) in dimension %d that is already being loaded. This will cause weird chunk breakages.", + cx, + cz, + worldObj.provider.dimensionId); } chunk = ForgeChunkManager.fetchDormantChunk(key, this.worldObj); @@ -385,10 +399,9 @@ public Chunk originalLoadChunk(int cx, int cz) { final boolean shouldGen = chunk == null; final CompletableFuture cf = new CompletableFuture<>(); final Chunk finalChunk = chunk; // java why do I have to do this, this isn't even a deep copy - this.mChunk.execute(() -> cf.complete( - shouldGen - ? this.generateChunk(cx, cz, key) - : this.decorateChunk(finalChunk, cx, cz, key))); + this.mChunk.execute( + () -> cf.complete( + shouldGen ? this.generateChunk(cx, cz, key) : this.decorateChunk(finalChunk, cx, cz, key))); try { return cf.get(); @@ -404,7 +417,8 @@ public Chunk originalLoadChunk(int cx, int cz) { @Override public Chunk provideChunk(int cx, int cz) { Chunk chunk = (Chunk) this.loadedChunkHashMap.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(cx, cz)); - return chunk == null ? (!this.worldObj.findingSpawnPoint && !this.loadChunkOnProvideRequest ? this.empty : this.loadChunk(cx, cz)) : chunk; + return chunk == null ? (!this.worldObj.findingSpawnPoint && !this.loadChunkOnProvideRequest ? this.empty + : this.loadChunk(cx, cz)) : chunk; } /** @@ -416,8 +430,8 @@ public Chunk safeLoadChunk(int cx, int cz) { return null; } else { final CompletableFuture cf = new CompletableFuture<>(); - this.mChunk.execute( - () -> cf.complete(this.loadChunkFromDisk(cx, cz, ChunkCoordIntPair.chunkXZ2Int(cx, cz)))); + this.mChunk + .execute(() -> cf.complete(this.loadChunkFromDisk(cx, cz, ChunkCoordIntPair.chunkXZ2Int(cx, cz)))); try { return cf.get(); @@ -476,8 +490,8 @@ public void populate(IChunkProvider backingCP, int cx, int cz) { } /** - * Two modes of operation: if passed true, save all Chunks in one go. If passed false, save up to two chunks. - * Return true if all chunks have been saved. + * Two modes of operation: if passed true, save all Chunks in one go. If passed false, save up to two chunks. Return + * true if all chunks have been saved. */ @Override public boolean saveChunks(boolean oneshot, IProgressUpdate p_73151_2_) { @@ -506,7 +520,7 @@ public boolean saveChunks(boolean oneshot, IProgressUpdate p_73151_2_) { } /** - * Save extra data not associated with any Chunk. Not saved during autosave, only during world unload. Currently + * Save extra data not associated with any Chunk. Not saved during autosave, only during world unload. Currently * unimplemented. */ @Override @@ -536,8 +550,11 @@ public boolean unloadQueuedChunks() { this.safeSaveChunk(chunk); this.safeSaveExtraChunkData(chunk); this.loadedChunks.remove(chunk); - ForgeChunkManager.putDormantChunk(ChunkCoordIntPair.chunkXZ2Int(chunk.xPosition, chunk.zPosition), chunk); - if (loadedChunks.isEmpty() && ForgeChunkManager.getPersistentChunksFor(this.worldObj).isEmpty() && !DimensionManager.shouldLoadSpawn(this.worldObj.provider.dimensionId)){ + ForgeChunkManager.putDormantChunk( + ChunkCoordIntPair.chunkXZ2Int(chunk.xPosition, chunk.zPosition), + chunk); + if (loadedChunks.isEmpty() && ForgeChunkManager.getPersistentChunksFor(this.worldObj).isEmpty() + && !DimensionManager.shouldLoadSpawn(this.worldObj.provider.dimensionId)) { DimensionManager.unloadWorld(this.worldObj.provider.dimensionId); return currentChunkProvider.unloadQueuedChunks(); } @@ -571,7 +588,9 @@ public boolean canSave() { */ @Override public String makeString() { - return "ServerChunkCache: " + this.loadedChunkHashMap.getNumHashElements() + " Drop: " + this.chunksToUnload.size(); + return "ServerChunkCache: " + this.loadedChunkHashMap.getNumHashElements() + + " Drop: " + + this.chunksToUnload.size(); } /** diff --git a/src/main/java/com/mitchej123/hodgepodge/server/NewIntCache.java b/src/main/java/com/mitchej123/hodgepodge/server/NewIntCache.java index ade21204..1a80635a 100644 --- a/src/main/java/com/mitchej123/hodgepodge/server/NewIntCache.java +++ b/src/main/java/com/mitchej123/hodgepodge/server/NewIntCache.java @@ -1,11 +1,11 @@ package com.mitchej123.hodgepodge.server; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; - import java.util.List; import java.util.Map; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + public class NewIntCache { private static final int SMALLEST = 256; From b7aa2dc3e45d4777ca57798cbeea153bfb36fe83 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Fri, 15 Mar 2024 22:16:09 -0400 Subject: [PATCH 03/18] Sending more chunks This breaks... why? --- .../mitchej123/hodgepodge/mixins/Mixins.java | 3 +- .../fastload/MixinEntityPlayerMP.java | 110 ++++++++++++++++++ .../minecraft/fastload/MixinWorldServer.java | 73 +++++++++--- .../mitchej123/hodgepodge/server/FastCPS.java | 11 +- 4 files changed, 173 insertions(+), 24 deletions(-) create mode 100644 src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinEntityPlayerMP.java diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java index 5806b458..a351ccdb 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java @@ -381,7 +381,8 @@ public enum Mixins { .addMixinClasses( "minecraft.fastload.MixinIntCache", "minecraft.fastload.MixinWorldChunkManager", - "minecraft.fastload.MixinWorldServer") + "minecraft.fastload.MixinWorldServer", + "minecraft.fastload.MixinEntityPlayerMP") .setApplyIf(() -> SpeedupsConfig.fastChunkHandling)), // Ic2 adjustments 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..fe6e51ba --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinEntityPlayerMP.java @@ -0,0 +1,110 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import com.mojang.authlib.GameProfile; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectImmutableList; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.ICrafting; +import net.minecraft.network.NetHandlerPlayServer; +import net.minecraft.network.Packet; +import net.minecraft.network.play.server.S26PacketMapChunkBulk; +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.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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.ArrayList; +import java.util.List; + +import static com.mitchej123.hodgepodge.Common.log; + +@Mixin(EntityPlayerMP.class) +public abstract class MixinEntityPlayerMP extends EntityPlayer implements ICrafting { + + @Unique + private final List> hodgepodge$chunkSends = new ObjectArrayList<>(); + @Unique + private int hodgepodge$totalChunks = 0; + @Unique + private static final S26PacketMapChunkBulk hodgepodge$dummyPacket = new S26PacketMapChunkBulk(); + + @Shadow + public abstract WorldServer getServerForPlayer(); + + public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { + super(p_i45324_1_, p_i45324_2_); + } + + @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Ljava/util/ArrayList;size()I", ordinal = 0)) + private int hodgepodge$numChunks(ArrayList chunks) { + return this.hodgepodge$totalChunks; + } + + @Inject(method = "onUpdate", at = @At(value = "NEW", target = "()Ljava/util/ArrayList;")) + private void hodgepodge$setNumChunks(CallbackInfo ci) { + this.hodgepodge$totalChunks = 0; + this.hodgepodge$chunkSends.clear(); + } + + @Inject(method = "onUpdate", at = @At(value = "INVOKE", target = "Ljava/util/ArrayList;add(Ljava/lang/Object;)Z")) + private void hodgepodge$incNumChunks(CallbackInfo ci) { + ++this.hodgepodge$totalChunks; + } + + @ModifyExpressionValue(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;func_149258_c()I")) + private int hodgepodge$maxChunks(int value) { + return value * 5; + } + + @Inject(method = "onUpdate", at = @At(value = "INVOKE", target = "Ljava/util/Iterator;next()Ljava/lang/Object;", ordinal = 1)) + private void hodgepodge$fasterChunkSend(CallbackInfo ci, @Local(name = "arraylist") ArrayList chunks) { + if (chunks.size() == S26PacketMapChunkBulk.func_149258_c()) { + this.hodgepodge$chunkSends.add(new ObjectImmutableList<>(chunks)); + chunks.clear(); + } + } + + @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Ljava/util/ArrayList;isEmpty()Z")) + private boolean hodgepodge$areChunksEmpty(ArrayList chunks) { + return this.hodgepodge$chunkSends.isEmpty(); + } + + @Redirect(method = "onUpdate", at = @At(value = "NEW", target = "(Ljava/util/List;)Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;")) + private S26PacketMapChunkBulk hodgepodge$sendChunks(List extracted) { + return hodgepodge$dummyPacket; + } + + @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetHandlerPlayServer;sendPacket(Lnet/minecraft/network/Packet;)V")) + private void hodgepodge$sendChunks(NetHandlerPlayServer instance, Packet enumchatvisibility) { + for (int i = 0; i < hodgepodge$chunkSends.size(); ++i) { + instance.sendPacket(new S26PacketMapChunkBulk(this.hodgepodge$chunkSends.get(i))); + } + } + + @Inject(method = "onUpdate", at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/ArrayList;iterator()Ljava/util/Iterator;", ordinal = 1), cancellable = true) + private void hodgepodge$fasterChunkSend(CallbackInfo ci) { + for (int i = 0; i < this.hodgepodge$chunkSends.size(); ++i) { + final ObjectImmutableList chunks = this.hodgepodge$chunkSends.get(i); + for (int j = 0; j < chunks.size(); ++j) { + final Chunk chunk = chunks.get(j); + this.getServerForPlayer().getEntityTracker().func_85172_a(((EntityPlayerMP) (Object) this), chunk); + MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.Watch(chunk.getChunkCoordIntPair(), ((EntityPlayerMP) (Object) this))); + } + } + + if (this.hodgepodge$totalChunks > S26PacketMapChunkBulk.func_149258_c()) + log.info("Sent {} chunks to the client", this.hodgepodge$totalChunks); + ci.cancel(); + } +} diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java index d268cd52..a6501f8d 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java @@ -1,8 +1,15 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; +import java.util.Iterator; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import com.mitchej123.hodgepodge.hax.LongChunkCoordIntPairSet; +import com.mitchej123.hodgepodge.util.ChunkPosUtil; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongIterator; import net.minecraft.profiler.Profiler; import net.minecraft.world.*; import net.minecraft.world.chunk.Chunk; @@ -22,11 +29,9 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; import com.mitchej123.hodgepodge.server.FastCPS; -import it.unimi.dsi.fastutil.objects.Object2ObjectMaps; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import static com.mitchej123.hodgepodge.Common.log; @Mixin(WorldServer.class) public abstract class MixinWorldServer extends World { @@ -34,9 +39,17 @@ public abstract class MixinWorldServer extends World { @Shadow public ChunkProviderServer theChunkProviderServer; @Unique - private final Object2ObjectOpenHashMap> hodgepodge$chunksToLoad = new Object2ObjectOpenHashMap<>(); + private final Long2ObjectOpenHashMap> hodgepodge$chunksToLoad = new Long2ObjectOpenHashMap<>(); @Unique - private final Object2ObjectOpenHashMap hodgepodge$chunks = new Object2ObjectOpenHashMap<>(); + private final Long2ObjectOpenHashMap hodgepodge$chunks = new Long2ObjectOpenHashMap<>(); + @Unique + private int hodgepodge$numNewGen = 0; + @Unique + private int hodgepodge$maxNewGen = 180; + @Unique + private LongChunkCoordIntPairSet hodgepodge$activeChunks2 = new LongChunkCoordIntPairSet(); + @Unique + private LongChunkCoordIntPairSet hodgepodge$activeChunks3; @WrapOperation( method = "createChunkProvider", @@ -54,42 +67,65 @@ public abstract class MixinWorldServer extends World { private void hodgepodge$threadChunkGen(CallbackInfo ci) { this.hodgepodge$chunksToLoad.clear(); + this.hodgepodge$numNewGen = 0; + this.hodgepodge$activeChunks2.clear(); // Queue chunks on worker threads or main final ChunkProviderServer cps = this.theChunkProviderServer; final AnvilChunkLoader acl = (AnvilChunkLoader) cps.currentChunkLoader; - for (ChunkCoordIntPair c : this.activeChunkSet) { + for (LongIterator i = ((LongChunkCoordIntPairSet) this.activeChunkSet).longIterator(); i.hasNext();) { + final long key = i.nextLong(); + final int cx = ChunkPosUtil.getPackedX(key); + final int cz = ChunkPosUtil.getPackedZ(key); - final long key = ChunkCoordIntPair.chunkXZ2Int(c.chunkXPos, c.chunkZPos); - final CompletableFuture cf = new CompletableFuture<>(); // If already loaded, just return it if (cps.loadedChunkHashMap.containsItem(key)) { + final CompletableFuture cf = new CompletableFuture<>(); cf.complete((Chunk) cps.loadedChunkHashMap.getValueByKey(key)); - } else if (acl.chunkExists(this, c.chunkXPos, c.chunkZPos)) { + this.hodgepodge$activeChunks2.addLong(key); + this.hodgepodge$chunksToLoad.put(key, cf); + } else if (acl.chunkExists(this, cx, cz)) { // The chunk exists on disk, but needs to be loaded. Trivially threaded, forge already has functions for // that. - ((FastCPS) this.theChunkProviderServer).queueDiskLoad(c.chunkXPos, c.chunkZPos, key, cf); + final CompletableFuture cf = new CompletableFuture<>(); + ((FastCPS) this.theChunkProviderServer).queueDiskLoad(cx, cz, key, cf); + this.hodgepodge$activeChunks2.addLong(key); + this.hodgepodge$chunksToLoad.put(key, cf); } else { - // These chunks need to be generated; let's try that - ((FastCPS) this.theChunkProviderServer).queueGenerate(c.chunkXPos, c.chunkZPos, key, cf); + // Throttle new generation + if (this.hodgepodge$numNewGen < this.hodgepodge$maxNewGen) { + // These chunks need to be generated; let's try that + final CompletableFuture cf = new CompletableFuture<>(); + ((FastCPS) this.theChunkProviderServer).queueGenerate(cx, cz, key, cf); + this.hodgepodge$activeChunks2.addLong(key); + this.hodgepodge$chunksToLoad.put(key, cf); + ++this.hodgepodge$numNewGen; + } } - - this.hodgepodge$chunksToLoad.put(c, cf); } + // This little shuffle is needed to swap two variables + this.hodgepodge$activeChunks3 = (LongChunkCoordIntPairSet) this.activeChunkSet; + this.activeChunkSet = this.hodgepodge$activeChunks2; + this.hodgepodge$activeChunks2 = this.hodgepodge$activeChunks3; + // All mChunk updates need to finish before the tick ends, to avoid CMEs... at least for now. // Guarantee that no chunks are still processing before moving on - Object2ObjectMaps.fastForEach(this.hodgepodge$chunksToLoad, e -> { + Long2ObjectMaps.fastForEach(this.hodgepodge$chunksToLoad, e -> { try { - this.hodgepodge$chunks.put(e.getKey(), e.getValue().get()); + this.hodgepodge$chunks.put(e.getLongKey(), e.getValue().get()); } catch (InterruptedException | ExecutionException ex) { throw new RuntimeException(ex); } }); + + if (this.hodgepodge$numNewGen != 0) { + log.info("Generated {} new chunks.", this.hodgepodge$numNewGen); + } } @Redirect( @@ -97,10 +133,9 @@ public abstract class MixinWorldServer extends World { at = @At( value = "INVOKE", target = "Lnet/minecraft/world/WorldServer;getChunkFromChunkCoords(II)Lnet/minecraft/world/chunk/Chunk;")) - private Chunk hodgepodge$threadChunkGen(WorldServer instance, int cx, int cz, - @Local(ordinal = 0) ChunkCoordIntPair c) { + private Chunk hodgepodge$threadChunkGen(WorldServer instance, int cx, int cz) { - final Chunk ch = this.hodgepodge$chunks.get(c); + final Chunk ch = this.hodgepodge$chunks.get(ChunkCoordIntPair.chunkXZ2Int(cx, cz)); return ch != null ? ch : this.chunkProvider.provideChunk(cx, cz); } diff --git a/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java index 34119130..b73a3c34 100644 --- a/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java +++ b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java @@ -411,14 +411,17 @@ public Chunk originalLoadChunk(int cx, int cz) { } /** - * Will return back a chunk, if it doesn't exist and its not a MP client it will generates all the blocks for the - * specified chunk from the map seed and chunk seed + * Provides the chunk, if it's been loaded. Otherwise, if {@link World#findingSpawnPoint} or + * {@link #loadChunkOnProvideRequest}, loads/generates the chunk. If neither is true, returns empty. */ @Override public Chunk provideChunk(int cx, int cz) { Chunk chunk = (Chunk) this.loadedChunkHashMap.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(cx, cz)); - return chunk == null ? (!this.worldObj.findingSpawnPoint && !this.loadChunkOnProvideRequest ? this.empty - : this.loadChunk(cx, cz)) : chunk; + return chunk != null + ? chunk + : this.worldObj.findingSpawnPoint || this.loadChunkOnProvideRequest + ? this.loadChunk(cx, cz) + : this.empty; } /** From f8b021ecc5242f8c45a02ea359067bc50aff2ff0 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Fri, 15 Mar 2024 22:21:18 -0400 Subject: [PATCH 04/18] Mixin oops --- .../mixins/early/minecraft/fastload/MixinEntityPlayerMP.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fe6e51ba..137e32aa 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 @@ -85,7 +85,7 @@ public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { return hodgepodge$dummyPacket; } - @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetHandlerPlayServer;sendPacket(Lnet/minecraft/network/Packet;)V")) + @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetHandlerPlayServer;sendPacket(Lnet/minecraft/network/Packet;)V", ordinal = 1)) private void hodgepodge$sendChunks(NetHandlerPlayServer instance, Packet enumchatvisibility) { for (int i = 0; i < hodgepodge$chunkSends.size(); ++i) { instance.sendPacket(new S26PacketMapChunkBulk(this.hodgepodge$chunkSends.get(i))); From 097922fceb9ef22296fb1b698118f4a854e34de9 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Fri, 15 Mar 2024 22:56:02 -0400 Subject: [PATCH 05/18] Fix chunk sends --- .../minecraft/fastload/MixinEntityPlayerMP.java | 13 ++++++++----- .../early/minecraft/fastload/MixinWorldServer.java | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) 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 137e32aa..bb9bfa1e 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 @@ -51,7 +51,7 @@ public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { return this.hodgepodge$totalChunks; } - @Inject(method = "onUpdate", at = @At(value = "NEW", target = "()Ljava/util/ArrayList;")) + @Inject(method = "onUpdate", at = @At(value = "NEW", target = "()Ljava/util/ArrayList;", ordinal = 0)) private void hodgepodge$setNumChunks(CallbackInfo ci) { this.hodgepodge$totalChunks = 0; this.hodgepodge$chunkSends.clear(); @@ -64,7 +64,7 @@ public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { @ModifyExpressionValue(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;func_149258_c()I")) private int hodgepodge$maxChunks(int value) { - return value * 5; + return Integer.MAX_VALUE; } @Inject(method = "onUpdate", at = @At(value = "INVOKE", target = "Ljava/util/Iterator;next()Ljava/lang/Object;", ordinal = 1)) @@ -77,7 +77,10 @@ public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Ljava/util/ArrayList;isEmpty()Z")) private boolean hodgepodge$areChunksEmpty(ArrayList chunks) { - return this.hodgepodge$chunkSends.isEmpty(); + + if (this.hodgepodge$chunkSends.isEmpty() && chunks.isEmpty()) return true; + this.hodgepodge$chunkSends.add(new ObjectImmutableList<>(chunks)); + return false; } @Redirect(method = "onUpdate", at = @At(value = "NEW", target = "(Ljava/util/List;)Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;")) @@ -96,8 +99,8 @@ public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { private void hodgepodge$fasterChunkSend(CallbackInfo ci) { for (int i = 0; i < this.hodgepodge$chunkSends.size(); ++i) { final ObjectImmutableList chunks = this.hodgepodge$chunkSends.get(i); - for (int j = 0; j < chunks.size(); ++j) { - final Chunk chunk = chunks.get(j); + for (int ii = 0; ii < chunks.size(); ++ii) { + final Chunk chunk = chunks.get(ii); this.getServerForPlayer().getEntityTracker().func_85172_a(((EntityPlayerMP) (Object) this), chunk); MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.Watch(chunk.getChunkCoordIntPair(), ((EntityPlayerMP) (Object) this))); } diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java index a6501f8d..9595fe66 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java @@ -45,7 +45,7 @@ public abstract class MixinWorldServer extends World { @Unique private int hodgepodge$numNewGen = 0; @Unique - private int hodgepodge$maxNewGen = 180; + private int hodgepodge$maxNewGen = 200; @Unique private LongChunkCoordIntPairSet hodgepodge$activeChunks2 = new LongChunkCoordIntPairSet(); @Unique From 9c9b8d9ad39b045e3fbd46f49d824d1d4c07d19f Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Sat, 16 Mar 2024 04:14:28 -0400 Subject: [PATCH 06/18] This doesn't actually work, but it's closer --- .../mitchej123/hodgepodge/mixins/Mixins.java | 3 +- .../fastload/MixinPlayerManager.java | 48 +++++++++++++++++++ .../minecraft/fastload/MixinWorldServer.java | 33 +++++++++---- .../mixins/interfaces/FastWorldServer.java | 20 ++++++++ .../mitchej123/hodgepodge/server/FastCPS.java | 18 +++++++ src/main/resources/META-INF/hodgepodge_at.cfg | 2 + 6 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java create mode 100644 src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/FastWorldServer.java diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java index a351ccdb..389ed57a 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java @@ -382,7 +382,8 @@ public enum Mixins { "minecraft.fastload.MixinIntCache", "minecraft.fastload.MixinWorldChunkManager", "minecraft.fastload.MixinWorldServer", - "minecraft.fastload.MixinEntityPlayerMP") + "minecraft.fastload.MixinEntityPlayerMP", + "minecraft.fastload.MixinPlayerManager") .setApplyIf(() -> SpeedupsConfig.fastChunkHandling)), // Ic2 adjustments diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java new file mode 100644 index 00000000..fa408989 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java @@ -0,0 +1,48 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.mixins.interfaces.FastWorldServer; +import com.mitchej123.hodgepodge.server.FastCPS; +import net.minecraft.server.management.PlayerManager; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.WorldServer; +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.Coerce; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.List; + +@Mixin(PlayerManager.class) +public class MixinPlayerManager { + + @Shadow @Final private WorldServer theWorldServer; + + @WrapWithCondition(method = "updatePlayerPertinentChunks", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z")) + private boolean hodgepodge$throttleChunkGen( + List instance, + @Coerce Object coord, + @Local(name = "i") int pcx, + @Local(name = "j") int pcz) { + + final ChunkCoordIntPair c = (ChunkCoordIntPair) coord; + final int cx = c.chunkXPos; + final int cz = c.chunkZPos; + if (cx == pcx && cz == pcz) + return true; // Always load the player's chunk + if (((FastCPS) this.theWorldServer.theChunkProviderServer).doesChunkExist(c)) + return true; // Always load generated chunks + if (((FastWorldServer) this.theWorldServer).isThrottlingGen()) + return false; // Don't generate new chunks while throttling + + // Generate, but count it against the budget this tick + ((FastWorldServer) this.theWorldServer).spendGenBudget(1); + return true; + } +} diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java index 9595fe66..656bca88 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java @@ -6,6 +6,7 @@ import java.util.concurrent.ExecutionException; import com.mitchej123.hodgepodge.hax.LongChunkCoordIntPairSet; +import com.mitchej123.hodgepodge.mixins.interfaces.FastWorldServer; import com.mitchej123.hodgepodge.util.ChunkPosUtil; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; @@ -20,6 +21,7 @@ import net.minecraft.world.storage.ISaveHandler; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -34,7 +36,7 @@ import static com.mitchej123.hodgepodge.Common.log; @Mixin(WorldServer.class) -public abstract class MixinWorldServer extends World { +public abstract class MixinWorldServer extends World implements FastWorldServer { @Shadow public ChunkProviderServer theChunkProviderServer; @@ -45,19 +47,31 @@ public abstract class MixinWorldServer extends World { @Unique private int hodgepodge$numNewGen = 0; @Unique - private int hodgepodge$maxNewGen = 200; + private int hodgepodge$maxNewGen = 100; @Unique private LongChunkCoordIntPairSet hodgepodge$activeChunks2 = new LongChunkCoordIntPairSet(); - @Unique - private LongChunkCoordIntPairSet hodgepodge$activeChunks3; - @WrapOperation( + @Override + public boolean isThrottlingGen() { + return this.hodgepodge$numNewGen < this.hodgepodge$maxNewGen; + } + + @Override + public int remainingGenBudget() { + return this.hodgepodge$maxNewGen - this.hodgepodge$numNewGen; + } + + @Override + public void spendGenBudget(int amount) { + this.hodgepodge$numNewGen += amount; + } + + @Redirect( method = "createChunkProvider", at = @At( value = "NEW", target = "(Lnet/minecraft/world/WorldServer;Lnet/minecraft/world/chunk/storage/IChunkLoader;Lnet/minecraft/world/chunk/IChunkProvider;)Lnet/minecraft/world/gen/ChunkProviderServer;")) - private ChunkProviderServer hodgepodge$replaceChunkProvider(WorldServer server, IChunkLoader loader, - IChunkProvider backingCP, Operation original) { + private ChunkProviderServer hodgepodge$replaceChunkProvider(WorldServer server, IChunkLoader loader, IChunkProvider backingCP) { return new FastCPS(server, (AnvilChunkLoader) loader, backingCP); } @@ -66,6 +80,7 @@ public abstract class MixinWorldServer extends World { at = @At(value = "INVOKE", target = "Ljava/util/Set;iterator()Ljava/util/Iterator;")) private void hodgepodge$threadChunkGen(CallbackInfo ci) { + this.hodgepodge$chunks.clear(); this.hodgepodge$chunksToLoad.clear(); this.hodgepodge$numNewGen = 0; this.hodgepodge$activeChunks2.clear(); @@ -109,9 +124,9 @@ public abstract class MixinWorldServer extends World { } // This little shuffle is needed to swap two variables - this.hodgepodge$activeChunks3 = (LongChunkCoordIntPairSet) this.activeChunkSet; + LongChunkCoordIntPairSet tmp = (LongChunkCoordIntPairSet) this.activeChunkSet; this.activeChunkSet = this.hodgepodge$activeChunks2; - this.hodgepodge$activeChunks2 = this.hodgepodge$activeChunks3; + this.hodgepodge$activeChunks2 = tmp; // All mChunk updates need to finish before the tick ends, to avoid CMEs... at least for now. // Guarantee that no chunks are still processing before moving on diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/FastWorldServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/FastWorldServer.java new file mode 100644 index 00000000..60871ab2 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/FastWorldServer.java @@ -0,0 +1,20 @@ +package com.mitchej123.hodgepodge.mixins.interfaces; + +public interface FastWorldServer { + + /** + * If true, the server has hit the maximum number of chunks to load that tick and new generation should be avoided, + * where possible. + */ + boolean isThrottlingGen(); + + /** + * Returns the number of chunks left before the server starts throttling generation + */ + int remainingGenBudget(); + + /** + * Reduce the generation budget this tick by amount. + */ + void spendGenBudget(int amount); +} diff --git a/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java index b73a3c34..6935df3a 100644 --- a/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java +++ b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java @@ -614,4 +614,22 @@ public int getLoadedChunkCount() { return this.loadedChunkHashMap.getNumHashElements(); } + /** + * See {@link #doesChunkExist(int, int, long)} + */ + public boolean doesChunkExist(ChunkCoordIntPair coords) { + + final long key = ChunkCoordIntPair.chunkXZ2Int(coords.chunkXPos, coords.chunkZPos); + return this.doesChunkExist(coords.chunkXPos, coords.chunkZPos, key); + } + + /** + * Given chunk coordinates, returns whether that chunk has been generated already. + */ + public boolean doesChunkExist(int cx, int cz, long key) { + + if (this.loadedChunkHashMap.containsItem(key)) return true; + return ((AnvilChunkLoader) this.currentChunkLoader).chunkExists(this.worldObj, cx, cz); + } + } diff --git a/src/main/resources/META-INF/hodgepodge_at.cfg b/src/main/resources/META-INF/hodgepodge_at.cfg index 81bd7193..f788984a 100644 --- a/src/main/resources/META-INF/hodgepodge_at.cfg +++ b/src/main/resources/META-INF/hodgepodge_at.cfg @@ -20,3 +20,5 @@ public net.minecraft.client.resources.FallbackResourceManager field_110540_a # r public net.minecraft.client.resources.FileResourcePack field_110600_d # resourcePackZipFile public net.minecraft.client.resources.SimpleReloadableResourceManager field_110548_a # domainResourceManagers public net.minecraft.world.gen.ChunkProviderServer func_73239_e(II)Lnet/minecraft/world/chunk/Chunk; # safeLoadChunk +public net.minecraft.server.management.PlayerManager$PlayerInstance +public net.minecraft.server.management.PlayerManager$PlayerInstance field_73264_c # chunkLocation From e67a6159a68e31d6953a33a7c6071dca0df1a504 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Sat, 16 Mar 2024 05:32:43 -0400 Subject: [PATCH 07/18] Catch more worldgen --- .../fastload/MixinPlayerManager.java | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java index fa408989..e1005384 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java @@ -14,8 +14,6 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Coerce; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import java.util.List; @@ -24,6 +22,39 @@ public class MixinPlayerManager { @Shadow @Final private WorldServer theWorldServer; + private static final ChunkCoordIntPair hodgepodge$dummyCoord = new ChunkCoordIntPair(Integer.MAX_VALUE, Integer.MAX_VALUE); + + @WrapOperation( + method = "filterChunkLoadQueue", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;", + ordinal = 1)) + private PlayerManager.PlayerInstance hodgepodge$throttleChunkGen(PlayerManager instance, int cx, int cz, boolean instantiate, Operation original) { + + final long key = ChunkCoordIntPair.chunkXZ2Int(cx, cz); + + if (((FastCPS) this.theWorldServer.theChunkProviderServer).doesChunkExist(cx, cz, key)) + return original.call(instance, cx, cz, instantiate); // Always load generated chunks + if (((FastWorldServer) this.theWorldServer).isThrottlingGen()) + return null; // Don't generate new chunks while throttling + + // Generate, but count it against the budget this tick + ((FastWorldServer) this.theWorldServer).spendGenBudget(1); + return original.call(instance, cx, cz, instantiate); + } + + @WrapOperation( + method = "filterChunkLoadQueue", + at = @At( + value = "FIELD", + target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;", + ordinal = 1)) + private ChunkCoordIntPair hodgepodge$throttleChunkGen(PlayerManager.PlayerInstance instance, Operation original) { + if (instance != null) return original.call(instance); + return hodgepodge$dummyCoord; + } + @WrapWithCondition(method = "updatePlayerPertinentChunks", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z")) private boolean hodgepodge$throttleChunkGen( List instance, From f6d21b7bb999f2f0d4f4680b440613281e96cb11 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Sat, 16 Mar 2024 05:44:06 -0400 Subject: [PATCH 08/18] Maybe grab a little more? --- .../fastload/MixinPlayerManager.java | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java index e1005384..535ea445 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java @@ -14,6 +14,7 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Coerce; +import org.spongepowered.asm.mixin.injection.Slice; import java.util.List; @@ -25,11 +26,19 @@ public class MixinPlayerManager { private static final ChunkCoordIntPair hodgepodge$dummyCoord = new ChunkCoordIntPair(Integer.MAX_VALUE, Integer.MAX_VALUE); @WrapOperation( - method = "filterChunkLoadQueue", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;", - ordinal = 1)) + method = "filterChunkLoadQueue", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;"), + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;", + ordinal = 1), + to = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;", + ordinal = 2))) private PlayerManager.PlayerInstance hodgepodge$throttleChunkGen(PlayerManager instance, int cx, int cz, boolean instantiate, Operation original) { final long key = ChunkCoordIntPair.chunkXZ2Int(cx, cz); @@ -45,11 +54,19 @@ public class MixinPlayerManager { } @WrapOperation( - method = "filterChunkLoadQueue", - at = @At( - value = "FIELD", - target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;", - ordinal = 1)) + method = "filterChunkLoadQueue", + at = @At( + value = "FIELD", + target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;"), + slice = @Slice( + from = @At( + value = "FIELD", + target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;", + ordinal = 1), + to = @At( + value = "FIELD", + target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;", + ordinal = 2))) private ChunkCoordIntPair hodgepodge$throttleChunkGen(PlayerManager.PlayerInstance instance, Operation original) { if (instance != null) return original.call(instance); return hodgepodge$dummyCoord; From d2b9dcf4333ddd921c1b4610e0082fbde22ace73 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Sat, 16 Mar 2024 16:37:54 -0400 Subject: [PATCH 09/18] Oops --- .../mixins/early/minecraft/fastload/MixinWorldServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java index 656bca88..ffc312c6 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java @@ -53,7 +53,7 @@ public abstract class MixinWorldServer extends World implements FastWorldServer @Override public boolean isThrottlingGen() { - return this.hodgepodge$numNewGen < this.hodgepodge$maxNewGen; + return this.hodgepodge$numNewGen >= this.hodgepodge$maxNewGen; } @Override From da0a18e4dba4b0ea19e8f35d24eb83ca5dbb526e Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Sun, 24 Mar 2024 03:46:25 -0400 Subject: [PATCH 10/18] Fix chunk throttling and add debugging --- dependencies.gradle | 2 + .../minecraft/fastload/MixinWorldServer.java | 43 +++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index f8314662..2b1d83e6 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -56,6 +56,8 @@ dependencies { transformedModCompileOnly(deobf("https://forum.industrial-craft.net/core/attachment/4316-advancedsolarpanel-1-7-10-3-5-1-jar/")) runtimeOnly(deobf("https://github.com/makamys/CoreTweaks/releases/download/0.3.3.2/CoreTweaks-1.7.10-0.3.3.2+nomixin.jar")) + runtimeOnlyNonPublishable(deobfCurse("bsprint-227409:2725690")) + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Angelica:1.0.0-alpha35") } // Replace when RFG support deobfuscation from notch mappings diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java index ffc312c6..9b4d9a7f 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java @@ -1,7 +1,5 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import java.util.Iterator; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -21,7 +19,6 @@ import net.minecraft.world.storage.ISaveHandler; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -29,8 +26,6 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.mitchej123.hodgepodge.server.FastCPS; import static com.mitchej123.hodgepodge.Common.log; @@ -47,7 +42,15 @@ public abstract class MixinWorldServer extends World implements FastWorldServer @Unique private int hodgepodge$numNewGen = 0; @Unique - private int hodgepodge$maxNewGen = 100; + private final int hodgepodge$maxNewGen = 100; + @Unique + private int hodgepodge$overLoad = 0; + @Unique + private int hodgepodge$properActive = 0; + @Unique + private int hodgepodge$realActive = 0; + @Unique + private boolean hodgepodge$flag = false; @Unique private LongChunkCoordIntPairSet hodgepodge$activeChunks2 = new LongChunkCoordIntPairSet(); @@ -77,12 +80,25 @@ public void spendGenBudget(int amount) { @Inject( method = "func_147456_g", - at = @At(value = "INVOKE", target = "Ljava/util/Set;iterator()Ljava/util/Iterator;")) + at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;func_147456_g()V", shift = At.Shift.AFTER)) private void hodgepodge$threadChunkGen(CallbackInfo ci) { + if (this.hodgepodge$properActive != this.hodgepodge$realActive) { + + log.warn("{} active chunks last tick, should have been {}", this.hodgepodge$realActive, this.hodgepodge$properActive); + } + if (this.hodgepodge$overLoad != 0) { + log.warn("{} excess chunks loaded last tick", this.hodgepodge$overLoad); + this.hodgepodge$overLoad = 0; + } + + this.hodgepodge$flag = false; + this.hodgepodge$chunks.clear(); this.hodgepodge$chunksToLoad.clear(); this.hodgepodge$numNewGen = 0; + this.hodgepodge$properActive = this.activeChunkSet.size(); + this.hodgepodge$realActive = 0; this.hodgepodge$activeChunks2.clear(); // Queue chunks on worker threads or main @@ -140,6 +156,7 @@ public void spendGenBudget(int amount) { if (this.hodgepodge$numNewGen != 0) { log.info("Generated {} new chunks.", this.hodgepodge$numNewGen); + log.info("Loading {} chunks.", this.activeChunkSet.size()); } } @@ -150,8 +167,18 @@ public void spendGenBudget(int amount) { target = "Lnet/minecraft/world/WorldServer;getChunkFromChunkCoords(II)Lnet/minecraft/world/chunk/Chunk;")) private Chunk hodgepodge$threadChunkGen(WorldServer instance, int cx, int cz) { + if (!this.hodgepodge$flag && this.isThrottlingGen()) { + this.hodgepodge$flag = true; + log.warn("{} active chunks", this.activeChunkSet.size()); + } + + ++this.hodgepodge$realActive; final Chunk ch = this.hodgepodge$chunks.get(ChunkCoordIntPair.chunkXZ2Int(cx, cz)); - return ch != null ? ch : this.chunkProvider.provideChunk(cx, cz); + if (ch != null) return ch; + + //log.warn("New chunk at x: {} z: {}", cx, cz); + ++this.hodgepodge$overLoad; + return this.chunkProvider.provideChunk(cx, cz); } public MixinWorldServer(ISaveHandler p_i45368_1_, String p_i45368_2_, WorldProvider p_i45368_3_, From a30a37dbb2940fd17c62dcdbf2778487add0c370 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Sun, 24 Mar 2024 06:30:30 -0400 Subject: [PATCH 11/18] This doesn't unthrottle chunks... why? --- .../fastload/MixinEntityPlayerMP.java | 16 +++- .../fastload/MixinPlayerManager.java | 82 +++++++++++++++++-- .../minecraft/fastload/MixinWorldServer.java | 5 ++ .../mixins/interfaces/ExtEntityPlayerMP.java | 8 ++ 4 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java 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 bb9bfa1e..debcd4f7 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 @@ -2,6 +2,7 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; import com.mojang.authlib.GameProfile; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectImmutableList; @@ -30,7 +31,7 @@ import static com.mitchej123.hodgepodge.Common.log; @Mixin(EntityPlayerMP.class) -public abstract class MixinEntityPlayerMP extends EntityPlayer implements ICrafting { +public abstract class MixinEntityPlayerMP extends EntityPlayer implements ICrafting, ExtEntityPlayerMP { @Unique private final List> hodgepodge$chunkSends = new ObjectArrayList<>(); @@ -38,6 +39,19 @@ public abstract class MixinEntityPlayerMP extends EntityPlayer implements ICraft private int hodgepodge$totalChunks = 0; @Unique private static final S26PacketMapChunkBulk hodgepodge$dummyPacket = new S26PacketMapChunkBulk(); + @Unique + private boolean hodgepodge$isThrottled = false; + @Unique + private boolean hodgepodge$wasThrottled = false; + + @Override + public void setThrottled(boolean val) { this.hodgepodge$isThrottled = val; } + @Override + public boolean isThrottled() { return this.hodgepodge$isThrottled; } + @Override + public void setWasThrottled(boolean val) { this.hodgepodge$wasThrottled = val; } + @Override + public boolean wasThrottled() { return this.hodgepodge$wasThrottled; } @Shadow public abstract WorldServer getServerForPlayer(); diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java index 535ea445..0ffb8e55 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java @@ -4,18 +4,20 @@ import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; import com.mitchej123.hodgepodge.mixins.interfaces.FastWorldServer; import com.mitchej123.hodgepodge.server.FastCPS; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.management.PlayerManager; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.WorldServer; 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.Coerce; -import org.spongepowered.asm.mixin.injection.Slice; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.ArrayList; import java.util.List; @Mixin(PlayerManager.class) @@ -25,6 +27,10 @@ public class MixinPlayerManager { private static final ChunkCoordIntPair hodgepodge$dummyCoord = new ChunkCoordIntPair(Integer.MAX_VALUE, Integer.MAX_VALUE); + /** + * @author ah-OOG-ah + * @reason throttles new chunk generation in filterChunkLoadQueue + */ @WrapOperation( method = "filterChunkLoadQueue", at = @At( @@ -39,20 +45,33 @@ public class MixinPlayerManager { value = "INVOKE", target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;", ordinal = 2))) - private PlayerManager.PlayerInstance hodgepodge$throttleChunkGen(PlayerManager instance, int cx, int cz, boolean instantiate, Operation original) { + private PlayerManager.PlayerInstance hodgepodge$throttleChunkGen( + PlayerManager instance, + int cx, + int cz, + boolean instantiate, + Operation original, + @Local(argsOnly = true) EntityPlayerMP player) { final long key = ChunkCoordIntPair.chunkXZ2Int(cx, cz); if (((FastCPS) this.theWorldServer.theChunkProviderServer).doesChunkExist(cx, cz, key)) return original.call(instance, cx, cz, instantiate); // Always load generated chunks - if (((FastWorldServer) this.theWorldServer).isThrottlingGen()) + if (((FastWorldServer) this.theWorldServer).isThrottlingGen()) { + + ((ExtEntityPlayerMP) player).setThrottled(true); return null; // Don't generate new chunks while throttling + } // Generate, but count it against the budget this tick ((FastWorldServer) this.theWorldServer).spendGenBudget(1); return original.call(instance, cx, cz, instantiate); } + /** + * @author ah-OOG-ah + * @reason handles nulls passed from {@link #hodgepodge$throttleChunkGen(PlayerManager, int, int, boolean, Operation, EntityPlayerMP)} + */ @WrapOperation( method = "filterChunkLoadQueue", at = @At( @@ -72,10 +91,58 @@ public class MixinPlayerManager { return hodgepodge$dummyCoord; } + @WrapOperation(method = "filterChunkLoadQueue", at = @At(value = "INVOKE", target = "Ljava/util/ArrayList;contains(Ljava/lang/Object;)Z")) + private boolean hodgepodge$forceChunkSend( + ArrayList priorChunks, + Object o, + Operation original, + @Local(argsOnly = true) EntityPlayerMP player, + @Local(name = "j") int viewRadius, + @Local(name = "k") int pcx, + @Local(name = "l") int pcz) { + + if (o instanceof ChunkCoordIntPair c) { + + if (!((ExtEntityPlayerMP) player).isThrottled()) { + return original.call(priorChunks, o); + } + + // Returns true for all chunks in a square with sides of (2*viewRadius+1)^2, centered around the player + int dist = Math.max(Math.abs(pcx - c.chunkXPos), Math.abs(pcz - c.chunkZPos)); + return dist <= viewRadius; + } + + return false; + } + + @Inject(method = "updatePlayerPertinentChunks", at = @At(value = "HEAD")) + private void hodgepodge$resetChunkThrottle(CallbackInfo ci, @Local(argsOnly = true) EntityPlayerMP player) { + + if (player instanceof ExtEntityPlayerMP eemp) { + + eemp.setWasThrottled(eemp.isThrottled()); + eemp.setThrottled(false); + } + } + + /** + * @author ah-OOG-ah + * @reason force chunks sent to the player to update if the player's chunkgen was throttled last tick + */ + @ModifyConstant(method = "updatePlayerPertinentChunks", constant = @Constant(doubleValue = 64.0)) + private double hodgepodge$forceChunkUpdate(double original, @Local(argsOnly = true) EntityPlayerMP player) { + return ((ExtEntityPlayerMP) player).wasThrottled() ? Double.MIN_VALUE : original; + } + + /** + * @author ah-OOG-ah + * @reason throttles new chunk generation in updatePlayerPertinentChunks + */ @WrapWithCondition(method = "updatePlayerPertinentChunks", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z")) private boolean hodgepodge$throttleChunkGen( List instance, @Coerce Object coord, + @Local(argsOnly = true) EntityPlayerMP player, @Local(name = "i") int pcx, @Local(name = "j") int pcz) { @@ -86,8 +153,11 @@ public class MixinPlayerManager { return true; // Always load the player's chunk if (((FastCPS) this.theWorldServer.theChunkProviderServer).doesChunkExist(c)) return true; // Always load generated chunks - if (((FastWorldServer) this.theWorldServer).isThrottlingGen()) + if (((FastWorldServer) this.theWorldServer).isThrottlingGen()) { + + ((ExtEntityPlayerMP) player).setThrottled(true); return false; // Don't generate new chunks while throttling + } // Generate, but count it against the budget this tick ((FastWorldServer) this.theWorldServer).spendGenBudget(1); diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java index 9b4d9a7f..1bcdd156 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java @@ -1,5 +1,6 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -9,6 +10,8 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.profiler.Profiler; import net.minecraft.world.*; import net.minecraft.world.chunk.Chunk; @@ -50,6 +53,8 @@ public abstract class MixinWorldServer extends World implements FastWorldServer @Unique private int hodgepodge$realActive = 0; @Unique + private final Set hodgepodge$partialRendering = new ObjectOpenHashSet<>(); + @Unique private boolean hodgepodge$flag = false; @Unique private LongChunkCoordIntPairSet hodgepodge$activeChunks2 = new LongChunkCoordIntPairSet(); diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java b/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java new file mode 100644 index 00000000..a845e71e --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java @@ -0,0 +1,8 @@ +package com.mitchej123.hodgepodge.mixins.interfaces; + +public interface ExtEntityPlayerMP { + void setThrottled(boolean val); + void setWasThrottled(boolean val); + boolean isThrottled(); + boolean wasThrottled(); +} From 7db638b12d3f35e77f4b6d9340addfff15de0625 Mon Sep 17 00:00:00 2001 From: Martin Robertz Date: Sun, 24 Mar 2024 13:43:23 +0100 Subject: [PATCH 12/18] sa --- .../fastload/MixinEntityPlayerMP.java | 74 +++++++--- .../fastload/MixinPlayerManager.java | 128 +++++++++--------- .../minecraft/fastload/MixinWorldServer.java | 28 ++-- .../mixins/interfaces/ExtEntityPlayerMP.java | 4 + .../mitchej123/hodgepodge/server/FastCPS.java | 8 +- 5 files changed, 141 insertions(+), 101 deletions(-) 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 debcd4f7..c25b3d60 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,11 +1,10 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import com.llamalad7.mixinextras.sugar.Local; -import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; -import com.mojang.authlib.GameProfile; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectImmutableList; +import static com.mitchej123.hodgepodge.Common.log; + +import java.util.ArrayList; +import java.util.List; + import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.inventory.ICrafting; @@ -17,6 +16,7 @@ import net.minecraft.world.chunk.Chunk; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.ChunkWatchEvent; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -25,10 +25,13 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.ArrayList; -import java.util.List; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; +import com.mojang.authlib.GameProfile; -import static com.mitchej123.hodgepodge.Common.log; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectImmutableList; @Mixin(EntityPlayerMP.class) public abstract class MixinEntityPlayerMP extends EntityPlayer implements ICrafting, ExtEntityPlayerMP { @@ -45,13 +48,24 @@ public abstract class MixinEntityPlayerMP extends EntityPlayer implements ICraft private boolean hodgepodge$wasThrottled = false; @Override - public void setThrottled(boolean val) { this.hodgepodge$isThrottled = val; } + public void setThrottled(boolean val) { + this.hodgepodge$isThrottled = val; + } + @Override - public boolean isThrottled() { return this.hodgepodge$isThrottled; } + public boolean isThrottled() { + return this.hodgepodge$isThrottled; + } + @Override - public void setWasThrottled(boolean val) { this.hodgepodge$wasThrottled = val; } + public void setWasThrottled(boolean val) { + this.hodgepodge$wasThrottled = val; + } + @Override - public boolean wasThrottled() { return this.hodgepodge$wasThrottled; } + public boolean wasThrottled() { + return this.hodgepodge$wasThrottled; + } @Shadow public abstract WorldServer getServerForPlayer(); @@ -76,12 +90,18 @@ public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { ++this.hodgepodge$totalChunks; } - @ModifyExpressionValue(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;func_149258_c()I")) + @ModifyExpressionValue( + method = "onUpdate", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;func_149258_c()I")) private int hodgepodge$maxChunks(int value) { return Integer.MAX_VALUE; } - @Inject(method = "onUpdate", at = @At(value = "INVOKE", target = "Ljava/util/Iterator;next()Ljava/lang/Object;", ordinal = 1)) + @Inject( + method = "onUpdate", + at = @At(value = "INVOKE", target = "Ljava/util/Iterator;next()Ljava/lang/Object;", ordinal = 1)) private void hodgepodge$fasterChunkSend(CallbackInfo ci, @Local(name = "arraylist") ArrayList chunks) { if (chunks.size() == S26PacketMapChunkBulk.func_149258_c()) { this.hodgepodge$chunkSends.add(new ObjectImmutableList<>(chunks)); @@ -97,26 +117,42 @@ public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { return false; } - @Redirect(method = "onUpdate", at = @At(value = "NEW", target = "(Ljava/util/List;)Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;")) + @Redirect( + method = "onUpdate", + at = @At( + value = "NEW", + target = "(Ljava/util/List;)Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;")) private S26PacketMapChunkBulk hodgepodge$sendChunks(List extracted) { return hodgepodge$dummyPacket; } - @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/NetHandlerPlayServer;sendPacket(Lnet/minecraft/network/Packet;)V", ordinal = 1)) + @Redirect( + method = "onUpdate", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/NetHandlerPlayServer;sendPacket(Lnet/minecraft/network/Packet;)V", + ordinal = 1)) private void hodgepodge$sendChunks(NetHandlerPlayServer instance, Packet enumchatvisibility) { for (int i = 0; i < hodgepodge$chunkSends.size(); ++i) { instance.sendPacket(new S26PacketMapChunkBulk(this.hodgepodge$chunkSends.get(i))); } } - @Inject(method = "onUpdate", at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/ArrayList;iterator()Ljava/util/Iterator;", ordinal = 1), cancellable = true) + @Inject( + method = "onUpdate", + at = @At( + value = "INVOKE_ASSIGN", + target = "Ljava/util/ArrayList;iterator()Ljava/util/Iterator;", + ordinal = 1), + cancellable = true) private void hodgepodge$fasterChunkSend(CallbackInfo ci) { for (int i = 0; i < this.hodgepodge$chunkSends.size(); ++i) { final ObjectImmutableList chunks = this.hodgepodge$chunkSends.get(i); for (int ii = 0; ii < chunks.size(); ++ii) { final Chunk chunk = chunks.get(ii); this.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))); } } diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java index 0ffb8e55..9d0a8627 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java @@ -1,56 +1,58 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; -import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; -import com.mitchej123.hodgepodge.mixins.interfaces.FastWorldServer; -import com.mitchej123.hodgepodge.server.FastCPS; +import java.util.ArrayList; +import java.util.List; + import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.management.PlayerManager; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.WorldServer; + import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.*; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.ArrayList; -import java.util.List; +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; +import com.mitchej123.hodgepodge.mixins.interfaces.FastWorldServer; +import com.mitchej123.hodgepodge.server.FastCPS; @Mixin(PlayerManager.class) public class MixinPlayerManager { - @Shadow @Final private WorldServer theWorldServer; + @Shadow + @Final + private WorldServer theWorldServer; - private static final ChunkCoordIntPair hodgepodge$dummyCoord = new ChunkCoordIntPair(Integer.MAX_VALUE, Integer.MAX_VALUE); + private static final ChunkCoordIntPair hodgepodge$dummyCoord = new ChunkCoordIntPair( + Integer.MAX_VALUE, + Integer.MAX_VALUE); /** * @author ah-OOG-ah * @reason throttles new chunk generation in filterChunkLoadQueue */ @WrapOperation( - method = "filterChunkLoadQueue", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;"), - slice = @Slice( - from = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;", - ordinal = 1), - to = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;", - ordinal = 2))) - private PlayerManager.PlayerInstance hodgepodge$throttleChunkGen( - PlayerManager instance, - int cx, - int cz, - boolean instantiate, - Operation original, + method = "filterChunkLoadQueue", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;"), + slice = @Slice( + from = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;", + ordinal = 1), + to = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;", + ordinal = 2))) + private PlayerManager.PlayerInstance hodgepodge$throttleChunkGen(PlayerManager instance, int cx, int cz, + boolean instantiate, Operation original, @Local(argsOnly = true) EntityPlayerMP player) { final long key = ChunkCoordIntPair.chunkXZ2Int(cx, cz); @@ -70,36 +72,35 @@ public class MixinPlayerManager { /** * @author ah-OOG-ah - * @reason handles nulls passed from {@link #hodgepodge$throttleChunkGen(PlayerManager, int, int, boolean, Operation, EntityPlayerMP)} + * @reason handles nulls passed from + * {@link #hodgepodge$throttleChunkGen(PlayerManager, int, int, boolean, Operation, EntityPlayerMP)} */ @WrapOperation( - method = "filterChunkLoadQueue", - at = @At( - value = "FIELD", - target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;"), - slice = @Slice( - from = @At( - value = "FIELD", - target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;", - ordinal = 1), - to = @At( - value = "FIELD", - target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;", - ordinal = 2))) - private ChunkCoordIntPair hodgepodge$throttleChunkGen(PlayerManager.PlayerInstance instance, Operation original) { + method = "filterChunkLoadQueue", + at = @At( + value = "FIELD", + target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;"), + slice = @Slice( + from = @At( + value = "FIELD", + target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;", + ordinal = 1), + to = @At( + value = "FIELD", + target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;", + ordinal = 2))) + private ChunkCoordIntPair hodgepodge$throttleChunkGen(PlayerManager.PlayerInstance instance, + Operation original) { if (instance != null) return original.call(instance); return hodgepodge$dummyCoord; } - @WrapOperation(method = "filterChunkLoadQueue", at = @At(value = "INVOKE", target = "Ljava/util/ArrayList;contains(Ljava/lang/Object;)Z")) - private boolean hodgepodge$forceChunkSend( - ArrayList priorChunks, - Object o, - Operation original, - @Local(argsOnly = true) EntityPlayerMP player, - @Local(name = "j") int viewRadius, - @Local(name = "k") int pcx, - @Local(name = "l") int pcz) { + @WrapOperation( + method = "filterChunkLoadQueue", + at = @At(value = "INVOKE", target = "Ljava/util/ArrayList;contains(Ljava/lang/Object;)Z")) + private boolean hodgepodge$forceChunkSend(ArrayList priorChunks, Object o, + Operation original, @Local(argsOnly = true) EntityPlayerMP player, + @Local(name = "j") int viewRadius, @Local(name = "k") int pcx, @Local(name = "l") int pcz) { if (o instanceof ChunkCoordIntPair c) { @@ -138,21 +139,18 @@ public class MixinPlayerManager { * @author ah-OOG-ah * @reason throttles new chunk generation in updatePlayerPertinentChunks */ - @WrapWithCondition(method = "updatePlayerPertinentChunks", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z")) - private boolean hodgepodge$throttleChunkGen( - List instance, - @Coerce Object coord, - @Local(argsOnly = true) EntityPlayerMP player, - @Local(name = "i") int pcx, - @Local(name = "j") int pcz) { + @WrapWithCondition( + method = "updatePlayerPertinentChunks", + at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z")) + private boolean hodgepodge$throttleChunkGen(List instance, @Coerce Object coord, + @Local(argsOnly = true) EntityPlayerMP player, @Local(name = "i") int pcx, @Local(name = "j") int pcz) { final ChunkCoordIntPair c = (ChunkCoordIntPair) coord; final int cx = c.chunkXPos; final int cz = c.chunkZPos; - if (cx == pcx && cz == pcz) - return true; // Always load the player's chunk - if (((FastCPS) this.theWorldServer.theChunkProviderServer).doesChunkExist(c)) - return true; // Always load generated chunks + if (cx == pcx && cz == pcz) return true; // Always load the player's chunk + if (((FastCPS) this.theWorldServer.theChunkProviderServer).doesChunkExist(c)) return true; // Always load + // generated chunks if (((FastWorldServer) this.theWorldServer).isThrottlingGen()) { ((ExtEntityPlayerMP) player).setThrottled(true); diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java index 1bcdd156..3c9c8c7a 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java @@ -1,16 +1,11 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; +import static com.mitchej123.hodgepodge.Common.log; + import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import com.mitchej123.hodgepodge.hax.LongChunkCoordIntPairSet; -import com.mitchej123.hodgepodge.mixins.interfaces.FastWorldServer; -import com.mitchej123.hodgepodge.util.ChunkPosUtil; -import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.longs.LongIterator; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.profiler.Profiler; import net.minecraft.world.*; @@ -29,9 +24,15 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import com.mitchej123.hodgepodge.hax.LongChunkCoordIntPairSet; +import com.mitchej123.hodgepodge.mixins.interfaces.FastWorldServer; import com.mitchej123.hodgepodge.server.FastCPS; +import com.mitchej123.hodgepodge.util.ChunkPosUtil; -import static com.mitchej123.hodgepodge.Common.log; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; @Mixin(WorldServer.class) public abstract class MixinWorldServer extends World implements FastWorldServer { @@ -79,7 +80,8 @@ public void spendGenBudget(int amount) { at = @At( value = "NEW", target = "(Lnet/minecraft/world/WorldServer;Lnet/minecraft/world/chunk/storage/IChunkLoader;Lnet/minecraft/world/chunk/IChunkProvider;)Lnet/minecraft/world/gen/ChunkProviderServer;")) - private ChunkProviderServer hodgepodge$replaceChunkProvider(WorldServer server, IChunkLoader loader, IChunkProvider backingCP) { + private ChunkProviderServer hodgepodge$replaceChunkProvider(WorldServer server, IChunkLoader loader, + IChunkProvider backingCP) { return new FastCPS(server, (AnvilChunkLoader) loader, backingCP); } @@ -90,7 +92,10 @@ public void spendGenBudget(int amount) { if (this.hodgepodge$properActive != this.hodgepodge$realActive) { - log.warn("{} active chunks last tick, should have been {}", this.hodgepodge$realActive, this.hodgepodge$properActive); + log.warn( + "{} active chunks last tick, should have been {}", + this.hodgepodge$realActive, + this.hodgepodge$properActive); } if (this.hodgepodge$overLoad != 0) { log.warn("{} excess chunks loaded last tick", this.hodgepodge$overLoad); @@ -114,7 +119,6 @@ public void spendGenBudget(int amount) { final int cx = ChunkPosUtil.getPackedX(key); final int cz = ChunkPosUtil.getPackedZ(key); - // If already loaded, just return it if (cps.loadedChunkHashMap.containsItem(key)) { @@ -181,7 +185,7 @@ public void spendGenBudget(int amount) { final Chunk ch = this.hodgepodge$chunks.get(ChunkCoordIntPair.chunkXZ2Int(cx, cz)); if (ch != null) return ch; - //log.warn("New chunk at x: {} z: {}", cx, cz); + // log.warn("New chunk at x: {} z: {}", cx, cz); ++this.hodgepodge$overLoad; return this.chunkProvider.provideChunk(cx, cz); } diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java b/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java index a845e71e..d328bc29 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java @@ -1,8 +1,12 @@ package com.mitchej123.hodgepodge.mixins.interfaces; public interface ExtEntityPlayerMP { + void setThrottled(boolean val); + void setWasThrottled(boolean val); + boolean isThrottled(); + boolean wasThrottled(); } diff --git a/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java index 6935df3a..33746dc5 100644 --- a/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java +++ b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java @@ -417,11 +417,9 @@ public Chunk originalLoadChunk(int cx, int cz) { @Override public Chunk provideChunk(int cx, int cz) { Chunk chunk = (Chunk) this.loadedChunkHashMap.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(cx, cz)); - return chunk != null - ? chunk - : this.worldObj.findingSpawnPoint || this.loadChunkOnProvideRequest - ? this.loadChunk(cx, cz) - : this.empty; + return chunk != null ? chunk + : this.worldObj.findingSpawnPoint || this.loadChunkOnProvideRequest ? this.loadChunk(cx, cz) + : this.empty; } /** From 8d71bd0fdcb1ca32f4f96ec00b0cca1aec57ae9a Mon Sep 17 00:00:00 2001 From: Martin Robertz Date: Sun, 24 Mar 2024 13:43:34 +0100 Subject: [PATCH 13/18] update deps --- dependencies.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 2b1d83e6..ce9c10ee 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -15,11 +15,11 @@ dependencies { api("com.github.GTNewHorizons:GTNHLib:0.2.10:dev") transformedMod("com.github.GTNewHorizons:NotEnoughItems:2.5.25-GTNH:dev") // force a more up-to-date NEI version - transformedModCompileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-344-GTNH") + transformedModCompileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-346-GTNH") transformedModCompileOnly("com.github.GTNewHorizons:Baubles:1.0.4:dev") // Transitive updates to make runClient17 work transformedModCompileOnly("com.github.GTNewHorizons:ForgeMultipart:1.4.8:dev") - transformedModCompileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.45.116:dev") + transformedModCompileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.45.124:dev") transformedModCompileOnly("com.github.GTNewHorizons:harvestcraft:1.1.10-GTNH:dev") transformedModCompileOnly("com.github.GTNewHorizons:HungerOverhaul:1.1.0-GTNH:dev") transformedModCompileOnly("com.github.GTNewHorizons:MrTJPCore:1.1.6:dev") // Do not update, fixed afterwards From cfedb9f1fb99506b0c5db3a260f5b2447a7cfccd Mon Sep 17 00:00:00 2001 From: Martin Robertz Date: Sun, 24 Mar 2024 13:57:20 +0100 Subject: [PATCH 14/18] no wildcard imports --- .../early/minecraft/fastload/MixinPlayerManager.java | 7 ++++++- .../mixins/early/minecraft/fastload/MixinWorldServer.java | 6 +++++- .../java/com/mitchej123/hodgepodge/server/FastCPS.java | 6 +++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java index 9d0a8627..c8afcfa7 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java @@ -11,7 +11,12 @@ import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Coerce; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyConstant; +import org.spongepowered.asm.mixin.injection.Slice; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java index 3c9c8c7a..6687edea 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinWorldServer.java @@ -8,7 +8,11 @@ import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.profiler.Profiler; -import net.minecraft.world.*; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.World; +import net.minecraft.world.WorldProvider; +import net.minecraft.world.WorldServer; +import net.minecraft.world.WorldSettings; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.chunk.storage.AnvilChunkLoader; diff --git a/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java index 33746dc5..e0f65e49 100644 --- a/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java +++ b/src/main/java/com/mitchej123/hodgepodge/server/FastCPS.java @@ -21,7 +21,11 @@ import net.minecraft.util.ChunkCoordinates; import net.minecraft.util.IProgressUpdate; import net.minecraft.util.ReportedException; -import net.minecraft.world.*; +import net.minecraft.world.ChunkCoordIntPair; +import net.minecraft.world.ChunkPosition; +import net.minecraft.world.MinecraftException; +import net.minecraft.world.World; +import net.minecraft.world.WorldServer; import net.minecraft.world.biome.BiomeGenBase; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.EmptyChunk; From ea9c8451f3d3b2e35912b819fef43b4a5069a2e6 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Sat, 30 Mar 2024 01:31:39 -0400 Subject: [PATCH 15/18] Fix some issues And cause others :fail: --- .../mitchej123/hodgepodge/mixins/Mixins.java | 3 +- .../fastload/MixinEntityPlayerMP.java | 171 +++++++------ .../fastload/MixinPlayerInstance.java | 31 +++ .../fastload/MixinPlayerManager.java | 237 +++++++++--------- .../mixins/interfaces/ExtEntityPlayerMP.java | 6 + .../hodgepodge/util/ChunkPosUtil.java | 99 +++++++- 6 files changed, 340 insertions(+), 207 deletions(-) create mode 100644 src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerInstance.java diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java index 389ed57a..4d89321d 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java @@ -383,7 +383,8 @@ public enum Mixins { "minecraft.fastload.MixinWorldChunkManager", "minecraft.fastload.MixinWorldServer", "minecraft.fastload.MixinEntityPlayerMP", - "minecraft.fastload.MixinPlayerManager") + "minecraft.fastload.MixinPlayerManager", + "minecraft.fastload.MixinPlayerInstance") .setApplyIf(() -> SpeedupsConfig.fastChunkHandling)), // Ic2 adjustments 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 c25b3d60..24bd18b2 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,22 +1,24 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import static com.mitchej123.hodgepodge.Common.log; - -import java.util.ArrayList; -import java.util.List; - +import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; +import com.mitchej123.hodgepodge.util.ChunkPosUtil; +import com.mojang.authlib.GameProfile; +import it.unimi.dsi.fastutil.longs.LongAVLTreeSet; +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectImmutableList; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.inventory.ICrafting; import net.minecraft.network.NetHandlerPlayServer; -import net.minecraft.network.Packet; import net.minecraft.network.play.server.S26PacketMapChunkBulk; +import net.minecraft.tileentity.TileEntity; 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.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -25,13 +27,9 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import com.llamalad7.mixinextras.sugar.Local; -import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; -import com.mojang.authlib.GameProfile; +import java.util.List; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectImmutableList; +import static com.mitchej123.hodgepodge.Common.log; @Mixin(EntityPlayerMP.class) public abstract class MixinEntityPlayerMP extends EntityPlayer implements ICrafting, ExtEntityPlayerMP { @@ -39,6 +37,10 @@ public abstract class MixinEntityPlayerMP extends EntityPlayer implements ICraft @Unique private final List> hodgepodge$chunkSends = new ObjectArrayList<>(); @Unique + private final List hodgepodge$rollingChunks = new ObjectArrayList<>(); + @Unique + private final List hodgepodge$rollingTEs = new ObjectArrayList<>(); + @Unique private int hodgepodge$totalChunks = 0; @Unique private static final S26PacketMapChunkBulk hodgepodge$dummyPacket = new S26PacketMapChunkBulk(); @@ -46,6 +48,10 @@ public abstract class MixinEntityPlayerMP extends EntityPlayer implements ICraft private boolean hodgepodge$isThrottled = false; @Unique private boolean hodgepodge$wasThrottled = false; + @Unique + private final LongOpenHashSet hodgepodge$loadedChunks = new LongOpenHashSet(); + @Unique + private final LongOpenHashSet hodgepodge$chunksToLoad = new LongOpenHashSet(); @Override public void setThrottled(boolean val) { @@ -67,97 +73,98 @@ public boolean wasThrottled() { return this.hodgepodge$wasThrottled; } + @Override + public LongOpenHashSet chunksToLoad() { + return this.hodgepodge$chunksToLoad; + } + + @Override + public LongOpenHashSet loadedChunks() { + return this.hodgepodge$loadedChunks; + } + @Shadow public abstract WorldServer getServerForPlayer(); + @Shadow public NetHandlerPlayServer playerNetServerHandler; + + @Shadow protected abstract void func_147097_b(TileEntity p_147097_1_); + public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { super(p_i45324_1_, p_i45324_2_); } - @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Ljava/util/ArrayList;size()I", ordinal = 0)) - private int hodgepodge$numChunks(ArrayList chunks) { - return this.hodgepodge$totalChunks; + @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Ljava/util/List;isEmpty()Z", ordinal = 1)) + private boolean hodgepodge$skipOGChunkList(List instance) { + return true; } - @Inject(method = "onUpdate", at = @At(value = "NEW", target = "()Ljava/util/ArrayList;", ordinal = 0)) - private void hodgepodge$setNumChunks(CallbackInfo ci) { - this.hodgepodge$totalChunks = 0; - this.hodgepodge$chunkSends.clear(); - } + @Inject(method = "onUpdate", at = @At(value = "TAIL")) + private void hodgepodge$replaceChunkList(CallbackInfo ci) { - @Inject(method = "onUpdate", at = @At(value = "INVOKE", target = "Ljava/util/ArrayList;add(Ljava/lang/Object;)Z")) - private void hodgepodge$incNumChunks(CallbackInfo ci) { - ++this.hodgepodge$totalChunks; - } + if (this.hodgepodge$chunksToLoad.longStream().anyMatch(this.hodgepodge$loadedChunks::contains)) + log.warn("sending duplicate!!!"); - @ModifyExpressionValue( - method = "onUpdate", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;func_149258_c()I")) - private int hodgepodge$maxChunks(int value) { - return Integer.MAX_VALUE; - } + final int chunksPPacket = S26PacketMapChunkBulk.func_149258_c(); + final LongIterator chunkKeys = this.hodgepodge$chunksToLoad.longIterator(); + Chunk chunk; - @Inject( - method = "onUpdate", - at = @At(value = "INVOKE", target = "Ljava/util/Iterator;next()Ljava/lang/Object;", ordinal = 1)) - private void hodgepodge$fasterChunkSend(CallbackInfo ci, @Local(name = "arraylist") ArrayList chunks) { - if (chunks.size() == S26PacketMapChunkBulk.func_149258_c()) { - this.hodgepodge$chunkSends.add(new ObjectImmutableList<>(chunks)); - chunks.clear(); - } - } + // For every chunk... + while (chunkKeys.hasNext()) { - @Redirect(method = "onUpdate", at = @At(value = "INVOKE", target = "Ljava/util/ArrayList;isEmpty()Z")) - private boolean hodgepodge$areChunksEmpty(ArrayList chunks) { + final long key = chunkKeys.nextLong(); + final int cx = ChunkPosUtil.getPackedX(key); + final int cz = ChunkPosUtil.getPackedZ(key); - if (this.hodgepodge$chunkSends.isEmpty() && chunks.isEmpty()) return true; - this.hodgepodge$chunkSends.add(new ObjectImmutableList<>(chunks)); - return false; - } + // Only send the chunk if it exists + if (this.worldObj.blockExists(cx << 4, 0, cz << 4)) { + chunk = this.worldObj.getChunkFromChunkCoords(cx, cz); - @Redirect( - method = "onUpdate", - at = @At( - value = "NEW", - target = "(Ljava/util/List;)Lnet/minecraft/network/play/server/S26PacketMapChunkBulk;")) - private S26PacketMapChunkBulk hodgepodge$sendChunks(List extracted) { - return hodgepodge$dummyPacket; - } + if (chunk.func_150802_k()) { + + ++this.hodgepodge$totalChunks; + this.hodgepodge$rollingChunks.add(chunk); + this.hodgepodge$loadedChunks.add(key); + this.hodgepodge$rollingTEs.addAll(((WorldServer) this.worldObj).func_147486_a(cx << 4, 0, cz << 4, (cx << 4) + 15, 256, (cz << 16) + 15)); + //BugFix: 16 makes it load an extra chunk, which isn't associated with a player, which makes it not unload unless a player walks near it. + chunkKeys.remove(); + } + } - @Redirect( - method = "onUpdate", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/network/NetHandlerPlayServer;sendPacket(Lnet/minecraft/network/Packet;)V", - ordinal = 1)) - private void hodgepodge$sendChunks(NetHandlerPlayServer instance, Packet enumchatvisibility) { - for (int i = 0; i < hodgepodge$chunkSends.size(); ++i) { - instance.sendPacket(new S26PacketMapChunkBulk(this.hodgepodge$chunkSends.get(i))); + // Don't overflow the packet size + if (this.hodgepodge$rollingChunks.size() == chunksPPacket) { + this.hodgepodge$chunkSends.add(new ObjectImmutableList<>(this.hodgepodge$rollingChunks)); + this.hodgepodge$rollingChunks.clear(); + } } - } - @Inject( - method = "onUpdate", - at = @At( - value = "INVOKE_ASSIGN", - target = "Ljava/util/ArrayList;iterator()Ljava/util/Iterator;", - ordinal = 1), - cancellable = true) - private void hodgepodge$fasterChunkSend(CallbackInfo ci) { - for (int i = 0; i < this.hodgepodge$chunkSends.size(); ++i) { - final ObjectImmutableList chunks = this.hodgepodge$chunkSends.get(i); - for (int ii = 0; ii < chunks.size(); ++ii) { - final Chunk chunk = chunks.get(ii); - this.getServerForPlayer().getEntityTracker().func_85172_a(((EntityPlayerMP) (Object) this), chunk); - MinecraftForge.EVENT_BUS.post( - new ChunkWatchEvent.Watch(chunk.getChunkCoordIntPair(), ((EntityPlayerMP) (Object) this))); + // Catch a half-full packet + if (this.hodgepodge$rollingChunks.size() < chunksPPacket) + this.hodgepodge$chunkSends.add(new ObjectImmutableList<>(this.hodgepodge$rollingChunks)); + + if (!this.hodgepodge$chunkSends.isEmpty()) { + + for (int i = 0; i < this.hodgepodge$chunkSends.size(); ++i) { + this.playerNetServerHandler.sendPacket(new S26PacketMapChunkBulk(this.hodgepodge$chunkSends.get(i))); + } + + for (int i = 0; i < this.hodgepodge$rollingTEs.size(); ++i) { + this.func_147097_b(this.hodgepodge$rollingTEs.get(i)); + } + + for (int i = 0; i < this.hodgepodge$totalChunks; ++i) { + chunk = this.hodgepodge$chunkSends.get(i / chunksPPacket).get(i % chunksPPacket); + this.getServerForPlayer().getEntityTracker().func_85172_a((EntityPlayerMP) (Object) this, chunk); + MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.Watch(chunk.getChunkCoordIntPair(), (EntityPlayerMP) (Object) this)); } } if (this.hodgepodge$totalChunks > S26PacketMapChunkBulk.func_149258_c()) log.info("Sent {} chunks to the client", this.hodgepodge$totalChunks); - ci.cancel(); + + this.hodgepodge$totalChunks = 0; + this.hodgepodge$chunkSends.clear(); + this.hodgepodge$rollingChunks.clear(); + this.hodgepodge$rollingTEs.clear(); } } diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerInstance.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerInstance.java new file mode 100644 index 00000000..778cd3c5 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerInstance.java @@ -0,0 +1,31 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; + +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; +import com.mitchej123.hodgepodge.util.ChunkPosUtil; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.management.PlayerManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.List; + +@Mixin(PlayerManager.PlayerInstance.class) +public class MixinPlayerInstance { + + @Redirect(method = "addPlayer", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", ordinal = 1)) + private boolean hodgepodge$replaceChunkSetAdd(List instance, Object o, @Local(argsOnly = true) EntityPlayerMP player) { + return ((ExtEntityPlayerMP) player).chunksToLoad().add(ChunkPosUtil.toLong(o)); + } + + @Redirect(method = "removePlayer", at = @At(value = "INVOKE", target = "Ljava/util/List;remove(Ljava/lang/Object;)Z", ordinal = 3)) + private boolean hodgepodge$replaceChunkSetRemove(List instance, Object o, @Local(argsOnly = true) EntityPlayerMP player) { + return ((ExtEntityPlayerMP) player).chunksToLoad().remove(ChunkPosUtil.toLong(o)); + } + + @Redirect(method = "sendToAllPlayersWatchingChunk", at = @At(value = "INVOKE", target = "Ljava/util/List;contains(Ljava/lang/Object;)Z")) + private boolean hodgepodge$replaceChunkSetContains(List instance, Object o, @Local EntityPlayerMP player) { + return ((ExtEntityPlayerMP) player).chunksToLoad().remove(ChunkPosUtil.toLong(o)); + } +} diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java index c8afcfa7..53617b80 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java @@ -1,169 +1,160 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import java.util.ArrayList; -import java.util.List; - +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; +import com.mitchej123.hodgepodge.mixins.interfaces.FastWorldServer; +import com.mitchej123.hodgepodge.server.FastCPS; +import com.mitchej123.hodgepodge.util.ChunkPosUtil; +import it.unimi.dsi.fastutil.longs.LongArrayList; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.management.PlayerManager; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.WorldServer; import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.Overwrite; 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.Coerce; -import org.spongepowered.asm.mixin.injection.Constant; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyConstant; -import org.spongepowered.asm.mixin.injection.Slice; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; -import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; -import com.mitchej123.hodgepodge.mixins.interfaces.FastWorldServer; -import com.mitchej123.hodgepodge.server.FastCPS; +import org.spongepowered.asm.mixin.injection.Redirect; +import static com.mitchej123.hodgepodge.Common.log; + +import java.util.List; @Mixin(PlayerManager.class) -public class MixinPlayerManager { +public abstract class MixinPlayerManager { - @Shadow - @Final - private WorldServer theWorldServer; + @Shadow @Final private WorldServer theWorldServer; + @Shadow private int playerViewRadius; + @Shadow protected abstract boolean overlaps(int x1, int z1, int x2, int z2, int radius); + @Shadow protected abstract PlayerManager.PlayerInstance getOrCreateChunkWatcher(int cx, int cz, boolean shouldCreate); + @Unique final private ChunkPosUtil.FastComparator cmp = new ChunkPosUtil.FastComparator(); - private static final ChunkCoordIntPair hodgepodge$dummyCoord = new ChunkCoordIntPair( - Integer.MAX_VALUE, - Integer.MAX_VALUE); + @Unique + private final ChunkPosUtil.FastComparator hodgepodge$comparator = new ChunkPosUtil.FastComparator(); /** * @author ah-OOG-ah - * @reason throttles new chunk generation in filterChunkLoadQueue + * @reason Original method kinda sucked ngl */ - @WrapOperation( - method = "filterChunkLoadQueue", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;"), - slice = @Slice( - from = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;", - ordinal = 1), - to = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/management/PlayerManager;getOrCreateChunkWatcher(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance;", - ordinal = 2))) - private PlayerManager.PlayerInstance hodgepodge$throttleChunkGen(PlayerManager instance, int cx, int cz, - boolean instantiate, Operation original, - @Local(argsOnly = true) EntityPlayerMP player) { - - final long key = ChunkCoordIntPair.chunkXZ2Int(cx, cz); - - if (((FastCPS) this.theWorldServer.theChunkProviderServer).doesChunkExist(cx, cz, key)) - return original.call(instance, cx, cz, instantiate); // Always load generated chunks - if (((FastWorldServer) this.theWorldServer).isThrottlingGen()) { + @Overwrite + public void filterChunkLoadQueue(EntityPlayerMP player) { - ((ExtEntityPlayerMP) player).setThrottled(true); - return null; // Don't generate new chunks while throttling - } - - // Generate, but count it against the budget this tick - ((FastWorldServer) this.theWorldServer).spendGenBudget(1); - return original.call(instance, cx, cz, instantiate); + ((ExtEntityPlayerMP) player).chunksToLoad().removeIf(l -> cmp.setPos(player).withinRadius(l, this.playerViewRadius)); + ((ExtEntityPlayerMP) player).loadedChunks().removeIf(l -> cmp.setPos(player).withinRadius(l, this.playerViewRadius)); } /** * @author ah-OOG-ah - * @reason handles nulls passed from - * {@link #hodgepodge$throttleChunkGen(PlayerManager, int, int, boolean, Operation, EntityPlayerMP)} + * @reason Original method was convoluted and impossible to fix */ - @WrapOperation( - method = "filterChunkLoadQueue", - at = @At( - value = "FIELD", - target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;"), - slice = @Slice( - from = @At( - value = "FIELD", - target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;", - ordinal = 1), - to = @At( - value = "FIELD", - target = "Lnet/minecraft/server/management/PlayerManager$PlayerInstance;chunkLocation:Lnet/minecraft/world/ChunkCoordIntPair;", - ordinal = 2))) - private ChunkCoordIntPair hodgepodge$throttleChunkGen(PlayerManager.PlayerInstance instance, - Operation original) { - if (instance != null) return original.call(instance); - return hodgepodge$dummyCoord; - } - - @WrapOperation( - method = "filterChunkLoadQueue", - at = @At(value = "INVOKE", target = "Ljava/util/ArrayList;contains(Ljava/lang/Object;)Z")) - private boolean hodgepodge$forceChunkSend(ArrayList priorChunks, Object o, - Operation original, @Local(argsOnly = true) EntityPlayerMP player, - @Local(name = "j") int viewRadius, @Local(name = "k") int pcx, @Local(name = "l") int pcz) { - - if (o instanceof ChunkCoordIntPair c) { - - if (!((ExtEntityPlayerMP) player).isThrottled()) { - return original.call(priorChunks, o); + @Overwrite + public void updatePlayerPertinentChunks(EntityPlayerMP player) { + + final ExtEntityPlayerMP eemp = (ExtEntityPlayerMP) player; + eemp.setWasThrottled(eemp.isThrottled()); + eemp.setThrottled(false); + + final double deltax = player.posX - player.managedPosX; + final double deltaz = player.posZ - player.managedPosZ; + final double distMovedSquared = deltax * deltax + deltaz * deltaz; + + // If the player moved less than half a chunk (8^2 = 64), or if the player wasn't throttled last tick, no need + // to update + if (!eemp.wasThrottled() && distMovedSquared < 64) + return; + + final int pcx = (int) player.posX >> 4; + final int pcz = (int) player.posZ >> 4; + final int prev_cx = (int) player.managedPosX >> 4; + final int prev_cz = (int) player.managedPosZ >> 4; + final int dpcx = pcx - prev_cx; + final int dpcz = pcz - prev_cz; + + // If the player hasn't moved to a new chunk (and wasn't throttled), no need to update + if (!eemp.wasThrottled() && dpcx == 0 && dpcz == 0) + return; + + int x = 0; + int z = 0; + int sideLen = this.playerViewRadius * 2 + 1; + final LongArrayList chunksToLoad = new LongArrayList(sideLen * sideLen); + + // Add all chunk coords in a spiral around the player + for (int i = 0; i < sideLen * sideLen; ++i) { + + final int cx = x + pcx; + final int cz = z + pcz; + final long key = ChunkCoordIntPair.chunkXZ2Int(cx, cz); + if (!eemp.loadedChunks().contains(key)) + chunksToLoad.add(key); + + // At the same time, we can check the previous chunk grid, and remove the ones which are out of range + if (!this.overlaps(cx - dpcx, cz - dpcz, pcx, pcz, this.playerViewRadius)) { + final PlayerManager.PlayerInstance playerinstance = this.getOrCreateChunkWatcher(cx - dpcx, cz - dpcz, false); + + if (playerinstance != null) + playerinstance.removePlayer(player); } - // Returns true for all chunks in a square with sides of (2*viewRadius+1)^2, centered around the player - int dist = Math.max(Math.abs(pcx - c.chunkXPos), Math.abs(pcz - c.chunkZPos)); - return dist <= viewRadius; + if (Math.abs(x) <= Math.abs(z) && (x != z || x >= 0)) + x += z >= 0 ? 1 : -1; + else + z += x >= 0 ? -1 : 1; } - return false; - } + // Purge the old load queue + this.filterChunkLoadQueue(player); - @Inject(method = "updatePlayerPertinentChunks", at = @At(value = "HEAD")) - private void hodgepodge$resetChunkThrottle(CallbackInfo ci, @Local(argsOnly = true) EntityPlayerMP player) { + player.managedPosX = player.posX; + player.managedPosZ = player.posZ; - if (player instanceof ExtEntityPlayerMP eemp) { + // Generate nearest chunks first + // This also refills the load queue + chunksToLoad.sort(hodgepodge$comparator.setPos(pcx, pcz)); + chunksToLoad.forEach(l -> { + if (this.hodgepodge$allowChunkGen(l, pcx, pcz, eemp)) { + this.getOrCreateChunkWatcher( + ChunkPosUtil.getPackedX(l), + ChunkPosUtil.getPackedZ(l), + true).addPlayer(player); - eemp.setWasThrottled(eemp.isThrottled()); - eemp.setThrottled(false); - } + if (!eemp.chunksToLoad().contains(l)) + eemp.chunksToLoad().add(l); + } + }); } /** - * @author ah-OOG-ah - * @reason force chunks sent to the player to update if the player's chunkgen was throttled last tick + * Checks if a chunk may be loaded without violating the chunkgen throttle. */ - @ModifyConstant(method = "updatePlayerPertinentChunks", constant = @Constant(doubleValue = 64.0)) - private double hodgepodge$forceChunkUpdate(double original, @Local(argsOnly = true) EntityPlayerMP player) { - return ((ExtEntityPlayerMP) player).wasThrottled() ? Double.MIN_VALUE : original; - } + @Unique + private boolean hodgepodge$allowChunkGen(long c, int pcx, int pcz, ExtEntityPlayerMP player) { + + final int cx = ChunkPosUtil.getPackedX(c); + final int cz = ChunkPosUtil.getPackedZ(c); + + if (cx == pcx && cz == pcz) + return true; // Always load the player's chunk + + if (((FastCPS) this.theWorldServer.theChunkProviderServer).doesChunkExist(cx, cz, c)) + return true; // Always load chunks from disk - /** - * @author ah-OOG-ah - * @reason throttles new chunk generation in updatePlayerPertinentChunks - */ - @WrapWithCondition( - method = "updatePlayerPertinentChunks", - at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z")) - private boolean hodgepodge$throttleChunkGen(List instance, @Coerce Object coord, - @Local(argsOnly = true) EntityPlayerMP player, @Local(name = "i") int pcx, @Local(name = "j") int pcz) { - - final ChunkCoordIntPair c = (ChunkCoordIntPair) coord; - final int cx = c.chunkXPos; - final int cz = c.chunkZPos; - if (cx == pcx && cz == pcz) return true; // Always load the player's chunk - if (((FastCPS) this.theWorldServer.theChunkProviderServer).doesChunkExist(c)) return true; // Always load - // generated chunks if (((FastWorldServer) this.theWorldServer).isThrottlingGen()) { - ((ExtEntityPlayerMP) player).setThrottled(true); - return false; // Don't generate new chunks while throttling + // Don't generate new chunks while throttling + player.setThrottled(true); + return false; } // Generate, but count it against the budget this tick ((FastWorldServer) this.theWorldServer).spendGenBudget(1); return true; } + + @Redirect(method = "isPlayerWatchingChunk", at = @At(value = "INVOKE", target = "Ljava/util/List;contains(Ljava/lang/Object;)Z")) + private boolean hodgepodge$replaceLoadedChunks(List instance, Object o, @Local(argsOnly = true) EntityPlayerMP player) { + return ((ExtEntityPlayerMP) player).chunksToLoad().contains(ChunkPosUtil.toLong(o)); + } } diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java b/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java index d328bc29..740801f1 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/interfaces/ExtEntityPlayerMP.java @@ -1,5 +1,7 @@ package com.mitchej123.hodgepodge.mixins.interfaces; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; + public interface ExtEntityPlayerMP { void setThrottled(boolean val); @@ -9,4 +11,8 @@ public interface ExtEntityPlayerMP { boolean isThrottled(); boolean wasThrottled(); + + LongOpenHashSet chunksToLoad(); + + LongOpenHashSet loadedChunks(); } diff --git a/src/main/java/com/mitchej123/hodgepodge/util/ChunkPosUtil.java b/src/main/java/com/mitchej123/hodgepodge/util/ChunkPosUtil.java index e9799b2a..5cf7e85c 100644 --- a/src/main/java/com/mitchej123/hodgepodge/util/ChunkPosUtil.java +++ b/src/main/java/com/mitchej123/hodgepodge/util/ChunkPosUtil.java @@ -1,8 +1,16 @@ package com.mitchej123.hodgepodge.util; +import it.unimi.dsi.fastutil.longs.LongComparator; +import it.unimi.dsi.fastutil.longs.LongPredicate; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.world.ChunkCoordIntPair; + +import java.util.Comparator; + public class ChunkPosUtil { - public static long INT_MASK = (1L << Integer.SIZE) - 1; + public static final long INT_MASK = (1L << Integer.SIZE) - 1; + public static final long INVALID_COORD = Long.MAX_VALUE; public static int getPackedX(long pos) { return (int) (pos & INT_MASK); @@ -16,4 +24,93 @@ public static long toLong(int x, int z) { return (long) x & 4294967295L | ((long) z & 4294967295L) << 32; } + public static long toLong(Object o) { + + if (o instanceof ChunkCoordIntPair c) + return toLong(c.chunkXPos, c.chunkZPos); + + return INVALID_COORD; + } + + public static class ObjComparator implements Comparator { + + private int cx; + private int cz; + + public ObjComparator setPos(int cx, int cz) { + this.cx = cx; + this.cz = cz; + return this; + } + + /** + * Returns a negative value if the first position is closer than the second, and vice versa. Returns zero if the + * positions are identical or equally far from the set position. + */ + @Override + public int compare(ChunkCoordIntPair c1, ChunkCoordIntPair c2) { + + if (c1 == c2) + return 0; + + final int dx1 = c1.chunkXPos - cx; + final int dz1 = c1.chunkZPos - cz; + final int dist1Sq = dx1 * dx1 + dz1 * dz1; + + final int dx2 = c2.chunkXPos - cx; + final int dz2 = c2.chunkZPos - cz; + final int dist2Sq = dx2 * dx2 + dz2 * dz2; + + return Integer.compare(dist1Sq, dist2Sq); + } + } + + public static class FastComparator implements LongComparator { + + private int cx; + private int cz; + + public FastComparator setPos(int cx, int cz) { + this.cx = cx; + this.cz = cz; + return this; + } + + public FastComparator setPos(EntityPlayerMP player) { + this.cx = (int) player.posX >> 4; + this.cz = (int) player.posZ >> 4; + return this; + } + + public boolean withinRadius(long key, int renderDistance) { + if (Math.abs(getPackedX(key) - this.cx) > renderDistance) + return false; + + if (Math.abs(getPackedZ(key) - this.cz) > renderDistance) + return false; + + return true; + } + + /** + * Returns a negative value if the first position is closer than the second, and vice versa. Returns zero if the + * positions are identical or equally far from the set position. + */ + @Override + public int compare(long c1, long c2) { + + if (c1 == c2) + return 0; + + final int dx1 = getPackedX(c1) - cx; + final int dz1 = getPackedZ(c1) - cz; + final int dist1Sq = dx1 * dx1 + dz1 * dz1; + + final int dx2 = getPackedX(c2) - cx; + final int dz2 = getPackedZ(c2) - cz; + final int dist2Sq = dx2 * dx2 + dz2 * dz2; + + return Integer.compare(dist1Sq, dist2Sq); + } + } } From eaff739184edc796a7d7f75df0b4e966755e02b4 Mon Sep 17 00:00:00 2001 From: Martin Robertz Date: Sat, 30 Mar 2024 06:58:58 +0100 Subject: [PATCH 16/18] sa --- .../fastload/MixinEntityPlayerMP.java | 40 ++++---- .../fastload/MixinPlayerInstance.java | 28 ++++-- .../fastload/MixinPlayerManager.java | 92 ++++++++++--------- .../hodgepodge/util/ChunkPosUtil.java | 21 ++--- 4 files changed, 101 insertions(+), 80 deletions(-) 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 24bd18b2..d8b7a485 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,9 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; -import com.mitchej123.hodgepodge.util.ChunkPosUtil; -import com.mojang.authlib.GameProfile; -import it.unimi.dsi.fastutil.longs.LongAVLTreeSet; -import it.unimi.dsi.fastutil.longs.LongIterator; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import it.unimi.dsi.fastutil.objects.ObjectImmutableList; +import static com.mitchej123.hodgepodge.Common.log; + +import java.util.List; + import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.inventory.ICrafting; @@ -19,6 +15,7 @@ import net.minecraft.world.chunk.Chunk; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.ChunkWatchEvent; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -27,9 +24,14 @@ import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import java.util.List; +import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; +import com.mitchej123.hodgepodge.util.ChunkPosUtil; +import com.mojang.authlib.GameProfile; -import static com.mitchej123.hodgepodge.Common.log; +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectImmutableList; @Mixin(EntityPlayerMP.class) public abstract class MixinEntityPlayerMP extends EntityPlayer implements ICrafting, ExtEntityPlayerMP { @@ -86,9 +88,11 @@ public LongOpenHashSet loadedChunks() { @Shadow public abstract WorldServer getServerForPlayer(); - @Shadow public NetHandlerPlayServer playerNetServerHandler; + @Shadow + public NetHandlerPlayServer playerNetServerHandler; - @Shadow protected abstract void func_147097_b(TileEntity p_147097_1_); + @Shadow + protected abstract void func_147097_b(TileEntity p_147097_1_); public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { super(p_i45324_1_, p_i45324_2_); @@ -100,7 +104,7 @@ public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { } @Inject(method = "onUpdate", at = @At(value = "TAIL")) - private void hodgepodge$replaceChunkList(CallbackInfo ci) { + private void hodgepodge$replaceChunkList(CallbackInfo ci) { if (this.hodgepodge$chunksToLoad.longStream().anyMatch(this.hodgepodge$loadedChunks::contains)) log.warn("sending duplicate!!!"); @@ -125,8 +129,11 @@ public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { ++this.hodgepodge$totalChunks; this.hodgepodge$rollingChunks.add(chunk); this.hodgepodge$loadedChunks.add(key); - this.hodgepodge$rollingTEs.addAll(((WorldServer) this.worldObj).func_147486_a(cx << 4, 0, cz << 4, (cx << 4) + 15, 256, (cz << 16) + 15)); - //BugFix: 16 makes it load an extra chunk, which isn't associated with a player, which makes it not unload unless a player walks near it. + this.hodgepodge$rollingTEs.addAll( + ((WorldServer) this.worldObj) + .func_147486_a(cx << 4, 0, cz << 4, (cx << 4) + 15, 256, (cz << 16) + 15)); + // BugFix: 16 makes it load an extra chunk, which isn't associated with a player, which makes it not + // unload unless a player walks near it. chunkKeys.remove(); } } @@ -155,7 +162,8 @@ public MixinEntityPlayerMP(World p_i45324_1_, GameProfile p_i45324_2_) { for (int i = 0; i < this.hodgepodge$totalChunks; ++i) { chunk = this.hodgepodge$chunkSends.get(i / chunksPPacket).get(i % chunksPPacket); this.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)); } } diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerInstance.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerInstance.java index 778cd3c5..268c55b2 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerInstance.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerInstance.java @@ -1,30 +1,40 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import com.llamalad7.mixinextras.sugar.Local; -import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; -import com.mitchej123.hodgepodge.util.ChunkPosUtil; +import java.util.List; + import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.management.PlayerManager; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import java.util.List; +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; +import com.mitchej123.hodgepodge.util.ChunkPosUtil; @Mixin(PlayerManager.PlayerInstance.class) public class MixinPlayerInstance { - @Redirect(method = "addPlayer", at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", ordinal = 1)) - private boolean hodgepodge$replaceChunkSetAdd(List instance, Object o, @Local(argsOnly = true) EntityPlayerMP player) { + @Redirect( + method = "addPlayer", + at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", ordinal = 1)) + private boolean hodgepodge$replaceChunkSetAdd(List instance, Object o, + @Local(argsOnly = true) EntityPlayerMP player) { return ((ExtEntityPlayerMP) player).chunksToLoad().add(ChunkPosUtil.toLong(o)); } - @Redirect(method = "removePlayer", at = @At(value = "INVOKE", target = "Ljava/util/List;remove(Ljava/lang/Object;)Z", ordinal = 3)) - private boolean hodgepodge$replaceChunkSetRemove(List instance, Object o, @Local(argsOnly = true) EntityPlayerMP player) { + @Redirect( + method = "removePlayer", + at = @At(value = "INVOKE", target = "Ljava/util/List;remove(Ljava/lang/Object;)Z", ordinal = 3)) + private boolean hodgepodge$replaceChunkSetRemove(List instance, Object o, + @Local(argsOnly = true) EntityPlayerMP player) { return ((ExtEntityPlayerMP) player).chunksToLoad().remove(ChunkPosUtil.toLong(o)); } - @Redirect(method = "sendToAllPlayersWatchingChunk", at = @At(value = "INVOKE", target = "Ljava/util/List;contains(Ljava/lang/Object;)Z")) + @Redirect( + method = "sendToAllPlayersWatchingChunk", + at = @At(value = "INVOKE", target = "Ljava/util/List;contains(Ljava/lang/Object;)Z")) private boolean hodgepodge$replaceChunkSetContains(List instance, Object o, @Local EntityPlayerMP player) { return ((ExtEntityPlayerMP) player).chunksToLoad().remove(ChunkPosUtil.toLong(o)); } diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java index 53617b80..fcb35b68 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/fastload/MixinPlayerManager.java @@ -1,35 +1,45 @@ package com.mitchej123.hodgepodge.mixins.early.minecraft.fastload; -import com.llamalad7.mixinextras.sugar.Local; -import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; -import com.mitchej123.hodgepodge.mixins.interfaces.FastWorldServer; -import com.mitchej123.hodgepodge.server.FastCPS; -import com.mitchej123.hodgepodge.util.ChunkPosUtil; -import it.unimi.dsi.fastutil.longs.LongArrayList; +import java.util.List; + import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.server.management.PlayerManager; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.WorldServer; import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; 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.Redirect; -import static com.mitchej123.hodgepodge.Common.log; -import java.util.List; +import com.llamalad7.mixinextras.sugar.Local; +import com.mitchej123.hodgepodge.mixins.interfaces.ExtEntityPlayerMP; +import com.mitchej123.hodgepodge.mixins.interfaces.FastWorldServer; +import com.mitchej123.hodgepodge.server.FastCPS; +import com.mitchej123.hodgepodge.util.ChunkPosUtil; + +import it.unimi.dsi.fastutil.longs.LongArrayList; @Mixin(PlayerManager.class) public abstract class MixinPlayerManager { - @Shadow @Final private WorldServer theWorldServer; - @Shadow private int playerViewRadius; - @Shadow protected abstract boolean overlaps(int x1, int z1, int x2, int z2, int radius); - @Shadow protected abstract PlayerManager.PlayerInstance getOrCreateChunkWatcher(int cx, int cz, boolean shouldCreate); - @Unique final private ChunkPosUtil.FastComparator cmp = new ChunkPosUtil.FastComparator(); + @Shadow + @Final + private WorldServer theWorldServer; + @Shadow + private int playerViewRadius; + + @Shadow + protected abstract boolean overlaps(int x1, int z1, int x2, int z2, int radius); + + @Shadow + protected abstract PlayerManager.PlayerInstance getOrCreateChunkWatcher(int cx, int cz, boolean shouldCreate); + + @Unique + final private ChunkPosUtil.FastComparator cmp = new ChunkPosUtil.FastComparator(); @Unique private final ChunkPosUtil.FastComparator hodgepodge$comparator = new ChunkPosUtil.FastComparator(); @@ -41,8 +51,10 @@ public abstract class MixinPlayerManager { @Overwrite public void filterChunkLoadQueue(EntityPlayerMP player) { - ((ExtEntityPlayerMP) player).chunksToLoad().removeIf(l -> cmp.setPos(player).withinRadius(l, this.playerViewRadius)); - ((ExtEntityPlayerMP) player).loadedChunks().removeIf(l -> cmp.setPos(player).withinRadius(l, this.playerViewRadius)); + ((ExtEntityPlayerMP) player).chunksToLoad() + .removeIf(l -> cmp.setPos(player).withinRadius(l, this.playerViewRadius)); + ((ExtEntityPlayerMP) player).loadedChunks() + .removeIf(l -> cmp.setPos(player).withinRadius(l, this.playerViewRadius)); } /** @@ -62,8 +74,7 @@ public void updatePlayerPertinentChunks(EntityPlayerMP player) { // If the player moved less than half a chunk (8^2 = 64), or if the player wasn't throttled last tick, no need // to update - if (!eemp.wasThrottled() && distMovedSquared < 64) - return; + if (!eemp.wasThrottled() && distMovedSquared < 64) return; final int pcx = (int) player.posX >> 4; final int pcz = (int) player.posZ >> 4; @@ -73,8 +84,7 @@ public void updatePlayerPertinentChunks(EntityPlayerMP player) { final int dpcz = pcz - prev_cz; // If the player hasn't moved to a new chunk (and wasn't throttled), no need to update - if (!eemp.wasThrottled() && dpcx == 0 && dpcz == 0) - return; + if (!eemp.wasThrottled() && dpcx == 0 && dpcz == 0) return; int x = 0; int z = 0; @@ -87,21 +97,18 @@ public void updatePlayerPertinentChunks(EntityPlayerMP player) { final int cx = x + pcx; final int cz = z + pcz; final long key = ChunkCoordIntPair.chunkXZ2Int(cx, cz); - if (!eemp.loadedChunks().contains(key)) - chunksToLoad.add(key); + if (!eemp.loadedChunks().contains(key)) chunksToLoad.add(key); // At the same time, we can check the previous chunk grid, and remove the ones which are out of range if (!this.overlaps(cx - dpcx, cz - dpcz, pcx, pcz, this.playerViewRadius)) { - final PlayerManager.PlayerInstance playerinstance = this.getOrCreateChunkWatcher(cx - dpcx, cz - dpcz, false); + final PlayerManager.PlayerInstance playerinstance = this + .getOrCreateChunkWatcher(cx - dpcx, cz - dpcz, false); - if (playerinstance != null) - playerinstance.removePlayer(player); + if (playerinstance != null) playerinstance.removePlayer(player); } - if (Math.abs(x) <= Math.abs(z) && (x != z || x >= 0)) - x += z >= 0 ? 1 : -1; - else - z += x >= 0 ? -1 : 1; + if (Math.abs(x) <= Math.abs(z) && (x != z || x >= 0)) x += z >= 0 ? 1 : -1; + else z += x >= 0 ? -1 : 1; } // Purge the old load queue @@ -115,13 +122,10 @@ public void updatePlayerPertinentChunks(EntityPlayerMP player) { chunksToLoad.sort(hodgepodge$comparator.setPos(pcx, pcz)); chunksToLoad.forEach(l -> { if (this.hodgepodge$allowChunkGen(l, pcx, pcz, eemp)) { - this.getOrCreateChunkWatcher( - ChunkPosUtil.getPackedX(l), - ChunkPosUtil.getPackedZ(l), - true).addPlayer(player); + this.getOrCreateChunkWatcher(ChunkPosUtil.getPackedX(l), ChunkPosUtil.getPackedZ(l), true) + .addPlayer(player); - if (!eemp.chunksToLoad().contains(l)) - eemp.chunksToLoad().add(l); + if (!eemp.chunksToLoad().contains(l)) eemp.chunksToLoad().add(l); } }); } @@ -135,11 +139,12 @@ public void updatePlayerPertinentChunks(EntityPlayerMP player) { final int cx = ChunkPosUtil.getPackedX(c); final int cz = ChunkPosUtil.getPackedZ(c); - if (cx == pcx && cz == pcz) - return true; // Always load the player's chunk + if (cx == pcx && cz == pcz) return true; // Always load the player's chunk - if (((FastCPS) this.theWorldServer.theChunkProviderServer).doesChunkExist(cx, cz, c)) - return true; // Always load chunks from disk + if (((FastCPS) this.theWorldServer.theChunkProviderServer).doesChunkExist(cx, cz, c)) return true; // Always + // load + // chunks + // from disk if (((FastWorldServer) this.theWorldServer).isThrottlingGen()) { @@ -153,8 +158,11 @@ public void updatePlayerPertinentChunks(EntityPlayerMP player) { return true; } - @Redirect(method = "isPlayerWatchingChunk", at = @At(value = "INVOKE", target = "Ljava/util/List;contains(Ljava/lang/Object;)Z")) - private boolean hodgepodge$replaceLoadedChunks(List instance, Object o, @Local(argsOnly = true) EntityPlayerMP player) { + @Redirect( + method = "isPlayerWatchingChunk", + at = @At(value = "INVOKE", target = "Ljava/util/List;contains(Ljava/lang/Object;)Z")) + private boolean hodgepodge$replaceLoadedChunks(List instance, Object o, + @Local(argsOnly = true) EntityPlayerMP player) { return ((ExtEntityPlayerMP) player).chunksToLoad().contains(ChunkPosUtil.toLong(o)); } } diff --git a/src/main/java/com/mitchej123/hodgepodge/util/ChunkPosUtil.java b/src/main/java/com/mitchej123/hodgepodge/util/ChunkPosUtil.java index 5cf7e85c..02c40ea0 100644 --- a/src/main/java/com/mitchej123/hodgepodge/util/ChunkPosUtil.java +++ b/src/main/java/com/mitchej123/hodgepodge/util/ChunkPosUtil.java @@ -1,11 +1,11 @@ package com.mitchej123.hodgepodge.util; -import it.unimi.dsi.fastutil.longs.LongComparator; -import it.unimi.dsi.fastutil.longs.LongPredicate; +import java.util.Comparator; + import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.world.ChunkCoordIntPair; -import java.util.Comparator; +import it.unimi.dsi.fastutil.longs.LongComparator; public class ChunkPosUtil { @@ -26,8 +26,7 @@ public static long toLong(int x, int z) { public static long toLong(Object o) { - if (o instanceof ChunkCoordIntPair c) - return toLong(c.chunkXPos, c.chunkZPos); + if (o instanceof ChunkCoordIntPair c) return toLong(c.chunkXPos, c.chunkZPos); return INVALID_COORD; } @@ -50,8 +49,7 @@ public ObjComparator setPos(int cx, int cz) { @Override public int compare(ChunkCoordIntPair c1, ChunkCoordIntPair c2) { - if (c1 == c2) - return 0; + if (c1 == c2) return 0; final int dx1 = c1.chunkXPos - cx; final int dz1 = c1.chunkZPos - cz; @@ -83,11 +81,9 @@ public FastComparator setPos(EntityPlayerMP player) { } public boolean withinRadius(long key, int renderDistance) { - if (Math.abs(getPackedX(key) - this.cx) > renderDistance) - return false; + if (Math.abs(getPackedX(key) - this.cx) > renderDistance) return false; - if (Math.abs(getPackedZ(key) - this.cz) > renderDistance) - return false; + if (Math.abs(getPackedZ(key) - this.cz) > renderDistance) return false; return true; } @@ -99,8 +95,7 @@ public boolean withinRadius(long key, int renderDistance) { @Override public int compare(long c1, long c2) { - if (c1 == c2) - return 0; + if (c1 == c2) return 0; final int dx1 = getPackedX(c1) - cx; final int dz1 = getPackedZ(c1) - cz; From 0f091dcd708f3fe4e0a9caf647c48f1eed160035 Mon Sep 17 00:00:00 2001 From: Martin Robertz Date: Sat, 30 Mar 2024 06:59:19 +0100 Subject: [PATCH 17/18] update deps --- dependencies.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index ce9c10ee..695c0b87 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -19,12 +19,12 @@ dependencies { transformedModCompileOnly("com.github.GTNewHorizons:Baubles:1.0.4:dev") // Transitive updates to make runClient17 work transformedModCompileOnly("com.github.GTNewHorizons:ForgeMultipart:1.4.8:dev") - transformedModCompileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.45.124:dev") + transformedModCompileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.45.132:dev") transformedModCompileOnly("com.github.GTNewHorizons:harvestcraft:1.1.10-GTNH:dev") transformedModCompileOnly("com.github.GTNewHorizons:HungerOverhaul:1.1.0-GTNH:dev") - transformedModCompileOnly("com.github.GTNewHorizons:MrTJPCore:1.1.6:dev") // Do not update, fixed afterwards + transformedModCompileOnly("com.github.GTNewHorizons:MrTJPCore:1.1.7:dev") // Do not update, fixed afterwards transformedModCompileOnly("com.github.GTNewHorizons:Railcraft:9.15.7:dev") { exclude group: "thaumcraft", module: "Thaumcraft" } - transformedModCompileOnly("com.github.GTNewHorizons:TinkersConstruct:1.11.12-GTNH:dev") + transformedModCompileOnly("com.github.GTNewHorizons:TinkersConstruct:1.11.13-GTNH:dev") transformedModCompileOnly(deobfCurse("bibliocraft-228027:2423369")) transformedModCompileOnly("curse.maven:biomes-o-plenty-220318:2499612") transformedModCompileOnly("curse.maven:cofh-core-69162:2388751") From 856c3db9e521c8bfd389a198427ecc138f800d37 Mon Sep 17 00:00:00 2001 From: ah-OOG-ah <75745146+ah-OOG-ah@users.noreply.github.com> Date: Mon, 13 May 2024 12:56:24 -0400 Subject: [PATCH 18/18] Bump Angelica --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 00801689..99c9f0e2 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -59,7 +59,7 @@ dependencies { runtimeOnly(deobf("https://github.com/makamys/CoreTweaks/releases/download/0.3.3.2/CoreTweaks-1.7.10-0.3.3.2+nomixin.jar")) runtimeOnlyNonPublishable(deobfCurse("bsprint-227409:2725690")) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Angelica:1.0.0-alpha35") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Angelica:1.0.0-alpha47") } // Replace when RFG support deobfuscation from notch mappings