From 2372955f6687afa177ae71109c82beda9d54418f Mon Sep 17 00:00:00 2001 From: Simon Gardling Date: Fri, 14 May 2021 14:53:43 -0400 Subject: [PATCH] rebase c2me port --- .github/workflows/build.yml | 2 +- Jenkinsfile | 11 +- PATCHES.md | 6 + build.gradle.kts | 6 +- patches/api/0009-java-11.patch | 21 + .../0072-Multi-threaded-RegionFile-IO.patch | 307 ++++++++ patches/server/0068-C2ME-Port.patch | 653 ++++++++++++++++++ .../0069-Multi-threaded-World-Upgrade.patch | 144 ++++ ...0-lithium-MultiNoiseBiomeSourceMixin.patch | 195 ++++++ patches/server/0071-Force-world-save.patch | 42 ++ patches/server/0072-java-11.patch | 58 ++ 11 files changed, 1436 insertions(+), 9 deletions(-) create mode 100644 patches/api/0009-java-11.patch create mode 100644 patches/removed/server/0072-Multi-threaded-RegionFile-IO.patch create mode 100644 patches/server/0068-C2ME-Port.patch create mode 100644 patches/server/0069-Multi-threaded-World-Upgrade.patch create mode 100644 patches/server/0070-lithium-MultiNoiseBiomeSourceMixin.patch create mode 100644 patches/server/0071-Force-world-save.patch create mode 100644 patches/server/0072-java-11.patch diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 247818a5..b47b5212 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '8', '11', '15', '16' ] + java: [ '11', '15', '16' ] fail-fast: false steps: - uses: actions/checkout@v2 diff --git a/Jenkinsfile b/Jenkinsfile index 01731e4e..47a3e562 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -9,7 +9,7 @@ pipeline { stages { stage('Cleanup') { tools { - jdk "OpenJDK 8" + jdk "OpenJDK 11" } steps { scmSkip(deleteBuild: true, skipPattern:'.*\\[CI-SKIP\\].*') @@ -29,7 +29,7 @@ pipeline { } stage('Init project & submodules') { tools { - jdk "OpenJDK 8" + jdk "OpenJDK 11" } steps { withMaven( @@ -43,7 +43,7 @@ pipeline { } stage('Decompile & apply patches') { tools { - jdk "OpenJDK 8" + jdk "OpenJDK 11" } steps { withMaven( @@ -60,7 +60,7 @@ pipeline { } stage('Build') { tools { - jdk "OpenJDK 8" + jdk "OpenJDK 11" } steps { withMaven( @@ -76,7 +76,8 @@ pipeline { paperworkdir="$basedir/Paper/work" mcver=$(cat "$paperworkdir/BuildData/info.json" | grep minecraftVersion | cut -d '"' -f 4) - patchedJarPath="$basedir/Yatopia-Server/build/libs/yatopia-server-$mcver-R0.1-SNAPSHOT.jar" + patchedJarPath=$(find "$basedir/Yatopia-Server/build/libs/" -type f -name "*.jar" | grep -v '\\-sources.jar$') + vanillaJarPath="$paperworkdir/Minecraft/$mcver/$mcver.jar" cd "$paperworkdir/Paperclip" diff --git a/PATCHES.md b/PATCHES.md index 0d2d8c96..e2ad6bda 100644 --- a/PATCHES.md +++ b/PATCHES.md @@ -105,6 +105,7 @@ # Patches | server | Breedable Polar Bears | William Blake Galbreath | | | api | Bring back server name | William Blake Galbreath | | | server | Bring back server name | William Blake Galbreath | | +| server | C2ME Port | ishland | Simon Gardling | | server | Cache climbing check for activation | Paul Sauve | | | server | Cache coordinate key for micro opt | Paul Sauve | | | server | Cache entityhuman display name | Paul Sauve | | @@ -229,6 +230,7 @@ # Patches | server | Fix the dead lagging the server | William Blake Galbreath | | | server | Fix vanilla command permission handler | William Blake Galbreath | | | server | Flying squids! Oh my! | William Blake Galbreath | | +| server | Force world save | ishland | | | api | Full netherite armor grants fire resistance | BillyGalbreath | | | server | Full netherite armor grants fire resistance | BillyGalbreath | | | server | Gamemode extra permissions | BillyGalbreath | | @@ -293,6 +295,7 @@ # Patches | server | Movement options for armor stands | Mariell Hoversholm | | | server | Multi-Threaded Server Ticking Vanilla | Spottedleaf | | | server | Multi-Threaded ticking CraftBukkit | Spottedleaf | | +| server | Multi-threaded World Upgrade | ishland | | | server | Name craft scheduler threads according to the plugin using | Spottedleaf | | | server | New nbt cache | Hugo Planque | ishland | | server | Nuke streams off BlockPosition | Ivan Pekov | | @@ -439,11 +442,14 @@ # Patches | server | Zombie horse naturally spawn | William Blake Galbreath | | | server | add config for logging login location | Simon Gardling | | | server | dont load chunks for physics | Aikar | | +| api | java 11 | Simon Gardling | | +| server | java 11 | Simon Gardling | | | server | lithium DataTrackerMixin | JellySquid | tr7zw | | server | lithium HashedList | JellySquid | | | server | lithium MixinBox | JellySquid | | | server | lithium MixinDirection | JellySquid | | | server | lithium MixinGoalSelector | JellySquid | | +| server | lithium MultiNoiseBiomeSourceMixin | ishland | | | server | lithium NoiseChunkGeneratorMixin | JellySquid | | | server | lithium PerlinNoiseSamplerMixin | JellySquid | Bud Gidiere | | server | lithium VoronoiBiomeAccessTypeMixin | JellySquid | | diff --git a/build.gradle.kts b/build.gradle.kts index 6de486f1..a9b9efdb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -59,10 +59,10 @@ subprojects { } java { - if(JavaVersion.VERSION_1_8 > JavaVersion.current()){ - error("This build must be run with Java 8 or better") + if(JavaVersion.VERSION_11 > JavaVersion.current()){ + error("This build must be run with Java 11 or later") } - sourceCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.current() withSourcesJar() } diff --git a/patches/api/0009-java-11.patch b/patches/api/0009-java-11.patch new file mode 100644 index 00000000..a1831efe --- /dev/null +++ b/patches/api/0009-java-11.patch @@ -0,0 +1,21 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Simon Gardling +Date: Fri, 23 Apr 2021 11:11:13 -0400 +Subject: [PATCH] java 11 + + +diff --git a/pom.xml b/pom.xml +index cc3149a51f5780b73c11492e13dbe7eb86d83e6c..6143478e7e4bfb12208b290b64eee0f95a84f445 100644 +--- a/pom.xml ++++ b/pom.xml +@@ -19,8 +19,8 @@ + + + +- 1.8 +- 1.8 ++ 11 ++ 11 + UTF-8 + 4.7.0 + diff --git a/patches/removed/server/0072-Multi-threaded-RegionFile-IO.patch b/patches/removed/server/0072-Multi-threaded-RegionFile-IO.patch new file mode 100644 index 00000000..daec76b4 --- /dev/null +++ b/patches/removed/server/0072-Multi-threaded-RegionFile-IO.patch @@ -0,0 +1,307 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: ishland +Date: Sun, 15 Nov 2020 10:42:27 +0800 +Subject: [PATCH] Multi-threaded RegionFile IO + + +diff --git a/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java b/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java +index 9fe91f9512ee8c2589fc8da76bda5f6d70c9fac4..8b81119cfdef81665a302d96cfada5bb43bc1077 100644 +--- a/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java ++++ b/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java +@@ -35,7 +35,7 @@ import java.util.function.Function; + * @see #scheduleSave(WorldServer, int, int, NBTTagCompound, NBTTagCompound, int) + * @see #loadChunkDataAsync(WorldServer, int, int, int, Consumer, boolean, boolean, boolean) + */ +-public final class PaperFileIOThread extends QueueExecutorThread { ++public final class PaperFileIOThread { // Yatopia + + public static final Logger LOGGER = MinecraftServer.LOGGER; + public static final NBTTagCompound FAILURE_VALUE = new NBTTagCompound(); +@@ -44,23 +44,84 @@ public final class PaperFileIOThread extends QueueExecutorThread { + + public static final PaperFileIOThread INSTANCE = new PaperFileIOThread(); + ++ /* Yatopia + static { + INSTANCE.start(); + } ++ */ + } + + private final AtomicLong writeCounter = new AtomicLong(); ++ // Yatopia start - multi-threaded RegionFile IO ++ private final com.ibm.asyncutil.locks.AsyncNamedLock regionFileLock = com.ibm.asyncutil.locks.AsyncNamedLock.createFair(); ++ private final PrioritizedTaskQueue queue; ++ private final PaperFileIOThread.FileIOExecutorThread receiver; ++ private final java.util.Set executorThreads = com.google.common.collect.Sets.newConcurrentHashSet(); ++ private final java.util.concurrent.ExecutorService executor = java.util.concurrent.Executors.newFixedThreadPool( ++ org.yatopiamc.yatopia.server.YatopiaConfig.regionFileIOThreadPoolSize == -1 ? Math.min(Runtime.getRuntime().availableProcessors(), Integer.getInteger("paper.maxChunkThreads", 8)) : org.yatopiamc.yatopia.server.YatopiaConfig.regionFileIOThreadPoolSize, ++ new com.google.common.util.concurrent.ThreadFactoryBuilder() ++ .setNameFormat("Paper RegionFile IO Worker #%d") ++ .setPriority(Thread.NORM_PRIORITY - 1) ++ .setDaemon(true) ++ .setThreadFactory(r -> { ++ Thread thr = new Thread(r); ++ executorThreads.add(thr); ++ return thr; ++ }) ++ .setUncaughtExceptionHandler((t, e) -> { ++ LOGGER.fatal("Uncaught exception thrown from " + t.getName() + ", report this!", e); ++ executorThreads.remove(t); ++ }) ++ .build() ++ ); + + private PaperFileIOThread() { +- super(new PrioritizedTaskQueue<>(), (int)(1.0e6)); // 1.0ms spinwait time +- this.setName("Paper RegionFile IO Thread"); +- this.setPriority(Thread.NORM_PRIORITY - 1); // we keep priority close to normal because threads can wait on us +- this.setUncaughtExceptionHandler((final Thread unused, final Throwable thr) -> { +- LOGGER.fatal("Uncaught exception thrown from IO thread, report this!", thr); ++ queue = new PrioritizedTaskQueue<>(); ++ receiver = new PaperFileIOThread.FileIOExecutorThread(queue, (int) (1.0e6)); // 1.0ms spinwait time ++ receiver.setName("Paper RegionFile IO Task Receiver"); ++ receiver.setPriority(Thread.NORM_PRIORITY - 1); // we keep priority close to normal because threads can wait on us ++ receiver.setUncaughtExceptionHandler((final Thread thread, final Throwable thr) -> { ++ LOGGER.fatal("Uncaught exception thrown from " + thread.getName() + ", report this!", thr); + }); ++ receiver.start(); ++ ++ } ++ ++ public void flush() { ++ receiver.flush(); ++ final java.util.Set> runningTasks = new java.util.HashSet<>(receiver.runningTasks); ++ LOGGER.debug("Flushing Chunk IO: Waiting for {} futures", runningTasks.size()); ++ for(CompletableFuture future: runningTasks) { ++ try { ++ future.join(); ++ } catch (Throwable ignored) { ++ } ++ } ++ } ++ ++ private void queueTask(PrioritizedTaskQueue.PrioritizedTask newTask) { ++ queue.add(newTask); ++ receiver.notifyTasks(); ++ } ++ ++ public void close(final boolean wait) { ++ receiver.close(wait, true); ++ this.flush(); ++ executor.shutdown(); ++ while (wait && !executor.isTerminated()) { ++ try { ++ executor.awaitTermination(30, java.util.concurrent.TimeUnit.SECONDS); ++ } catch (InterruptedException e) { ++ } ++ } ++ } ++ ++ @SuppressWarnings("BooleanMethodIsAlwaysInverted") ++ public boolean isOnWorkerThread() { ++ return executorThreads.contains(Thread.currentThread()); + } + +- /* run() is implemented by superclass */ ++ // Yatopia end + + /* + * +@@ -394,6 +455,85 @@ public final class PaperFileIOThread extends QueueExecutorThread { + this.queueTask(new GeneralTask(priority, runnable)); + } + ++ // Yatopia start ++ public static final class RegionFileCoord { ++ ++ public final int x; ++ public final int z; ++ ++ public RegionFileCoord(int x, int z) { ++ this.x = x; ++ this.z = z; ++ } ++ ++ @Override ++ public boolean equals(Object o) { ++ if (this == o) return true; ++ if (o == null || getClass() != o.getClass()) return false; ++ RegionFileCoord that = (RegionFileCoord) o; ++ return x == that.x && z == that.z; ++ } ++ ++ @Override ++ public int hashCode() { ++ return java.util.Objects.hash(x, z); ++ } ++ } ++ ++ final class FileIOExecutorThread extends QueueExecutorThread { ++ private final java.util.Set> runningTasks = com.google.common.collect.Sets.newConcurrentHashSet(); ++ ++ public FileIOExecutorThread(PrioritizedTaskQueue queue, long spinWaitTime) { ++ super(queue, spinWaitTime); ++ } ++ ++ @Override ++ protected void preMainLoop() { ++ runningTasks.removeIf(CompletableFuture::isDone); ++ } ++ ++ @Override ++ protected boolean pollTasks(boolean flushTasks) { ++ Runnable task; ++ boolean ret = false; ++ ++ while ((task = this.queue.poll()) != null) { ++ ret = true; ++ if (task instanceof ChunkDataTask) { ++ ChunkDataTask chunkDataTask = (ChunkDataTask) task; ++ runningTasks.add(regionFileLock.acquireLock(new RegionFileCoord(chunkDataTask.x >> 5, chunkDataTask.z >> 5)) ++ .thenApplyAsync(lockToken -> { ++ try { ++ chunkDataTask.run(); ++ } finally { ++ lockToken.releaseLock(); ++ } ++ return null; ++ }, executor) ++ .exceptionally(throwable -> { ++ LOGGER.fatal("Exception thrown from prioritized runnable task in thread '" + Thread.currentThread().getName() + "': " + IOUtil.genericToString(chunkDataTask), throwable); ++ return null; ++ }).toCompletableFuture()); ++ } else { ++ Runnable finalTask = task; ++ runningTasks.add(CompletableFuture.supplyAsync(() -> { ++ finalTask.run(); ++ return null; ++ }).exceptionally(throwable -> { ++ LOGGER.fatal("Exception thrown from prioritized runnable task in thread '" + Thread.currentThread().getName() + "': " + IOUtil.genericToString(finalTask), throwable); ++ return null; ++ })); ++ } ++ } ++ ++ if (flushTasks) { ++ this.handleFlushThreads(false); ++ } ++ ++ return ret; ++ } ++ } ++ // Yatopia end + static final class GeneralTask extends PrioritizedTaskQueue.PrioritizedTask implements Runnable { + + private final Runnable run; +diff --git a/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java b/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java +index ee906b594b306906c170180a29a8b61997d05168..7e348fcb813707fee830082b826932e0bbba1c49 100644 +--- a/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java ++++ b/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java +@@ -35,6 +35,7 @@ public class QueueExecutorThread { + @javax.annotation.Nullable + @Override + public NBTTagCompound read(ChunkCoordIntPair chunkcoordintpair) throws java.io.IOException { +- if (this.world != null && Thread.currentThread() != com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE) { ++ if (this.world != null && !com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.isOnWorkerThread()) { // Yatopia + NBTTagCompound ret = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE + .loadChunkDataAsyncFuture(this.world, chunkcoordintpair.x, chunkcoordintpair.z, com.destroystokyo.paper.io.IOUtil.getPriorityForCurrentThread(), + true, false, true).join().poiData; +@@ -519,7 +519,7 @@ public class VillagePlace extends RegionFileSection { + + @Override + public void write(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) throws java.io.IOException { +- if (this.world != null && Thread.currentThread() != com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE) { ++ if (this.world != null && !com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.isOnWorkerThread()) { // Yatopia + com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave( + this.world, chunkcoordintpair.x, chunkcoordintpair.z, nbttagcompound, null, + com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY); // Tuinity - writes are async, no need for priority +diff --git a/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java b/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java +index 545bf94bf47f0c6e9da1a4c162e081cbb2cd8390..e8e8e1fed06f0c17631a134e3673a25549d7c86c 100644 +--- a/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java ++++ b/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java +@@ -16,6 +16,7 @@ import org.bukkit.Bukkit; + import org.bukkit.command.Command; + import org.bukkit.configuration.InvalidConfigurationException; + import org.bukkit.configuration.file.YamlConfiguration; ++import com.google.common.base.Preconditions; + + public class YatopiaConfig { + +@@ -282,4 +283,10 @@ public class YatopiaConfig { + allowThreadedFeatures = getBoolean("settings.c2me.allow-threaded-features", allowThreadedFeatures); + c2meThreads = getInt("settings.c2me.parallelism", c2meThreads); + } ++ ++ public static int regionFileIOThreadPoolSize = -1; ++ private static void multiThreadedRegionFile() { ++ regionFileIOThreadPoolSize = getInt("settings.threads.regionfile", -1); ++ Preconditions.checkArgument(regionFileIOThreadPoolSize == -1 || regionFileIOThreadPoolSize > 0, "Invalid settings.threads.regionfile in yatopia.yml"); ++ } + } diff --git a/patches/server/0068-C2ME-Port.patch b/patches/server/0068-C2ME-Port.patch new file mode 100644 index 00000000..a47b2877 --- /dev/null +++ b/patches/server/0068-C2ME-Port.patch @@ -0,0 +1,653 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: ishland +Date: Thu, 22 Apr 2021 17:56:12 -0400 +Subject: [PATCH] C2ME Port + +Port of https://github.com/YatopiaMC/C2ME-fabric + +Co-authored-by: Simon Gardling + +diff --git a/pom.xml b/pom.xml +index d0259f18488e1ecf0276865e0ff7958726a40033..f0a73238612327d71cf78801df816823d80893a0 100644 +--- a/pom.xml ++++ b/pom.xml +@@ -203,6 +203,12 @@ + commons-rng-core + 1.3 + ++ ++ ++ com.ibm.async ++ asyncutil ++ 0.1.0 ++ + + + +diff --git a/src/main/java/net/minecraft/server/level/PlayerChunk.java b/src/main/java/net/minecraft/server/level/PlayerChunk.java +index 06157bb07cce3ba24087ceaca7138b5609b37b5b..47f0604a891d46f688abd5daa6fb4de8b56305e3 100644 +--- a/src/main/java/net/minecraft/server/level/PlayerChunk.java ++++ b/src/main/java/net/minecraft/server/level/PlayerChunk.java +@@ -374,6 +374,7 @@ public class PlayerChunk { + return either == null ? null : (Chunk) either.left().orElse(null); // CraftBukkit - decompile error + } + ++ @Nullable public IChunkAccess getCurrentChunk() { return this.f(); } // Yatopia - OBFHELPER + @Nullable + public IChunkAccess f() { + for (int i = PlayerChunk.CHUNK_STATUSES.size() - 1; i >= 0; --i) { +diff --git a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java +index e25476f6a9477447cc06f24ed05679326e83cd95..b77793771e8e264e90e09399e10246308ba1cb52 100644 +--- a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java +@@ -154,8 +154,9 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + public final LongSet unloadQueue; + private boolean updatingChunksModified; + private final ChunkTaskQueueSorter p; +- private final Mailbox> mailboxWorldGen; ++ // private final Mailbox> mailboxWorldGen; // Yatopia + public final Mailbox> mailboxMain; // Paper - private -> public ++ private final ThreadLocal capturedRequiredStatus = new ThreadLocal<>(); // Yatopia + // Paper start + final Mailbox> mailboxLight; + public void addLightTask(PlayerChunk playerchunk, Runnable run) { +@@ -461,7 +462,7 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + // Paper end + + this.p = new ChunkTaskQueueSorter(ImmutableList.of(threadedmailbox, mailbox, threadedmailbox1), executor, Integer.MAX_VALUE); +- this.mailboxWorldGen = this.p.a(threadedmailbox, false); ++ // this.mailboxWorldGen = this.p.a(threadedmailbox, false); // Yatopia + this.mailboxMain = this.p.a(mailbox, false); + this.mailboxLight = this.p.a(lightthreaded, false);// Paper + this.lightEngine = new LightEngineThreaded(ilightaccess, this, this.world.getDimensionManager().hasSkyLight(), threadedmailbox1, this.p.a(threadedmailbox1, false)); +@@ -1334,7 +1335,8 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + return this.z.put(chunkcoordintpair.pair(), (byte) (chunkstatus_type == ChunkStatus.Type.PROTOCHUNK ? -1 : 1)); + } + +- private CompletableFuture> b(PlayerChunk playerchunk, ChunkStatus chunkstatus) { ++ private CompletableFuture> b(PlayerChunk playerchunk, ChunkStatus chunkstatus) { // Yarn: upgradeChunk ++ this.capturedRequiredStatus.set(chunkstatus); // Yatopia - C2ME port + ChunkCoordIntPair chunkcoordintpair = playerchunk.i(); + CompletableFuture, PlayerChunk.Failure>> completablefuture = this.a(chunkcoordintpair, chunkstatus.f(), (i) -> { + return this.a(chunkstatus, i); +@@ -1372,7 +1374,11 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { + return; + } + // Paper end +- this.mailboxWorldGen.a(ChunkTaskQueueSorter.a(playerchunk, runnable)); ++ ++ // Yatopia start - C2ME port ++ org.yatopiamc.c2me.common.threading.GlobalExecutors.scheduler.execute(runnable); ++ // this.mailboxWorldGen.a(ChunkTaskQueueSorter.a(playerchunk, runnable)); // Yatopia ++ // Yatopia end + }).thenComposeAsync((either) -> { // Tuinity start - force competion on the main thread + return CompletableFuture.completedFuture(either); + }, this.mainInvokingExecutor); +diff --git a/src/main/java/net/minecraft/server/level/WorldServer.java b/src/main/java/net/minecraft/server/level/WorldServer.java +index 3068b307b7ad64f5add188e956d59a52126f332a..8892b54c85db83e3f5ad1ae26d3f23de754564f2 100644 +--- a/src/main/java/net/minecraft/server/level/WorldServer.java ++++ b/src/main/java/net/minecraft/server/level/WorldServer.java +@@ -178,7 +178,17 @@ import org.bukkit.event.world.TimeSkipEvent; + import it.unimi.dsi.fastutil.ints.IntArrayList; // Tuinity + import net.gegy1000.tictacs.NonBlockingWorldAccess; // Yatopia + +-public class WorldServer extends World implements GeneratorAccessSeed, NonBlockingWorldAccess { // Yatopia ++// Yatopia start ++import org.yatopiamc.c2me.common.threading.worldgen.IWorldGenLockable; ++import com.ibm.asyncutil.locks.AsyncLock; ++import com.ibm.asyncutil.locks.AsyncNamedLock; ++// Yatopia end ++ ++public class WorldServer extends World implements GeneratorAccessSeed, NonBlockingWorldAccess, IWorldGenLockable { // Yatopia // Yatopia - port C2ME ++ ++ private volatile AsyncLock worldGenSingleThreadedLock = null; // Yatopia - port C2ME ++ private volatile AsyncNamedLock worldGenChunkLock = null; ++ // Yatopia - port C2ME + + public static final BlockPosition a = new BlockPosition(100, 50, 0); + private static final Logger LOGGER = LogManager.getLogger(); +@@ -608,7 +618,23 @@ public class WorldServer extends World implements GeneratorAccessSeed, NonBlocki + + this.asyncChunkTaskManager = new com.destroystokyo.paper.io.chunk.ChunkTaskManager(this); // Paper + this.fakeTime = this.worldDataServer.getDayTime(); // Purpur ++ // Yatopia start - port C2ME ++ this.worldGenSingleThreadedLock = AsyncLock.createFair(); ++ this.worldGenChunkLock = AsyncNamedLock.createFair(); ++ // Yatopia end - port C2ME ++ } ++ ++ // Yatopia start - port C2ME ++ @Override ++ public AsyncLock getWorldGenSingleThreadedLock() { ++ return this.worldGenSingleThreadedLock; ++ } ++ ++ @Override ++ public AsyncNamedLock getWorldGenChunkLock() { ++ return this.worldGenChunkLock; + } ++ // Yatopia end - port C2ME + + // Tuinity start - optimise collision + public boolean collidesWithAnyBlockOrWorldBorder(@Nullable Entity entity, AxisAlignedBB axisalignedbb, boolean loadChunks, +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java b/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java +index e2b5d6155bebdbf99b0850de7f9e1f5d342f9e2f..30db0ba3674a85c8dd866fab94c5374ba203c5cd 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/WeightedList.java +@@ -14,7 +14,7 @@ import java.util.stream.Stream; + + public class WeightedList { + +- protected final List> list; public final List> getList() { return this.list; } // Paper - decompile conflict // Tuinity - OBFHELPER ++ public final List> list; public final List> getList() { return this.list; } // Paper - decompile conflict // Tuinity - OBFHELPER // Yatopia - protected -> public + private final Random b; + private final boolean isUnsafe; // Paper + +diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java +index f4a4d63a2e21b08580023cf0dcd15a68d192cf14..1802498d48493d3e63c999a067c71e65ea29a890 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java ++++ b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java +@@ -22,6 +22,9 @@ import net.minecraft.world.level.levelgen.HeightMap; + import net.minecraft.world.level.levelgen.WorldGenStage; + import net.minecraft.world.level.levelgen.structure.templatesystem.DefinedStructureManager; + import net.minecraft.world.level.lighting.LightEngine; ++import org.yatopiamc.c2me.common.threading.worldgen.ChunkStatusUtils; // Yatopia ++import org.yatopiamc.c2me.common.threading.worldgen.IWorldGenLockable; // Yatopia ++import java.util.function.Supplier; // Yatopia + + public class ChunkStatus { + +@@ -162,6 +165,7 @@ public class ChunkStatus { + return ichunkaccess.getChunkStatus().b(chunkstatus) && ichunkaccess.r(); + } + ++ public static ChunkStatus byDistanceFromFull(int level) { return a(level); } // Yatopia - OBFHELPER + public static ChunkStatus a(int i) { + return i >= ChunkStatus.q.size() ? ChunkStatus.EMPTY : (i < 0 ? ChunkStatus.FULL : (ChunkStatus) ChunkStatus.q.get(i)); + } +@@ -186,6 +190,14 @@ public class ChunkStatus { + this.t = chunkstatus == null ? 0 : chunkstatus.c() + 1; + } + ++ static { ++ // Yatopia start - C2ME port ++ for (ChunkStatus chunkStatus : IRegistry.CHUNK_STATUS) { ++ chunkStatus.calculateReducedTaskRadius(); ++ } ++ // Yatopia end ++ } ++ + public final int getStatusIndex() { return c(); } // Paper - OBFHELPER + public int c() { + return this.t; +@@ -200,8 +212,44 @@ public class ChunkStatus { + return this.u; + } + ++ // Yatopia start - C2ME port ++ private int reducedTaskRadius = -1; ++ ++ public void calculateReducedTaskRadius() { ++ if (this.getNeighborRadius() == 0) { ++ this.reducedTaskRadius = 0; ++ } else { ++ for (int i = 0; i <= this.getNeighborRadius(); i++) { ++ final ChunkStatus status = ChunkStatus.byDistanceFromFull(ChunkStatus.getTicketLevelOffset(this) + i); // TODO [VanillaCopy] from TACS getRequiredStatusForGeneration ++ if (status == ChunkStatus.STRUCTURE_STARTS) { ++ this.reducedTaskRadius = Math.min(this.getNeighborRadius(), i); ++ break; ++ } ++ } ++ } ++ //noinspection ConstantConditions ++ if ((Object) this == ChunkStatus.LIGHT) { ++ this.reducedTaskRadius = 1; ++ } ++ System.out.println(String.format("%s task radius: %d -> %d", this, this.getNeighborRadius(), this.reducedTaskRadius)); ++ } ++ // Yatopia end ++ + public CompletableFuture> a(WorldServer worldserver, ChunkGenerator chunkgenerator, DefinedStructureManager definedstructuremanager, LightEngineThreaded lightenginethreaded, Function>> function, List list) { +- return this.v.doWork(this, worldserver, chunkgenerator, definedstructuremanager, lightenginethreaded, function, list, (IChunkAccess) list.get(list.size() / 2)); ++ // Yatopia start - port C2ME ++ final IChunkAccess targetChunk = (IChunkAccess) list.get(list.size() / 2); ++ final Supplier>> generationTask = () -> ++ this.v.doWork(this, worldserver, chunkgenerator, definedstructuremanager, lightenginethreaded, function, list, targetChunk); ++ ++ if (targetChunk.getChunkStatus().isAtLeastStatus((ChunkStatus) (Object) this)) { ++ return generationTask.get(); ++ } else { ++ int lockRadius = org.yatopiamc.yatopia.server.YatopiaConfig.reduceLockRadius && this.reducedTaskRadius != -1 ? this.reducedTaskRadius : this.getNeighborRadius(); ++ //noinspection ConstantConditions ++ return ChunkStatusUtils.runChunkGenWithLock(targetChunk.getPos(), lockRadius, ((IWorldGenLockable) worldserver).getWorldGenChunkLock(), () -> ++ ChunkStatusUtils.getThreadingType((ChunkStatus) (Object) this).runTask(((IWorldGenLockable) worldserver).getWorldGenSingleThreadedLock(), generationTask)); ++ } ++ // Yatopia end + } + + public CompletableFuture> a(WorldServer worldserver, DefinedStructureManager definedstructuremanager, LightEngineThreaded lightenginethreaded, Function>> function, IChunkAccess ichunkaccess) { +diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/DefinedStructure.java b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/DefinedStructure.java +index 13983f3271d33ab6e4c7030de5865edbd7b0cd8a..7460f5c85800f0d3c6076bc944b10b5931ba22bf 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/DefinedStructure.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/structure/templatesystem/DefinedStructure.java +@@ -843,7 +843,7 @@ public class DefinedStructure { + private final Map> b; + + private a(List list) { +- this.b = Maps.newHashMap(); ++ this.b = new java.util.concurrent.ConcurrentHashMap<>(); // Yatopia - port C2ME + this.a = list; + } + +diff --git a/src/main/java/net/minecraft/world/level/newbiome/layer/GenLayers.java b/src/main/java/net/minecraft/world/level/newbiome/layer/GenLayers.java +index 5bbd71f2cf6db34dd01e8e209809a4661505aaf1..76995e812492d3fd0f9180525727174bf3d8c409 100644 +--- a/src/main/java/net/minecraft/world/level/newbiome/layer/GenLayers.java ++++ b/src/main/java/net/minecraft/world/level/newbiome/layer/GenLayers.java +@@ -13,7 +13,7 @@ import net.minecraft.world.level.newbiome.layer.traits.AreaTransformer2; + + public class GenLayers { + +- private static final Int2IntMap a = (Int2IntMap) SystemUtils.a((Object) (new Int2IntOpenHashMap()), (int2intopenhashmap) -> { ++ private static final Int2IntMap a = (Int2IntMap) SystemUtils.a((new Int2IntOpenHashMap()), (int2intopenhashmap) -> { // Yatopia - decompile fixes + a(int2intopenhashmap, GenLayers.Type.BEACH, 16); + a(int2intopenhashmap, GenLayers.Type.BEACH, 26); + a(int2intopenhashmap, GenLayers.Type.DESERT, 2); +@@ -154,9 +154,9 @@ public class GenLayers { + + public static GenLayer a(long i, boolean flag, int j, int k) { + boolean flag1 = true; +- AreaFactory areafactory = a(flag, j, k, (l) -> { ++ AreaFactory areafactory = () -> a(flag, j, k, (l) -> { // Yatopia + return new WorldGenContextArea(25, i, l); +- }); ++ }).make(); // Yatopia + + return new GenLayer(areafactory); + } +diff --git a/src/main/java/org/yatopiamc/c2me/common/threading/GlobalExecutors.java b/src/main/java/org/yatopiamc/c2me/common/threading/GlobalExecutors.java +new file mode 100644 +index 0000000000000000000000000000000000000000..dcf55bc98818f98c1a7b6869306a40c11b842cdf +--- /dev/null ++++ b/src/main/java/org/yatopiamc/c2me/common/threading/GlobalExecutors.java +@@ -0,0 +1,25 @@ ++package org.yatopiamc.c2me.common.threading; ++ ++import com.google.common.util.concurrent.ThreadFactoryBuilder; ++ ++import java.util.concurrent.ScheduledThreadPoolExecutor; ++import java.util.concurrent.atomic.AtomicReference; ++ ++public class GlobalExecutors { ++ ++ public static final ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor( ++ 1, ++ new ThreadFactoryBuilder().setNameFormat("C2ME scheduler").setDaemon(true).setPriority(Thread.NORM_PRIORITY - 1).setThreadFactory(r -> { ++ final Thread thread = new Thread(r); ++ GlobalExecutors.schedulerThread.set(thread); ++ return thread; ++ }).build() ++ ); ++ private static final AtomicReference schedulerThread = new AtomicReference<>(); ++ ++ public static void ensureSchedulerThread() { ++ if (Thread.currentThread() != schedulerThread.get()) ++ throw new IllegalStateException("Not on scheduler thread"); ++ } ++ ++} +\ No newline at end of file +diff --git a/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/ChunkStatusThreadingType.java b/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/ChunkStatusThreadingType.java +new file mode 100644 +index 0000000000000000000000000000000000000000..5af95799cb4cd380f25a31f50f67a2bcb9c1bec5 +--- /dev/null ++++ b/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/ChunkStatusThreadingType.java +@@ -0,0 +1,45 @@ ++ ++package org.yatopiamc.c2me.common.threading.worldgen; ++ ++import com.google.common.base.Preconditions; ++import com.ibm.asyncutil.locks.AsyncLock; ++import com.mojang.datafixers.util.Either; ++ ++import net.minecraft.world.level.chunk.IChunkAccess; ++import net.minecraft.server.level.PlayerChunk; ++ ++import java.util.concurrent.CompletableFuture; ++import java.util.function.Function; ++import java.util.function.Supplier; ++ ++public enum ChunkStatusThreadingType { ++ ++ PARALLELIZED() { ++ @Override ++ public CompletableFuture> runTask(AsyncLock lock, Supplier>> completableFuture) { ++ return CompletableFuture.supplyAsync(completableFuture, WorldGenThreadingExecutorUtils.mainExecutor).thenCompose(Function.identity()); ++ } ++ }, ++ SINGLE_THREADED() { ++ @Override ++ public CompletableFuture> runTask(AsyncLock lock, Supplier>> completableFuture) { ++ Preconditions.checkNotNull(lock); ++ return lock.acquireLock().toCompletableFuture().thenComposeAsync(lockToken -> { ++ try { ++ return completableFuture.get(); ++ } finally { ++ lockToken.releaseLock(); ++ } ++ }, WorldGenThreadingExecutorUtils.mainExecutor); ++ } ++ }, ++ AS_IS() { ++ @Override ++ public CompletableFuture> runTask(AsyncLock lock, Supplier>> completableFuture) { ++ return completableFuture.get(); ++ } ++ }; ++ ++ public abstract CompletableFuture> runTask(AsyncLock lock, Supplier>> completableFuture); ++ ++} +\ No newline at end of file +diff --git a/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/ChunkStatusUtils.java b/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/ChunkStatusUtils.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0c28024cf9a50f35e1a867188b2a3f8fbbccb3ce +--- /dev/null ++++ b/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/ChunkStatusUtils.java +@@ -0,0 +1,59 @@ ++package org.yatopiamc.c2me.common.threading.worldgen; ++ ++import com.ibm.asyncutil.locks.AsyncLock; ++import com.ibm.asyncutil.locks.AsyncNamedLock; ++import org.yatopiamc.c2me.common.threading.GlobalExecutors; ++import org.yatopiamc.c2me.common.util.AsyncCombinedLock; ++import org.yatopiamc.c2me.common.util.AsyncNamedLockDelegateAsyncLock; ++ ++import net.minecraft.world.level.chunk.ChunkStatus; ++import net.minecraft.world.level.ChunkCoordIntPair; ++ ++import java.util.ArrayList; ++import java.util.HashSet; ++import java.util.List; ++import java.util.concurrent.CompletableFuture; ++import java.util.function.Function; ++import java.util.function.Supplier; ++ ++import static org.yatopiamc.c2me.common.threading.worldgen.ChunkStatusThreadingType.AS_IS; ++import static org.yatopiamc.c2me.common.threading.worldgen.ChunkStatusThreadingType.PARALLELIZED; ++import static org.yatopiamc.c2me.common.threading.worldgen.ChunkStatusThreadingType.SINGLE_THREADED; ++import org.yatopiamc.yatopia.server.YatopiaConfig; ++ ++public class ChunkStatusUtils { ++ ++ public static ChunkStatusThreadingType getThreadingType(final ChunkStatus status) { ++ if (status.equals(ChunkStatus.STRUCTURE_STARTS) ++ || status.equals(ChunkStatus.STRUCTURE_REFERENCES) ++ || status.equals(ChunkStatus.BIOMES) ++ || status.equals(ChunkStatus.NOISE) ++ || status.equals(ChunkStatus.SURFACE) ++ || status.equals(ChunkStatus.CARVERS) ++ || status.equals(ChunkStatus.LIQUID_CARVERS) ++ || status.equals(ChunkStatus.HEIGHTMAPS)) { ++ return PARALLELIZED; ++ } else if (status.equals(ChunkStatus.SPAWN)) { ++ return SINGLE_THREADED; ++ } else if (status.equals(ChunkStatus.FEATURES)) { ++ return YatopiaConfig.allowThreadedFeatures ? PARALLELIZED : SINGLE_THREADED; ++ } ++ return AS_IS; ++ } ++ ++ public static CompletableFuture runChunkGenWithLock(ChunkCoordIntPair target, int radius, AsyncNamedLock chunkLock, Supplier> action) { ++ return CompletableFuture.supplyAsync(() -> { ++ ArrayList fetchedLocks = new ArrayList<>((2 * radius + 1) * (2 * radius + 1)); ++ for (int x = target.x - radius; x <= target.x + radius; x++) ++ for (int z = target.z - radius; z <= target.z + radius; z++) ++ fetchedLocks.add(new ChunkCoordIntPair(x, z)); ++ ++ return new AsyncCombinedLock(chunkLock, new HashSet<>(fetchedLocks)).getFuture().thenComposeAsync(lockToken -> { ++ final CompletableFuture future = action.get(); ++ future.thenRun(lockToken::releaseLock); ++ return future; ++ }, GlobalExecutors.scheduler); ++ }, AsyncCombinedLock.lockWorker).thenCompose(Function.identity()); ++ } ++ ++} +\ No newline at end of file +diff --git a/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/IWorldGenLockable.java b/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/IWorldGenLockable.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b80923bcda9045968e0fad39f2e40b99dba135dc +--- /dev/null ++++ b/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/IWorldGenLockable.java +@@ -0,0 +1,13 @@ ++package org.yatopiamc.c2me.common.threading.worldgen; ++ ++import com.ibm.asyncutil.locks.AsyncLock; ++import com.ibm.asyncutil.locks.AsyncNamedLock; ++import net.minecraft.world.level.ChunkCoordIntPair; ++ ++public interface IWorldGenLockable { ++ ++ AsyncLock getWorldGenSingleThreadedLock(); ++ ++ AsyncNamedLock getWorldGenChunkLock(); ++ ++} +\ No newline at end of file +diff --git a/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/WorldGenThreadingExecutorUtils.java b/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/WorldGenThreadingExecutorUtils.java +new file mode 100644 +index 0000000000000000000000000000000000000000..215010825a18881f84d94ead66314b946d46d75b +--- /dev/null ++++ b/src/main/java/org/yatopiamc/c2me/common/threading/worldgen/WorldGenThreadingExecutorUtils.java +@@ -0,0 +1,17 @@ ++package org.yatopiamc.c2me.common.threading.worldgen; ++ ++import org.yatopiamc.c2me.common.util.C2MEForkJoinWorkerThreadFactory; ++ ++import java.util.concurrent.ForkJoinPool; ++import org.yatopiamc.yatopia.server.YatopiaConfig; ++ ++public class WorldGenThreadingExecutorUtils { ++ ++ public static final ForkJoinPool mainExecutor = new ForkJoinPool( ++ YatopiaConfig.c2meThreads, ++ new C2MEForkJoinWorkerThreadFactory("C2ME worldgen worker #%d", Thread.NORM_PRIORITY - 1), ++ null, ++ true ++ ); ++ ++} +\ No newline at end of file +diff --git a/src/main/java/org/yatopiamc/c2me/common/util/AsyncCombinedLock.java b/src/main/java/org/yatopiamc/c2me/common/util/AsyncCombinedLock.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9e34b74d9abecae4b386d49514ceb0d1f333e271 +--- /dev/null ++++ b/src/main/java/org/yatopiamc/c2me/common/util/AsyncCombinedLock.java +@@ -0,0 +1,88 @@ ++package org.yatopiamc.c2me.common.util; ++ ++import com.google.common.collect.Sets; ++import com.ibm.asyncutil.locks.AsyncLock; ++import com.ibm.asyncutil.locks.AsyncNamedLock; ++import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; ++import net.minecraft.world.level.ChunkCoordIntPair; ++ ++import java.util.Optional; ++import java.util.Set; ++import java.util.concurrent.CompletableFuture; ++import java.util.concurrent.ForkJoinPool; ++import java.util.function.Function; ++import java.util.stream.Collectors; ++ ++public class AsyncCombinedLock { ++ ++ public static final ForkJoinPool lockWorker = new ForkJoinPool( ++ 2, ++ new C2MEForkJoinWorkerThreadFactory("C2ME lock worker #%d", Thread.NORM_PRIORITY - 1), ++ null, ++ true ++ ); ++ ++ private final AsyncNamedLock lock; ++ private final ChunkCoordIntPair[] names; ++ private final CompletableFuture future = new CompletableFuture<>(); ++ ++ public AsyncCombinedLock(AsyncNamedLock lock, Set names) { ++ this.lock = lock; ++ this.names = names.toArray(ChunkCoordIntPair[]::new); ++ lockWorker.execute(this::tryAcquire); ++ } ++ ++ private synchronized void tryAcquire() { // TODO optimize logic further ++ final LockEntry[] tryLocks = new LockEntry[names.length]; ++ boolean allAcquired = true; ++ for (int i = 0, namesLength = names.length; i < namesLength; i++) { ++ ChunkCoordIntPair name = names[i]; ++ final LockEntry entry = new LockEntry(name, this.lock.tryLock(name)); ++ tryLocks[i] = entry; ++ if (entry.lockToken.isEmpty()) { ++ allAcquired = false; ++ break; ++ } ++ } ++ if (allAcquired) { ++ future.complete(() -> { ++ for (LockEntry entry : tryLocks) { ++ //noinspection OptionalGetWithoutIsPresent ++ entry.lockToken.get().releaseLock(); // if it isn't present then something is really wrong ++ } ++ }); ++ } else { ++ boolean triedRelock = false; ++ for (LockEntry entry : tryLocks) { ++ if (entry == null) continue; ++ entry.lockToken.ifPresent(AsyncLock.LockToken::releaseLock); ++ if (!triedRelock && entry.lockToken.isEmpty()) { ++ this.lock.acquireLock(entry.name).thenCompose(lockToken -> { ++ lockToken.releaseLock(); ++ return CompletableFuture.runAsync(this::tryAcquire, lockWorker); ++ }); ++ triedRelock = true; ++ } ++ } ++ if (!triedRelock) { ++ // shouldn't happen at all... ++ lockWorker.execute(this::tryAcquire); ++ } ++ } ++ } ++ ++ public CompletableFuture getFuture() { ++ return future.thenApply(Function.identity()); ++ } ++ ++ @SuppressWarnings("OptionalUsedAsFieldOrParameterType") ++ private static class LockEntry { ++ public final ChunkCoordIntPair name; ++ public final Optional lockToken; ++ ++ private LockEntry(ChunkCoordIntPair name, Optional lockToken) { ++ this.name = name; ++ this.lockToken = lockToken; ++ } ++ } ++} +diff --git a/src/main/java/org/yatopiamc/c2me/common/util/AsyncNamedLockDelegateAsyncLock.java b/src/main/java/org/yatopiamc/c2me/common/util/AsyncNamedLockDelegateAsyncLock.java +new file mode 100644 +index 0000000000000000000000000000000000000000..119421953de58fbc928e14bf618b340ee6b2fe94 +--- /dev/null ++++ b/src/main/java/org/yatopiamc/c2me/common/util/AsyncNamedLockDelegateAsyncLock.java +@@ -0,0 +1,29 @@ ++package org.yatopiamc.c2me.common.util; ++ ++import com.ibm.asyncutil.locks.AsyncLock; ++import com.ibm.asyncutil.locks.AsyncNamedLock; ++ ++import java.util.Objects; ++import java.util.Optional; ++import java.util.concurrent.CompletionStage; ++ ++public class AsyncNamedLockDelegateAsyncLock implements AsyncLock { ++ ++ private final AsyncNamedLock delegate; ++ private final T name; ++ ++ public AsyncNamedLockDelegateAsyncLock(AsyncNamedLock delegate, T name) { ++ this.delegate = Objects.requireNonNull(delegate); ++ this.name = name; ++ } ++ ++ @Override ++ public CompletionStage acquireLock() { ++ return delegate.acquireLock(name); ++ } ++ ++ @Override ++ public Optional tryLock() { ++ return delegate.tryLock(name); ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/org/yatopiamc/c2me/common/util/C2MEForkJoinWorkerThreadFactory.java b/src/main/java/org/yatopiamc/c2me/common/util/C2MEForkJoinWorkerThreadFactory.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ab5b9be9dcf67bdd9237fb7d21574155c2d52306 +--- /dev/null ++++ b/src/main/java/org/yatopiamc/c2me/common/util/C2MEForkJoinWorkerThreadFactory.java +@@ -0,0 +1,39 @@ ++package org.yatopiamc.c2me.common.util; ++ ++import java.util.concurrent.ForkJoinPool; ++import java.util.concurrent.ForkJoinWorkerThread; ++import java.util.concurrent.atomic.AtomicLong; ++ ++public class C2MEForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { ++ private final AtomicLong serial = new AtomicLong(0); ++ private final String namePattern; ++ private final int priority; ++ ++ public C2MEForkJoinWorkerThreadFactory(String namePattern, int priority) { ++ this.namePattern = namePattern; ++ this.priority = priority; ++ } ++ ++ @Override ++ public ForkJoinWorkerThread newThread(ForkJoinPool pool) { ++ final C2MEForkJoinWorkerThread C2MEForkJoinWorkerThread = new C2MEForkJoinWorkerThread(pool); ++ C2MEForkJoinWorkerThread.setName(String.format(namePattern, serial.incrementAndGet())); ++ C2MEForkJoinWorkerThread.setPriority(priority); ++ C2MEForkJoinWorkerThread.setDaemon(true); ++ return C2MEForkJoinWorkerThread; ++ } ++ ++ private static class C2MEForkJoinWorkerThread extends ForkJoinWorkerThread { ++ ++ /** ++ * Creates a ForkJoinWorkerThread operating in the given pool. ++ * ++ * @param pool the pool this thread works in ++ * @throws NullPointerException if pool is null ++ */ ++ protected C2MEForkJoinWorkerThread(ForkJoinPool pool) { ++ super(pool); ++ } ++ ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java b/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java +index fce7ce0efca340cf5820cdcbe010c9fdeae7cafc..1d1717d72ceb56594bc29f8a14437b61f911f817 100644 +--- a/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java ++++ b/src/main/java/org/yatopiamc/yatopia/server/YatopiaConfig.java +@@ -271,4 +271,12 @@ public class YatopiaConfig { + fixProtocolLib = getBoolean("settings.fix-protocollib", fixProtocolLib); + } + ++ public static boolean allowThreadedFeatures = false; ++ public static int c2meThreads = Math.min(6, Runtime.getRuntime().availableProcessors()); ++ public static boolean reduceLockRadius = false; ++ private static void c2me() { ++ allowThreadedFeatures = getBoolean("settings.c2me.allow-threaded-features", allowThreadedFeatures); ++ c2meThreads = getInt("settings.c2me.parallelism", c2meThreads); ++ reduceLockRadius = getBoolean("settings.c2me.reduce-lock-radius", reduceLockRadius); ++ } + } diff --git a/patches/server/0069-Multi-threaded-World-Upgrade.patch b/patches/server/0069-Multi-threaded-World-Upgrade.patch new file mode 100644 index 00000000..13ad1080 --- /dev/null +++ b/patches/server/0069-Multi-threaded-World-Upgrade.patch @@ -0,0 +1,144 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: ishland +Date: Fri, 5 Feb 2021 19:34:00 +0800 +Subject: [PATCH] Multi-threaded World Upgrade + + +diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java +index 6818f8496ab76ee6ffc747bd6848b43830ec8914..1fe2610ed0ddab1e203e273914c9325aec74e7c2 100644 +--- a/src/main/java/net/minecraft/server/Main.java ++++ b/src/main/java/net/minecraft/server/Main.java +@@ -281,6 +281,8 @@ public class Main { + WorldUpgrader worldupgrader = new WorldUpgrader(convertable_conversionsession, datafixer, immutableset, flag); + IChatBaseComponent ichatbasecomponent = null; + ++ long lastLocation = 0L; // Yatopia ++ long lastTime = System.nanoTime(); // Yatopia + while (!worldupgrader.b()) { + IChatBaseComponent ichatbasecomponent1 = worldupgrader.h(); + +@@ -289,13 +291,16 @@ public class Main { + Main.LOGGER.info(worldupgrader.h().getString()); + } + +- int i = worldupgrader.e(); +- +- if (i > 0) { +- int j = worldupgrader.f() + worldupgrader.g(); +- +- Main.LOGGER.info("{}% completed ({} / {} chunks)...", MathHelper.d((float) j / (float) i * 100.0F), j, i); +- } ++ // Yatopia start ++ long totalChunkCount = worldupgrader.getTotalChunkCount(); ++ if (totalChunkCount > 0) { ++ long processedCount = worldupgrader.getUpgradedChunkCount() + worldupgrader.getSkippedChunkCount(); ++ long currentTime = System.nanoTime(); ++ String speedRate = String.format("%.1f", (processedCount - lastLocation) / ((currentTime - lastTime) / 1_000_000_000.0)); ++ Main.LOGGER.info("{}% completed ({} / {} chunks) at {}cps...", MathHelper.d((float) processedCount / (float) totalChunkCount * 100.0F), processedCount, totalChunkCount, speedRate); ++ lastLocation = processedCount; ++ lastTime = currentTime; ++ } // Yatopia end + + if (!booleansupplier.getAsBoolean()) { + worldupgrader.a(); +diff --git a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java +index 6725b31a5183d5af7f8f7566ed21eb61797ef4c9..f13e16aca89f5013ef316e7dbf3d4c67885e6f3e 100644 +--- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java ++++ b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java +@@ -33,6 +33,13 @@ import net.minecraft.world.level.storage.Convertable; + import net.minecraft.world.level.storage.WorldPersistentData; + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; ++// Yatopia start ++import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; ++import java.util.Set; ++import java.util.concurrent.CompletableFuture; ++import java.util.concurrent.ExecutorService; ++import java.util.concurrent.Executors; ++// Yatopia end + + // CraftBukkit start + import net.minecraft.world.level.dimension.DimensionManager; +@@ -50,9 +57,9 @@ public class WorldUpgrader { + private volatile boolean h = true; + private volatile boolean i; + private volatile float j; +- private volatile int k; +- private volatile int l; +- private volatile int m; ++ private volatile long k; // Yatopia - int -> long ++ private volatile long l; // Yatopia - int -> long ++ private volatile long m; // Yatopia - int -> long + private final Object2FloatMap> n = Object2FloatMaps.synchronize(new Object2FloatOpenCustomHashMap(SystemUtils.k())); // CraftBukkit + private volatile IChatBaseComponent o = new ChatMessage("optimizeWorld.stage.counting"); + private static final Pattern p = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$"); +@@ -117,6 +124,11 @@ public class WorldUpgrader { + + this.o = new ChatMessage("optimizeWorld.stage.upgrading"); + ++ // Yatopia start ++ final ExecutorService upgradeExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactoryBuilder().setDaemon(true).setPriority(Thread.NORM_PRIORITY - 1).setNameFormat("WorldUpgrader Worker #%d").build()); ++ final Set> futures = new ObjectOpenHashSet<>(); ++ final com.ibm.asyncutil.locks.AsyncNamedLock regionFileLocks = com.ibm.asyncutil.locks.AsyncNamedLock.createFair(); ++ // Yatopia end + while (this.h) { + boolean flag = false; + float f1 = 0.0F; +@@ -130,6 +142,11 @@ public class WorldUpgrader { + + if (listiterator.hasNext()) { + ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) listiterator.next(); ++ // Yatopia start ++ flag = true; ++ futures.add(regionFileLocks.acquireLock(String.format("%d %d", chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()).hashCode()).toCompletableFuture().thenAcceptAsync(lockToken -> { ++ try { ++ // Yatopia end + boolean flag1 = false; + + try { +@@ -179,7 +196,12 @@ public class WorldUpgrader { + ++this.m; + } + +- flag = true; ++ // Yatopia start ++ } finally { ++ lockToken.releaseLock(); ++ } ++ }, upgradeExecutor)); ++ // Yatopia end + } + + f2 = (float) listiterator.nextIndex() / f; +@@ -192,6 +214,8 @@ public class WorldUpgrader { + } + } + ++ CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); // Yatopia ++ upgradeExecutor.shutdown(); // Yatopia + this.o = new ChatMessage("optimizeWorld.stage.finished"); + UnmodifiableIterator unmodifiableiterator3 = immutablemap1.values().iterator(); + +@@ -280,16 +304,19 @@ public class WorldUpgrader { + } + + public int e() { +- return this.k; ++ return (int) this.k; // Yatopia + } ++ public long getTotalChunkCount() { return this.k; } // Yatopia + + public int f() { +- return this.l; ++ return (int) this.l; // Yatopia + } ++ public long getUpgradedChunkCount() { return this.l; } // Yatopia + + public int g() { +- return this.m; ++ return (int) this.m; // Yatopia + } ++ public long getSkippedChunkCount() { return this.m; } // Yatopia + + public IChatBaseComponent h() { + return this.o; diff --git a/patches/server/0070-lithium-MultiNoiseBiomeSourceMixin.patch b/patches/server/0070-lithium-MultiNoiseBiomeSourceMixin.patch new file mode 100644 index 00000000..d3b304ed --- /dev/null +++ b/patches/server/0070-lithium-MultiNoiseBiomeSourceMixin.patch @@ -0,0 +1,195 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: ishland +Date: Wed, 3 Feb 2021 23:00:18 +0800 +Subject: [PATCH] lithium MultiNoiseBiomeSourceMixin + + +diff --git a/src/main/java/net/minecraft/world/level/biome/BiomeBase.java b/src/main/java/net/minecraft/world/level/biome/BiomeBase.java +index c4fb051739c1c186c1574185e0653f513755987d..e9993fa0f3af9e3ecd01f91ef5c14e528bdb33b5 100644 +--- a/src/main/java/net/minecraft/world/level/biome/BiomeBase.java ++++ b/src/main/java/net/minecraft/world/level/biome/BiomeBase.java +@@ -52,6 +52,13 @@ import org.apache.logging.log4j.Logger; + public final class BiomeBase { + + public static final Logger LOGGER = LogManager.getLogger(); ++ // Yatopia start ++ static class cProxy extends BiomeBase.c { ++ public cProxy(float f, float f1, float f2, float f3, float f4) { ++ super(f, f1, f2, f3, f4); ++ } ++ } ++ // Yatopia end + // Paper start + private static class dProxy extends BiomeBase.d { + private dProxy(Precipitation biomebase_precipitation, float f, TemperatureModifier biomebase_temperaturemodifier, float f1) { +diff --git a/src/main/java/net/minecraft/world/level/biome/WorldChunkManagerMultiNoise.java b/src/main/java/net/minecraft/world/level/biome/WorldChunkManagerMultiNoise.java +index 5287aec4c384c6cea76334ae1d8dc99070e8f43e..23637d9be9f9973266c1bba699c40cdc6c5d9e6d 100644 +--- a/src/main/java/net/minecraft/world/level/biome/WorldChunkManagerMultiNoise.java ++++ b/src/main/java/net/minecraft/world/level/biome/WorldChunkManagerMultiNoise.java +@@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableList; + import com.google.common.collect.Maps; + import com.mojang.datafixers.util.Either; + import com.mojang.datafixers.util.Function3; ++import com.mojang.datafixers.util.Function6; // Yatopia - decompile fix + import com.mojang.datafixers.util.Pair; + import com.mojang.serialization.Codec; + import com.mojang.serialization.DataResult; +@@ -27,26 +28,43 @@ import net.minecraft.world.level.levelgen.synth.NoiseGeneratorNormal; + + public class WorldChunkManagerMultiNoise extends WorldChunkManager { + ++ // Yatopia start - decompile fix ++ private static class aProxy extends WorldChunkManagerMultiNoise.a { ++ public aProxy(int i, List list) { ++ super(i, list); ++ } ++ } ++ private static class bProxy extends WorldChunkManagerMultiNoise.b { ++ public bProxy(MinecraftKey minecraftkey, Function3, Long, WorldChunkManagerMultiNoise> function3) { ++ super(minecraftkey, function3); ++ } ++ } ++ private static class cProxy extends WorldChunkManagerMultiNoise.c { ++ private cProxy(WorldChunkManagerMultiNoise.b worldchunkmanagermultinoise_b, IRegistry iregistry, long i) { ++ super(worldchunkmanagermultinoise_b, iregistry, i); ++ } ++ } ++ // Yatopia end + private static final WorldChunkManagerMultiNoise.a g = new WorldChunkManagerMultiNoise.a(-7, ImmutableList.of(1.0D, 1.0D)); + public static final MapCodec e = RecordCodecBuilder.mapCodec((instance) -> { + return instance.group(Codec.LONG.fieldOf("seed").forGetter((worldchunkmanagermultinoise) -> { + return worldchunkmanagermultinoise.r; + }), RecordCodecBuilder.create((instance1) -> { +- return instance1.group(BiomeBase.c.a.fieldOf("parameters").forGetter(Pair::getFirst), BiomeBase.d.fieldOf("biome").forGetter(Pair::getSecond)).apply(instance1, Pair::of); +- }).listOf().fieldOf("biomes").forGetter((worldchunkmanagermultinoise) -> { +- return worldchunkmanagermultinoise.p; +- }), WorldChunkManagerMultiNoise.a.a.fieldOf("temperature_noise").forGetter((worldchunkmanagermultinoise) -> { ++ return instance1.group(BiomeBase.cProxy.a.fieldOf("parameters").forGetter((java.util.function.Function) t -> ((Pair) t).getFirst()), BiomeBase.d.fieldOf("biome").forGetter(t1 -> (Supplier) ((Pair) t1).getSecond())).apply(instance1, Pair::of); // Yatopia - decompile fix ++ }).listOf().fieldOf("biomes").forGetter((Function) worldchunkmanagermultinoise -> { // Yatopia - decompile fix ++ return ((WorldChunkManagerMultiNoise) worldchunkmanagermultinoise).p; // Yatopia - decompile fix ++ }), WorldChunkManagerMultiNoise.aProxy.a.fieldOf("temperature_noise").forGetter((worldchunkmanagermultinoise) -> { // Yatopia - decompile fix + return worldchunkmanagermultinoise.h; +- }), WorldChunkManagerMultiNoise.a.a.fieldOf("humidity_noise").forGetter((worldchunkmanagermultinoise) -> { ++ }), WorldChunkManagerMultiNoise.aProxy.a.fieldOf("humidity_noise").forGetter((worldchunkmanagermultinoise) -> { // Yatopia - decompile fix + return worldchunkmanagermultinoise.i; +- }), WorldChunkManagerMultiNoise.a.a.fieldOf("altitude_noise").forGetter((worldchunkmanagermultinoise) -> { ++ }), WorldChunkManagerMultiNoise.aProxy.a.fieldOf("altitude_noise").forGetter((worldchunkmanagermultinoise) -> { // Yatopia - decompile fix + return worldchunkmanagermultinoise.j; +- }), WorldChunkManagerMultiNoise.a.a.fieldOf("weirdness_noise").forGetter((worldchunkmanagermultinoise) -> { ++ }), WorldChunkManagerMultiNoise.aProxy.a.fieldOf("weirdness_noise").forGetter((worldchunkmanagermultinoise) -> { // Yatopia - decompile fix + return worldchunkmanagermultinoise.k; +- })).apply(instance, WorldChunkManagerMultiNoise::new); ++ })).apply(instance, (Function6>>, WorldChunkManagerMultiNoise.a, WorldChunkManagerMultiNoise.a, WorldChunkManagerMultiNoise.a, WorldChunkManagerMultiNoise.a, WorldChunkManagerMultiNoise>) WorldChunkManagerMultiNoise::new); // Yatopia - decompile fix + }); +- public static final Codec f = Codec.mapEither(WorldChunkManagerMultiNoise.c.a, WorldChunkManagerMultiNoise.e).xmap((either) -> { +- return (WorldChunkManagerMultiNoise) either.map(WorldChunkManagerMultiNoise.c::d, Function.identity()); ++ public static final Codec f = Codec.mapEither(WorldChunkManagerMultiNoise.cProxy.a, WorldChunkManagerMultiNoise.e).xmap((either) -> { // Yatopia - decompile fix ++ return (WorldChunkManagerMultiNoise) either.map(c -> c.d(), Function.identity()); // Yatopia - decompile fix + }, (worldchunkmanagermultinoise) -> { + return (Either) worldchunkmanagermultinoise.d().map(Either::left).orElseGet(() -> { + return Either.right(worldchunkmanagermultinoise); +@@ -100,8 +118,44 @@ public class WorldChunkManagerMultiNoise extends WorldChunkManager { + }); + } + ++ //Yatopia - Faster Method ++ /** ++ * @reason Remove stream based code in favor of regular collections. ++ * @author SuperCoder79 ++ */ + @Override + public BiomeBase getBiome(int i, int j, int k) { ++ // [VanillaCopy] MultiNoiseBiomeSource#getBiomeForNoiseGen ++ ++ // Get the y value for perlin noise sampling. This field is always set to false in vanilla code. ++ int l = this.q ? j : 0; ++ ++ // Calculate the noise point based using 4 perlin noise samplers. ++ BiomeBase.c biomebase_c = new BiomeBase.c( ++ (float) this.l.a(i, l, k), ++ (float) this.m.a(i, l, k), ++ (float) this.n.a(i, l, k), ++ (float) this.o.a(i, l, k), ++ 0.0F); ++ ++ int idx = -1; ++ float min = Float.POSITIVE_INFINITY; ++ ++ // Iterate through the biome points and calculate the distance to the current noise point. ++ for (int itterator = 0; itterator < this.p.size(); itterator++) { ++ float distance = this.p.get(itterator).getFirst().a(biomebase_c); ++ ++ // If the distance is less than the recorded minimum, update the minimum and set the current index. ++ if (min > distance) { ++ idx = itterator; ++ min = distance; ++ } ++ } ++ ++ // Return the biome with the noise point closest to the evaluated one. ++ return this.p.get(idx).getSecond().get() == null ? BiomeRegistry.b : this.p.get(idx).getSecond().get(); ++ } ++ /* //Yatopia - Replace Method + int l = this.q ? j : 0; + BiomeBase.c biomebase_c = new BiomeBase.c((float) this.l.a((double) i, (double) l, (double) k), (float) this.m.a((double) i, (double) l, (double) k), (float) this.n.a((double) i, (double) l, (double) k), (float) this.o.a((double) i, (double) l, (double) k), 0.0F); + +@@ -109,15 +163,17 @@ public class WorldChunkManagerMultiNoise extends WorldChunkManager { + return ((BiomeBase.c) pair.getFirst()).a(biomebase_c); + })).map(Pair::getSecond).map(Supplier::get).orElse(BiomeRegistry.b); + } ++ */ //Yatopia End ++ + + public boolean b(long i) { +- return this.r == i && this.s.isPresent() && Objects.equals(((Pair) this.s.get()).getSecond(), WorldChunkManagerMultiNoise.b.a); ++ return this.r == i && this.s.isPresent() && Objects.equals(((Pair) this.s.get()).getSecond(), WorldChunkManagerMultiNoise.bProxy.a); // Yatopia - decompile fix + } + + public static class b { + +- private static final Map b = Maps.newHashMap(); +- public static final WorldChunkManagerMultiNoise.b a = new WorldChunkManagerMultiNoise.b(new MinecraftKey("nether"), (worldchunkmanagermultinoise_b, iregistry, olong) -> { ++ protected static final Map b = Maps.newHashMap(); // Yatopia - decompile fix ++ public static final WorldChunkManagerMultiNoise.b a = new WorldChunkManagerMultiNoise.b(new MinecraftKey("nether"), (worldchunkmanagermultinoise_b, iregistry, olong) -> { // Yatopia - decompile fix + return new WorldChunkManagerMultiNoise(olong, ImmutableList.of(Pair.of(new BiomeBase.c(0.0F, 0.0F, 0.0F, 0.0F, 0.0F), () -> { + return (BiomeBase) iregistry.d(Biomes.NETHER_WASTES); + }), Pair.of(new BiomeBase.c(0.0F, -0.5F, 0.0F, 0.0F, 0.0F), () -> { +@@ -136,7 +192,7 @@ public class WorldChunkManagerMultiNoise extends WorldChunkManager { + public b(MinecraftKey minecraftkey, Function3, Long, WorldChunkManagerMultiNoise> function3) { + this.c = minecraftkey; + this.d = function3; +- WorldChunkManagerMultiNoise.b.b.put(minecraftkey, this); ++ WorldChunkManagerMultiNoise.bProxy.b.put(minecraftkey, this); // Yatopia - decompile fix + } + + public WorldChunkManagerMultiNoise a(IRegistry iregistry, long i) { +@@ -144,16 +200,16 @@ public class WorldChunkManagerMultiNoise extends WorldChunkManager { + } + } + +- static final class c { ++ static class c { // Yatopia - decompile fix + + public static final MapCodec a = RecordCodecBuilder.mapCodec((instance) -> { + return instance.group(MinecraftKey.a.flatXmap((minecraftkey) -> { +- return (DataResult) Optional.ofNullable(WorldChunkManagerMultiNoise.b.b.get(minecraftkey)).map(DataResult::success).orElseGet(() -> { ++ return (DataResult) Optional.ofNullable(bProxy.b.get(minecraftkey)).map(DataResult::success).orElseGet(() -> { // Yatopia - decompile fix + return DataResult.error("Unknown preset: " + minecraftkey); + }); + }, (worldchunkmanagermultinoise_b) -> { +- return DataResult.success(worldchunkmanagermultinoise_b.c); +- }).fieldOf("preset").stable().forGetter(WorldChunkManagerMultiNoise.c::a), RegistryLookupCodec.a(IRegistry.ay).forGetter(WorldChunkManagerMultiNoise.c::b), Codec.LONG.fieldOf("seed").stable().forGetter(WorldChunkManagerMultiNoise.c::c)).apply(instance, instance.stable(WorldChunkManagerMultiNoise.c::new)); ++ return DataResult.success(((WorldChunkManagerMultiNoise.b) worldchunkmanagermultinoise_b).c); // Yatopia - decompile fix ++ }).fieldOf("preset").stable().forGetter(o -> ((WorldChunkManagerMultiNoise.c) o).a()), (RecordCodecBuilder) RegistryLookupCodec.a(IRegistry.ay).forGetter((Function>) c -> c.b()), (RecordCodecBuilder) Codec.LONG.fieldOf("seed").stable().forGetter((Function) c -> c.c())).apply(instance, instance.stable((Function3, Long, WorldChunkManagerMultiNoise.c>) WorldChunkManagerMultiNoise.c::new)); // Yatopia - decompile fix + }); + private final WorldChunkManagerMultiNoise.b b; + private final IRegistry c; +@@ -187,7 +243,7 @@ public class WorldChunkManagerMultiNoise extends WorldChunkManager { + private final int b; + private final DoubleList c; + public static final Codec a = RecordCodecBuilder.create((instance) -> { +- return instance.group(Codec.INT.fieldOf("firstOctave").forGetter(WorldChunkManagerMultiNoise.a::a), Codec.DOUBLE.listOf().fieldOf("amplitudes").forGetter(WorldChunkManagerMultiNoise.a::b)).apply(instance, WorldChunkManagerMultiNoise.a::new); ++ return instance.group(Codec.INT.fieldOf("firstOctave").forGetter((Function) a -> a.a()), Codec.DOUBLE.listOf().fieldOf("amplitudes").forGetter(a -> a.b())).apply(instance, WorldChunkManagerMultiNoise.a::new); // Yatopia - decompile fix + }); + + public a(int i, List list) { diff --git a/patches/server/0071-Force-world-save.patch b/patches/server/0071-Force-world-save.patch new file mode 100644 index 00000000..8fdece00 --- /dev/null +++ b/patches/server/0071-Force-world-save.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: ishland +Date: Tue, 9 Feb 2021 21:42:09 +0800 +Subject: [PATCH] Force world save + + +diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java +index 29cd71efe86eea2227f373c15c39dc530e9e8199..4028526166fe528d772729e9334289df460f3b1e 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java ++++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/VillagePlace.java +@@ -366,6 +366,7 @@ public class VillagePlace extends RegionFileSection { + }).orElse(false); + } + ++ private final java.util.concurrent.atomic.AtomicBoolean hasWorked = new java.util.concurrent.atomic.AtomicBoolean(false); // Yatopia - enforce one chunk unload per tick + @Override + public void a(BooleanSupplier booleansupplier) { + // Paper start - async chunk io +@@ -373,7 +374,7 @@ public class VillagePlace extends RegionFileSection { + super.a(booleansupplier); + } else { + //super.a(booleansupplier); // re-implement below +- while (!((RegionFileSection)this).d.isEmpty() && booleansupplier.getAsBoolean() && !this.world.isSavingDisabled()) { // Tuinity - unload POI data - don't write to disk if saving is disabled ++ hasWorked.set(false); while (!((RegionFileSection)this).d.isEmpty() && (hasWorked.compareAndSet(false, true) || booleansupplier.getAsBoolean()) && !this.world.isSavingDisabled()) { // Tuinity - unload POI data - don't write to disk if saving is disabled // Yatopia - enforce one chunk unload per tick + ChunkCoordIntPair chunkcoordintpair = SectionPosition.a(((RegionFileSection)this).d.firstLong()).r(); + + NBTTagCompound data; +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java +index f70c14385c95763b5f270a6e2ce372cf047ba7bb..4454489153f7932deaaebe8aa32ecb603764f42b 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileSection.java +@@ -50,8 +50,9 @@ public class RegionFileSection extends RegionFileCache implements AutoCloseab + //this.b = new IOWorker(file, flag, file.getName()); // Paper - nuke IOWorker + } + ++ private final java.util.concurrent.atomic.AtomicBoolean hasWorked = new java.util.concurrent.atomic.AtomicBoolean(false); // Yatopia - enforce one chunk unload per tick + protected void a(BooleanSupplier booleansupplier) { +- while (!this.d.isEmpty() && booleansupplier.getAsBoolean()) { ++ hasWorked.set(false); while (!this.d.isEmpty() && (hasWorked.compareAndSet(false, true) || booleansupplier.getAsBoolean())) { // Yatopia - enforce one chunk unload per tick + ChunkCoordIntPair chunkcoordintpair = SectionPosition.a(this.d.firstLong()).r(); // Paper - conflict here to avoid obfhelpers + + this.d(chunkcoordintpair); diff --git a/patches/server/0072-java-11.patch b/patches/server/0072-java-11.patch new file mode 100644 index 00000000..5e488cbd --- /dev/null +++ b/patches/server/0072-java-11.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Simon Gardling +Date: Fri, 23 Apr 2021 11:11:20 -0400 +Subject: [PATCH] java 11 + + +diff --git a/pom.xml b/pom.xml +index f0a73238612327d71cf78801df816823d80893a0..57a87372c039fb410c97eaa00227b429b90da2b1 100644 +--- a/pom.xml ++++ b/pom.xml +@@ -14,8 +14,8 @@ + git + 1.16.5 + 1_16_R3 +- 1.8 +- 1.8 ++ 11 ++ 11 + + + +diff --git a/src/main/java/io/papermc/paper/util/PaperJvmChecker.java b/src/main/java/io/papermc/paper/util/PaperJvmChecker.java +index c6ea429819c07e7f4bc257cad73463a030767825..3fdca3afeefdb7f850934f9b1b5312f8979b50f3 100644 +--- a/src/main/java/io/papermc/paper/util/PaperJvmChecker.java ++++ b/src/main/java/io/papermc/paper/util/PaperJvmChecker.java +@@ -28,21 +28,17 @@ public class PaperJvmChecker { + public static void checkJvm() { + if (getJvmVersion() < 11) { + final Logger logger = LogManager.getLogger(); +- logger.warn("************************************************************"); +- logger.warn("* WARNING - YOU ARE RUNNING AN OUTDATED VERSION OF JAVA."); +- logger.warn("* PAPER WILL STOP BEING COMPATIBLE WITH THIS VERSION OF"); +- logger.warn("* JAVA WHEN MINECRAFT 1.17 IS RELEASED."); +- logger.warn("*"); +- logger.warn("* Please update the version of Java you use to run Paper"); +- logger.warn("* to at least Java 11. When Paper for Minecraft 1.17 is"); +- logger.warn("* released support for versions of Java before 11 will"); +- logger.warn("* be dropped."); +- logger.warn("*"); +- logger.warn("* Current Java version: {}", System.getProperty("java.version")); +- logger.warn("*"); +- logger.warn("* Check this forum post for more information: "); +- logger.warn("* https://papermc.io/java11"); +- logger.warn("************************************************************"); ++ // Yatopia start - require java 11+ ++ // Note - no clue how someone would run a jar built for java 11 on a java version lower than 11, but doesn't hurt to update this warning I guess. ++ logger.fatal("************************************************************"); ++ logger.fatal("* ERROR - YOU ARE RUNNING AN OUTDATED VERSION OF JAVA."); ++ logger.fatal("* YOU NEED TO BE RUNNING JAVA 11 OR HIGHER"); ++ logger.fatal("* In order to achieve Yatopia's high performance,"); ++ logger.fatal("* Yatopia uses features only found in Java 11 or higher."); ++ logger.fatal("* If you do not know how to install Java 11 or have any other questions,"); ++ logger.fatal("* Join our discord server (https://discord.io/YatopiaMC) and ask for support in #yatopia-help"); ++ logger.fatal("************************************************************"); ++ // Yatopia end + } + } + }