diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinWorld.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinWorld.java new file mode 100644 index 000000000..bfaf7d016 --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinWorld.java @@ -0,0 +1,34 @@ +package io.akarin.server.mixin.core; + +import java.util.List; + +import javax.annotation.Nullable; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import net.minecraft.server.AxisAlignedBB; +import net.minecraft.server.Entity; +import net.minecraft.server.World; + +/** + * Fixes MC-103516(https://bugs.mojang.com/browse/MC-103516) + */ +@Mixin(value = World.class, remap = false) +public abstract class MixinWorld { + @Shadow public abstract List getEntities(@Nullable Entity entity, AxisAlignedBB box); + + /** + * Returns true if there are no solid, live entities in the specified AxisAlignedBB, excluding the given entity + */ + public boolean a(AxisAlignedBB box, @Nullable Entity target) { // PAIL: checkNoEntityCollision + List list = this.getEntities(null, box); + + for (Entity each : list) { + if (!each.dead && each.i && each != target && (target == null || !each.x(target))) { // PAIL: preventEntitySpawning - isRidingSameEntity + return false; + } + } + return true; + } +} diff --git a/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunkProviderServer.java b/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunkProviderServer.java index 5d61b69f3..02e650fd6 100644 --- a/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunkProviderServer.java +++ b/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunkProviderServer.java @@ -10,7 +10,9 @@ import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; import net.minecraft.server.Chunk; import net.minecraft.server.ChunkProviderServer; import net.minecraft.server.IChunkLoader; @@ -21,13 +23,10 @@ public abstract class MixinChunkProviderServer { @Shadow @Final public WorldServer world; @Shadow public Long2ObjectOpenHashMap chunks; - public int pendingUnloadChunks; // For keeping unload target-size feature - public void unload(Chunk chunk) { if (this.world.worldProvider.c(chunk.locX, chunk.locZ)) { // Akarin - avoid using the queue and simply check the unloaded flag during unloads // this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(chunk.locX, chunk.locZ))); - pendingUnloadChunks++; chunk.setShouldUnload(true); } } @@ -42,46 +41,41 @@ public abstract class MixinChunkProviderServer { long now = System.currentTimeMillis(); long unloadAfter = world.paperConfig.delayChunkUnloadsBy; SlackActivityAccountant activityAccountant = world.getMinecraftServer().slackActivityAccountant; - Iterator it = chunks.values().iterator(); + activityAccountant.startActivity(0.5); + ObjectIterator> it = chunks.long2ObjectEntrySet().fastIterator(); + int remainingChunks = chunks.size(); + int targetSize = Math.min(remainingChunks - 100, (int) (remainingChunks * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive while (it.hasNext()) { - activityAccountant.startActivity(0.5); + Entry entry = it.next(); + Chunk chunk = entry.getValue(); + if (chunk == null) continue; - Chunk chunk = it.next(); - if (unloadAfter > 0) { - if (chunk.scheduledForUnload != null && now - chunk.scheduledForUnload > unloadAfter) { - chunk.scheduledForUnload = null; - unload(chunk); + if (chunk.isUnloading()) { + if (chunk.scheduledForUnload != null) { + if (now - chunk.scheduledForUnload > unloadAfter) { + chunk.scheduledForUnload = null; + } else continue; } - } - int targetSize = Math.min(pendingUnloadChunks - 100, (int) (pendingUnloadChunks * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive - - if (chunk != null && chunk.isUnloading()) { - // If a plugin cancelled it, we shouldn't trying unload it for a while - chunk.setShouldUnload(false); // Paper - if (!unloadChunk(chunk, true)) continue; // Event cancelled - it.remove(); + if (!unloadChunk(chunk, true)) { // Event cancelled + // If a plugin cancelled it, we shouldn't trying unload it for a while + chunk.setShouldUnload(false); + continue; + } - if (--pendingUnloadChunks <= targetSize && activityAccountant.activityTimeIsExhausted()) break; + it.remove(); // Life is strange + if (--remainingChunks <= targetSize || activityAccountant.activityTimeIsExhausted()) break; // more slack since the target size not work as intended } - activityAccountant.endActivity(); } + activityAccountant.endActivity(); this.chunkLoader.b(); // PAIL: chunkTick } return false; } - @Redirect(method = "unloadChunk", at = @At( - value = "INVOKE", - target = "it/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap.remove(J)Ljava/lang/Object;" - )) - private Object remove(Long2ObjectOpenHashMap chunks, long chunkHash) { - return null; - } - @Overwrite public String getName() { - return "ServerChunkCache: " + chunks.size(); // Akarin - remove unload queue + return "ServerChunkCache: " + chunks.size(); } } diff --git a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunk.java b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunk.java index 581f7e696..755c41fba 100644 --- a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunk.java +++ b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunk.java @@ -245,7 +245,7 @@ public abstract class MixinChunk implements IMixinChunk { return; } - if (Akari.isPrimaryThread()) { + if (Akari.isPrimaryThread(false)) { try { lightExecutorService.execute(() -> { this.checkLightAsync(neighborChunks); diff --git a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorld.java b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorld.java index 78c2bb908..8d926e016 100644 --- a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorld.java +++ b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorld.java @@ -34,7 +34,7 @@ import net.minecraft.server.IChunkProvider; import net.minecraft.server.MinecraftServer; import net.minecraft.server.World; -@Mixin(value = World.class, remap = false) +@Mixin(value = World.class, remap = false, priority = 1001) public abstract class MixinWorld { @Shadow protected IChunkProvider chunkProvider; @Shadow int[] J; // PAIL: lightUpdateBlockList diff --git a/sources/src/main/java/net/minecraft/server/Chunk.java b/sources/src/main/java/net/minecraft/server/Chunk.java index 616957287..b7fb0bd89 100644 --- a/sources/src/main/java/net/minecraft/server/Chunk.java +++ b/sources/src/main/java/net/minecraft/server/Chunk.java @@ -71,7 +71,7 @@ public class Chunk { return removed; } } - final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this); + public final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this); // Akarin - public // Paper end private volatile boolean done; // Akarin - volatile private volatile boolean lit; // Akarin - volatile diff --git a/sources/src/main/java/net/minecraft/server/PaperLightingQueue.java b/sources/src/main/java/net/minecraft/server/PaperLightingQueue.java new file mode 100644 index 000000000..8fa62c20b --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/PaperLightingQueue.java @@ -0,0 +1,98 @@ +package net.minecraft.server; + +import co.aikar.timings.Timing; +import it.unimi.dsi.fastutil.objects.ObjectCollection; + +import java.util.ArrayDeque; + +/** + * Akarin Changes Note
+ *
+ * 1) Expose private members
+ * @author cakoyo + */ +class PaperLightingQueue { + private static final long MAX_TIME = (long) (1000000000 / 20 * .95); + private static int updatesThisTick; + + + static void processQueue(long curTime) { + updatesThisTick = 0; + + final long startTime = System.nanoTime(); + final long maxTickTime = MAX_TIME - (startTime - curTime); + + START: + for (World world : MinecraftServer.getServer().worlds) { + if (!world.paperConfig.queueLightUpdates) { + continue; + } + + ObjectCollection loadedChunks = ((WorldServer) world).getChunkProviderServer().chunks.values(); + for (Chunk chunk : loadedChunks.toArray(new Chunk[loadedChunks.size()])) { + if (chunk.lightingQueue.processQueue(startTime, maxTickTime)) { + break START; + } + } + } + } + + public static class LightingQueue extends ArrayDeque { // Akarin - public + final private Chunk chunk; + + LightingQueue(Chunk chunk) { + super(); + this.chunk = chunk; + } + + /** + * Processes the lighting queue for this chunk + * + * @param startTime If start Time is 0, we will not limit execution time + * @param maxTickTime Maximum time to spend processing lighting updates + * @return true to abort processing furthur lighting updates + */ + private boolean processQueue(long startTime, long maxTickTime) { + if (this.isEmpty()) { + return false; + } + try (Timing ignored = chunk.world.timings.lightingQueueTimer.startTiming()) { + Runnable lightUpdate; + while ((lightUpdate = this.poll()) != null) { + lightUpdate.run(); + if (startTime > 0 && ++PaperLightingQueue.updatesThisTick % 10 == 0 && PaperLightingQueue.updatesThisTick > 10) { + if (System.nanoTime() - startTime > maxTickTime) { + return true; + } + } + } + } + + return false; + } + + /** + * Flushes lighting updates to unload the chunk + */ + public void processUnload() { // Akarin - public + if (!chunk.world.paperConfig.queueLightUpdates) { + return; + } + processQueue(0, 0); // No timeout + + final int radius = 1; // TODO: bitflip, why should this ever be 2? + for (int x = chunk.locX - radius; x <= chunk.locX + radius; ++x) { + for (int z = chunk.locZ - radius; z <= chunk.locZ + radius; ++z) { + if (x == chunk.locX && z == chunk.locZ) { + continue; + } + + Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(chunk.world, x, z); + if (neighbor != null) { + neighbor.lightingQueue.processQueue(0, 0); // No timeout + } + } + } + } + } +} diff --git a/sources/src/main/resources/mixins.akarin.core.json b/sources/src/main/resources/mixins.akarin.core.json index 0bdc2979a..2b3b586df 100644 --- a/sources/src/main/resources/mixins.akarin.core.json +++ b/sources/src/main/resources/mixins.akarin.core.json @@ -13,6 +13,7 @@ "bootstrap.MetricsBootstrap", "bootstrap.MixinRestartCommand", + "core.MixinWorld", "core.MixinMCUtil", "core.MixinPlayerList", "core.MixinCommandBan",