diff --git a/sources/src/main/java/io/akarin/api/internal/Akari.java b/sources/src/main/java/io/akarin/api/internal/Akari.java index 9095df37c..4566c4b48 100644 --- a/sources/src/main/java/io/akarin/api/internal/Akari.java +++ b/sources/src/main/java/io/akarin/api/internal/Akari.java @@ -96,9 +96,9 @@ public abstract class Akari { */ public final static Timing worldTiming = getTiming("Akarin - Full World Tick"); - public final static Timing entityCallbackTiming = getTiming("Akarin - Entity Callback"); + public final static Timing entityCallbackTiming = getTiming("Akarin - Entity Parallell Await"); - public final static Timing callbackTiming = getTiming("Akarin - Callback"); + public final static Timing callbackTiming = getTiming("Akarin - Callback Queue"); private static Timing getTiming(String name) { try { diff --git a/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java b/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java index 62115a4f5..f179b597d 100644 --- a/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java +++ b/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java @@ -172,7 +172,7 @@ public class AkarinGlobalConfig { public static long timeUpdateInterval; private static void timeUpdateInterval() { - timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10; + timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10 * 2; } public static long keepAliveSendInterval; @@ -267,7 +267,7 @@ public class AkarinGlobalConfig { public static long playersInfoUpdateInterval; private static void playersInfoUpdateInterval() { - playersInfoUpdateInterval = getSeconds(getString("core.tick-rate.players-info-update-interval", "30s")) * 10; + playersInfoUpdateInterval = getSeconds(getString("core.tick-rate.players-info-update-interval", "30s")) * 10 * 2; } public static long versionUpdateInterval; @@ -284,4 +284,9 @@ public class AkarinGlobalConfig { private static void forceHardcoreDifficulty() { forceHardcoreDifficulty = getBoolean("alternative.force-difficulty-on-hardcore", true); } + + public static int fileIOThreads; + private static void fileIOThreads() { + fileIOThreads = getInt("core.chunk-save-threads", 2); + } } diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinFileIOThread.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinFileIOThread.java new file mode 100644 index 000000000..62ba230e2 --- /dev/null +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinFileIOThread.java @@ -0,0 +1,56 @@ +package io.akarin.server.mixin.core; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import com.destroystokyo.paper.PaperConfig; +import com.google.common.util.concurrent.ThreadFactoryBuilder; + +import io.akarin.server.core.AkarinGlobalConfig; +import net.minecraft.server.FileIOThread; +import net.minecraft.server.IAsyncChunkSaver; + +@Mixin(value = FileIOThread.class, remap = false) +public abstract class MixinFileIOThread { + private final Executor executor = Executors.newFixedThreadPool(AkarinGlobalConfig.fileIOThreads, new ThreadFactoryBuilder().setNameFormat("Akarin File IO Thread - %1$d").setPriority(1).build()); + private final AtomicInteger queuedChunkCounter = new AtomicInteger(0); + + @Shadow(aliases = "e") private volatile boolean isAwaitFinish; + + @Overwrite // OBFHELPER: saveChunk + public void a(IAsyncChunkSaver iasyncchunksaver) { + queuedChunkCounter.incrementAndGet(); + executor.execute(() -> writeChunk(iasyncchunksaver)); + } + + /** + * Process a chunk, re-add to the queue if unsuccessful + */ + private void writeChunk(IAsyncChunkSaver iasyncchunksaver) { + if (!iasyncchunksaver.a()) { // PAIL: WriteNextIO() -> Returns if the write was unsuccessful + queuedChunkCounter.decrementAndGet(); + + if (PaperConfig.enableFileIOThreadSleep) { + try { + Thread.sleep(isAwaitFinish ? 0L : 2L); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + } else { + writeChunk(iasyncchunksaver); + } + } + + @Overwrite // OBFHELPER: waitForFinish + public void b() throws InterruptedException { + isAwaitFinish = true; + while (queuedChunkCounter.get() != 0) Thread.sleep(9L); + isAwaitFinish = false; + } +} diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java index b3fca0e5e..3a915fec0 100644 --- a/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java @@ -37,8 +37,8 @@ public abstract class MixinTimingHandler { @SuppressWarnings({"rawtypes", "unchecked"}) @Inject(method = "startTiming", at = @At("HEAD"), cancellable = true) - public void onStartTiming(CallbackInfoReturnable ci) { - if (!Akari.isPrimaryThread(false)) ci.setReturnValue(this); // Avoid modify any field + public void onStartTiming(CallbackInfoReturnable cir) { + if (!Akari.isPrimaryThread(false)) cir.setReturnValue(this); // Avoid modify any field } @Overwrite diff --git a/sources/src/main/java/net/minecraft/server/FileIOThread.java b/sources/src/main/java/net/minecraft/server/FileIOThread.java new file mode 100644 index 000000000..2fb331c08 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/FileIOThread.java @@ -0,0 +1,81 @@ +package net.minecraft.server; + +import java.util.List; + +/** + * Akarin Changes Note + * 1) Multi-threaded chunk saving (performance) + */ +public class FileIOThread implements Runnable { + + private static final FileIOThread a = new FileIOThread(); + private final List b = /*Collections.synchronizedList(Lists.newArrayList())*/ null; // Akarin - I don't think any plugin rely on this + private volatile long c; + private volatile long d; + private volatile boolean e; + + private FileIOThread() { + // Thread thread = new Thread(this, "File IO Thread"); // Akarin + + // thread.setPriority(1); // Akarin + // thread.start(); // Akarin + } + + public static FileIOThread a() { + return FileIOThread.a; + } + + public void run() { + while (true) { + this.c(); + } + } + + private void c() { + for (int i = 0; i < this.b.size(); ++i) { + IAsyncChunkSaver iasyncchunksaver = (IAsyncChunkSaver) this.b.get(i); + boolean flag = iasyncchunksaver.a(); + + if (!flag) { + this.b.remove(i--); + ++this.d; + } + + // Paper start - Add toggle + if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) { + try { + Thread.sleep(this.e ? 0L : 2L); + } catch (InterruptedException interruptedexception) { + interruptedexception.printStackTrace(); + } + } + // Paper end + } + + if (this.b.isEmpty()) { + try { + Thread.sleep(25L); + } catch (InterruptedException interruptedexception1) { + interruptedexception1.printStackTrace(); + } + } + + } + + public void a(IAsyncChunkSaver iasyncchunksaver) { + if (!this.b.contains(iasyncchunksaver)) { + ++this.c; + this.b.add(iasyncchunksaver); + } + } + + public void b() throws InterruptedException { + this.e = true; + + while (this.c != this.d) { + Thread.sleep(10L); + } + + this.e = false; + } +} diff --git a/sources/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/sources/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 78812800f..9d562e4ea 100644 --- a/sources/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/sources/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -1157,9 +1157,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player { getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other)); + tracker.entriesLock.lock(); // Akarin EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId()); if (entry != null && !entry.trackedPlayers.contains(getHandle())) { - tracker.entriesLock.lock(); // Akarin entry.updatePlayer(getHandle()); tracker.entriesLock.unlock(); // Akarin } diff --git a/sources/src/main/resources/mixins.akarin.core.json b/sources/src/main/resources/mixins.akarin.core.json index 98c68e838..371b8157a 100644 --- a/sources/src/main/resources/mixins.akarin.core.json +++ b/sources/src/main/resources/mixins.akarin.core.json @@ -19,6 +19,7 @@ "core.MixinCommandKick", "core.MixinCraftServer", "core.MixinWorldServer", + "core.MixinFileIOThread", "core.MixinWorldManager", "core.MixinCommandBanIp", "core.MixinChunkSection",