Multi-threaded chunk saving (not really)
This commit is contained in:
@@ -96,9 +96,9 @@ public abstract class Akari {
|
|||||||
*/
|
*/
|
||||||
public final static Timing worldTiming = getTiming("Akarin - Full World Tick");
|
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) {
|
private static Timing getTiming(String name) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ public class AkarinGlobalConfig {
|
|||||||
|
|
||||||
public static long timeUpdateInterval;
|
public static long timeUpdateInterval;
|
||||||
private static void 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;
|
public static long keepAliveSendInterval;
|
||||||
@@ -267,7 +267,7 @@ public class AkarinGlobalConfig {
|
|||||||
|
|
||||||
public static long playersInfoUpdateInterval;
|
public static long playersInfoUpdateInterval;
|
||||||
private static void 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;
|
public static long versionUpdateInterval;
|
||||||
@@ -284,4 +284,9 @@ public class AkarinGlobalConfig {
|
|||||||
private static void forceHardcoreDifficulty() {
|
private static void forceHardcoreDifficulty() {
|
||||||
forceHardcoreDifficulty = getBoolean("alternative.force-difficulty-on-hardcore", true);
|
forceHardcoreDifficulty = getBoolean("alternative.force-difficulty-on-hardcore", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int fileIOThreads;
|
||||||
|
private static void fileIOThreads() {
|
||||||
|
fileIOThreads = getInt("core.chunk-save-threads", 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,8 +37,8 @@ public abstract class MixinTimingHandler {
|
|||||||
|
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
@Inject(method = "startTiming", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "startTiming", at = @At("HEAD"), cancellable = true)
|
||||||
public void onStartTiming(CallbackInfoReturnable ci) {
|
public void onStartTiming(CallbackInfoReturnable cir) {
|
||||||
if (!Akari.isPrimaryThread(false)) ci.setReturnValue(this); // Avoid modify any field
|
if (!Akari.isPrimaryThread(false)) cir.setReturnValue(this); // Avoid modify any field
|
||||||
}
|
}
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
|
|||||||
81
sources/src/main/java/net/minecraft/server/FileIOThread.java
Normal file
81
sources/src/main/java/net/minecraft/server/FileIOThread.java
Normal file
@@ -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<IAsyncChunkSaver> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1157,9 +1157,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|||||||
|
|
||||||
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
|
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
|
||||||
|
|
||||||
|
tracker.entriesLock.lock(); // Akarin
|
||||||
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
|
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
|
||||||
if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
|
if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
|
||||||
tracker.entriesLock.lock(); // Akarin
|
|
||||||
entry.updatePlayer(getHandle());
|
entry.updatePlayer(getHandle());
|
||||||
tracker.entriesLock.unlock(); // Akarin
|
tracker.entriesLock.unlock(); // Akarin
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"core.MixinCommandKick",
|
"core.MixinCommandKick",
|
||||||
"core.MixinCraftServer",
|
"core.MixinCraftServer",
|
||||||
"core.MixinWorldServer",
|
"core.MixinWorldServer",
|
||||||
|
"core.MixinFileIOThread",
|
||||||
"core.MixinWorldManager",
|
"core.MixinWorldManager",
|
||||||
"core.MixinCommandBanIp",
|
"core.MixinCommandBanIp",
|
||||||
"core.MixinChunkSection",
|
"core.MixinChunkSection",
|
||||||
|
|||||||
Reference in New Issue
Block a user