From efc9a014b153778855a1045b4878b0a734aa0a7e Mon Sep 17 00:00:00 2001 From: AltronMaxX Date: Sun, 12 May 2024 16:04:03 +0400 Subject: [PATCH] 1.20.6 wip --- build.gradle.kts | 15 +- gradle.properties | 6 +- ...recated-tag-to-getScheduler-function.patch | 4 +- patches/server/0001-Rebranding.patch | 16 +- patches/server/0002-Feature-secure-seed.patch | 307 ++--- .../0003-Enable-a-lot-of-commands.patch | 14 +- patches/server/0005-Add-config.patch | 27 +- .../server/0006-Reenable-command-blocks.patch | 4 +- ...hread-unsafe-random-for-mob-spawning.patch | 8 +- ...ot-send-chunk-if-it-was-already-sent.patch | 4 +- ...nc-if-we-cannot-move-entity-off-main.patch | 6 +- ...-packets-and-vanilla-end-portal-tele.patch | 12 +- .../server/0011-Add-Foldenor-commands.patch | 4 +- ...function-to-tegion-tps-or-return-20-.patch | 4 +- .../server/0014-Linear-Region-Format.patch | 1176 ----------------- ...tScheduler-to-GlobalRegionScheduler.patch} | 0 ...aiiju-Don-t-pathfind-outside-region.patch} | 4 +- ...fish-Optimize-entity-coordinate-key.patch} | 2 +- ...Cache-climbing-check-for-activation.patch} | 4 +- .../0018-Alternative-Keepalive-Handling.patch | 133 -- ...ish-Improve-fluid-direction-caching.patch} | 2 +- ...return-optimization-for-target-find.patch} | 16 +- patches/server/0020-POI-changes.patch | 89 -- ...erfish-Reduce-chunk-loading-lookups.patch} | 4 +- ...heck-for-spooky-season-once-an-hour.patch} | 35 +- ...ow-null-plugins-in-region-scheduler.patch} | 0 ...0025-Pufferfish-Optimize-suffocation.patch | 95 -- ...ove-container-checking-with-a-bitset.patch | 514 ------- ...aper-6045-block-goal-shouldn-t-load-.patch | 18 - ...ale-Variable-entity-wake-up-duration.patch | 83 -- ...chunks-to-activate-climbing-entities.patch | 89 -- .../0033-Gale-Optimize-sun-burn-tick.patch | 56 - .../0034-Change-reload-config-work.patch | 32 - ...fferfish-Dynamic-Activation-of-Brain.patch | 447 ------- .../server/0036-Different-crash-fixes.patch | 57 - .../0016-Leaves-Protocol-Core.patch | 0 .../0017-Leaves-Appleskin-Protocol.patch | 0 .../0019-Villagers-dispawn-fix.patch | 0 patches/wip/0040-New-mob-spawn-system.patch | 967 -------------- 39 files changed, 186 insertions(+), 4068 deletions(-) delete mode 100644 patches/server/0014-Linear-Region-Format.patch rename patches/server/{0015-Redirect-CraftScheduler-to-GlobalRegionScheduler.patch => 0014-Redirect-CraftScheduler-to-GlobalRegionScheduler.patch} (100%) rename patches/server/{0021-Kaiiju-Don-t-pathfind-outside-region.patch => 0015-Kaiiju-Don-t-pathfind-outside-region.patch} (86%) rename patches/server/{0022-Pufferfish-Optimize-entity-coordinate-key.patch => 0016-Pufferfish-Optimize-entity-coordinate-key.patch} (90%) rename patches/server/{0023-Pufferfish-Cache-climbing-check-for-activation.patch => 0017-Pufferfish-Cache-climbing-check-for-activation.patch} (92%) delete mode 100644 patches/server/0018-Alternative-Keepalive-Handling.patch rename patches/server/{0024-Pufferfish-Improve-fluid-direction-caching.patch => 0018-Pufferfish-Improve-fluid-direction-caching.patch} (98%) rename patches/server/{0026-Pufferfish-Early-return-optimization-for-target-find.patch => 0019-Pufferfish-Early-return-optimization-for-target-find.patch} (78%) delete mode 100644 patches/server/0020-POI-changes.patch rename patches/server/{0027-Pufferfish-Reduce-chunk-loading-lookups.patch => 0020-Pufferfish-Reduce-chunk-loading-lookups.patch} (92%) rename patches/server/{0030-Pufferfish-Only-check-for-spooky-season-once-an-hour.patch => 0021-Pufferfish-Only-check-for-spooky-season-once-an-hour.patch} (64%) rename patches/server/{0037-Allow-null-plugins-in-region-scheduler.patch => 0022-Allow-null-plugins-in-region-scheduler.patch} (100%) delete mode 100644 patches/server/0025-Pufferfish-Optimize-suffocation.patch delete mode 100644 patches/server/0028-Pufferfish-Improve-container-checking-with-a-bitset.patch delete mode 100644 patches/server/0029-Pufferfish-Fix-Paper-6045-block-goal-shouldn-t-load-.patch delete mode 100644 patches/server/0031-Gale-Variable-entity-wake-up-duration.patch delete mode 100644 patches/server/0032-Gale-Don-t-load-chunks-to-activate-climbing-entities.patch delete mode 100644 patches/server/0033-Gale-Optimize-sun-burn-tick.patch delete mode 100644 patches/server/0034-Change-reload-config-work.patch delete mode 100644 patches/server/0035-Pufferfish-Dynamic-Activation-of-Brain.patch delete mode 100644 patches/server/0036-Different-crash-fixes.patch rename patches/{server => wip}/0016-Leaves-Protocol-Core.patch (100%) rename patches/{server => wip}/0017-Leaves-Appleskin-Protocol.patch (100%) rename patches/{server => wip}/0019-Villagers-dispawn-fix.patch (100%) delete mode 100644 patches/wip/0040-New-mob-spawn-system.patch diff --git a/build.gradle.kts b/build.gradle.kts index 0da1fff..db4f6e2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,8 +3,7 @@ import io.papermc.paperweight.util.constants.PAPERCLIP_CONFIG plugins { java `maven-publish` - id("com.github.johnrengelman.shadow") version "8.1.1" apply false - id("io.papermc.paperweight.patcher") version "1.5.13" + id("io.papermc.paperweight.patcher") version "1.6.4-SNAPSHOT" } val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/" @@ -14,12 +13,13 @@ repositories { maven(paperMavenPublicUrl) { content { onlyForConfigurations(PAPERCLIP_CONFIG) } } + maven("https://maven.nostal.ink/repository/maven-snapshots/") } dependencies { - remapper("net.fabricmc:tiny-remapper:0.10.1:fat") - decompiler("org.vineflower:vineflower:1.9.3") - paperclip("io.papermc:paperclip:3.0.4-SNAPSHOT") + remapper("net.fabricmc:tiny-remapper:0.10.2:fat") + decompiler("org.vineflower:vineflower:1.10.1") + paperclip("cn.dreeam:quantumleaper:1.0.0-SNAPSHOT") } allprojects { @@ -58,7 +58,7 @@ allprojects { subprojects { tasks.withType { options.encoding = Charsets.UTF_8.name() - options.release.set(17) + options.release.set(21) } tasks.withType { options.encoding = Charsets.UTF_8.name() @@ -85,7 +85,8 @@ paperweight { ref.set(providers.gradleProperty("foliaRef")) withStandardPatcher { - baseName("Folia") + apiSourceDirPath.set("folia-api") + serverSourceDirPath.set("folia-server") apiPatchDir.set(layout.projectDirectory.dir("patches/api")) apiOutputDir.set(layout.projectDirectory.dir("Foldenor-api")) diff --git a/gradle.properties b/gradle.properties index 77cc2e7..cad1d74 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ group=dev.edenor.foldenor -version=1.20.4-R0.1-SNAPSHOT -mcVersion=1.20.4 -foliaRef=4b4e25c6bc724f44891f2ef14e6a0d46b3a0be6b +version=1.20.6-R0.1-SNAPSHOT +mcVersion=1.20.6 +foliaRef=b1bfe7b55f33db5a9d68169f7e0a3503cad066ff org.gradle.caching=true org.gradle.parallel=true diff --git a/patches/api/0001-Add-deprecated-tag-to-getScheduler-function.patch b/patches/api/0001-Add-deprecated-tag-to-getScheduler-function.patch index 1c34cda..0f96d3c 100644 --- a/patches/api/0001-Add-deprecated-tag-to-getScheduler-function.patch +++ b/patches/api/0001-Add-deprecated-tag-to-getScheduler-function.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add-deprecated-tag-to-getScheduler-function diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java -index 4d6de4f2c67b1f122768806443766bd20c5ae617..dd677c37f0517f25978d11f375b4754d288e2cac 100644 +index b4327a55c422380ca6b3a1dc47c3adbe76de4655..3b9e51b24b029327e09d074706c138f1f7801288 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java -@@ -780,8 +780,10 @@ public final class Bukkit { +@@ -782,8 +782,10 @@ public final class Bukkit { * Gets the scheduler for managing scheduled events. * * @return a scheduling service for this server diff --git a/patches/server/0001-Rebranding.patch b/patches/server/0001-Rebranding.patch index 78edd0e..ff9643c 100644 --- a/patches/server/0001-Rebranding.patch +++ b/patches/server/0001-Rebranding.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Rebranding diff --git a/build.gradle.kts b/build.gradle.kts -index 905262067dabc7cbe5b08639fcefd3a996a937e0..0b66ac537a56f907022f615c47ea970c7b26bbf3 100644 +index b1d621ed91c74790fd5b66339c044ec444de62ef..4f982050dc499c2d037c389c3b25338a01b16ba5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -14,7 +14,7 @@ val alsoShade: Configuration by configurations.creating +@@ -13,7 +13,7 @@ val alsoShade: Configuration by configurations.creating dependencies { // Folia start @@ -17,7 +17,7 @@ index 905262067dabc7cbe5b08639fcefd3a996a937e0..0b66ac537a56f907022f615c47ea970c implementation("io.papermc.paper:paper-mojangapi:${project.version}") { exclude("io.papermc.paper", "paper-api") } -@@ -75,7 +75,7 @@ tasks.jar { +@@ -83,7 +83,7 @@ tasks.jar { attributes( "Main-Class" to "org.bukkit.craftbukkit.Main", "Implementation-Title" to "CraftBukkit", @@ -82,7 +82,7 @@ index e2f704c115fd6e00960bb56bb0779f1100c89c17..d77980fe69758834ffded0d24c2436e6 org.bukkit.Bukkit.getLogger().warning("Version: " + org.bukkit.Bukkit.getBukkitVersion()); } diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java -index 99c5038672b09d0874125e3df280174c1e8151e6..0cc595adcfe1af04ce1b5c2c2987ba62814ecb59 100644 +index 4f3cc14d48690bb183d09bb7a5ba1e23e8a0c08a..b51283f1722f0973888b7094ca5eaeec6c78dae8 100644 --- a/src/main/java/net/minecraft/CrashReport.java +++ b/src/main/java/net/minecraft/CrashReport.java @@ -125,6 +125,7 @@ public class CrashReport { @@ -94,10 +94,10 @@ index 99c5038672b09d0874125e3df280174c1e8151e6..0cc595adcfe1af04ce1b5c2c2987ba62 stringbuilder.append(CrashReport.getErrorComment()); stringbuilder.append("\n\n"); diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index d6a7188227cee9072976db98613324ee2d3dcdc8..8cc74922fb00a1975dfb9bd2af8972c7afcfdb14 100644 +index 49a121862fbb823a00753d99d24d04c87479fcd1..967aa20540b284023a36d27201d90b9c8af824bc 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1963,7 +1963,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop dispatcher, boolean dedicated) { -@@ -12,10 +16,19 @@ public class SeedCommand { - return !dedicated || source.hasPermission(2); - }).executes((context) -> { + dispatcher.register(Commands.literal("seed").requires(source -> !dedicated || source.hasPermission(2)).executes(context -> { long l = context.getSource().getLevel().getSeed(); - Component component = ComponentUtils.copyOnClickText(String.valueOf(l)); -- context.getSource().sendSuccess(() -> { -- return Component.translatable("commands.seed.success", component); -- }, false); +- context.getSource().sendSuccess(() -> Component.translatable("commands.seed.success", component), false); ++ //Component component = ComponentUtils.copyOnClickText(String.valueOf(l)); Foldenor - feature secure seed ++ //context.getSource().sendSuccess(() -> Component.translatable("commands.seed.success", component), false); Foldenor - feature secure seed ++ ++ // Foldenor start - feature secure seed + Globals.setupGlobals(context.getSource().getLevel()); + String seedStr = Globals.seedToString(Globals.worldSeed); + + Component seedComponent = ComponentUtils.wrapInSquareBrackets(Component.literal(String.valueOf(l)).withStyle((style) -> { -+ return style.withColor(ChatFormatting.GREEN).withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, String.valueOf(l))).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable("chat.copy.click"))).withInsertion(String.valueOf(l)); ++ return style.withColor(ChatFormatting.GREEN).withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, String.valueOf(l))).withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable("chat.copy.click"))).withInsertion(String.valueOf(l)); + })); + + Component featureSeedComponent = ComponentUtils.wrapInSquareBrackets(Component.translatable("chat.copy.click").withStyle((style) -> { @@ -379,14 +379,15 @@ index 342362c217df5476a927eb54cef3cafcea3889fd..097f549a0275846199f3ba929f3cb0d9 + + context.getSource().sendSuccess(() -> {return Component.translatable("commands.seed.success", seedComponent);}, false); + context.getSource().sendSuccess(() -> {return Component.translatable("Feature seed: %s", featureSeedComponent);}, false); ++ // Foldenor end return (int)l; })); } diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -index bab2471616404821671264ccefd729cab8d0bf58..3e90fb061f1ba37bb7cc49d5c7861d0eb5d55841 100644 +index 9d10cdacb3aed2c00dc60aeb6f2cbeb48905e21f..d9e4f59e6e283b41bb2fcf00c2347eacbb342c4d 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -@@ -43,6 +43,7 @@ import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; +@@ -42,6 +42,7 @@ import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; import net.minecraft.world.level.levelgen.presets.WorldPreset; import net.minecraft.world.level.levelgen.presets.WorldPresets; import org.slf4j.Logger; @@ -394,7 +395,7 @@ index bab2471616404821671264ccefd729cab8d0bf58..3e90fb061f1ba37bb7cc49d5c7861d0e // CraftBukkit start import joptsimple.OptionSet; -@@ -160,7 +161,17 @@ public class DedicatedServerProperties extends Settings { diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 74483543836d9ed042cc7b9cbbde8d58d6994475..cd82703d47bf0f2457b5698b5bb08e83d18b20e6 100644 +index 1cb09933aa4fa9f766c92ce000aed103fb2a5f54..2155d49bc648650f4e99b50356b4aabf7cb59049 100644 --- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java +++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -46,6 +46,8 @@ import net.minecraft.world.level.levelgen.RandomState; - import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelStorageSource; -+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper -+import net.edenor.foldenor.secureseed.Globals; - - public class ServerChunkCache extends ChunkSource { - -@@ -684,6 +686,7 @@ public class ServerChunkCache extends ChunkSource { +@@ -682,6 +682,7 @@ public class ServerChunkCache extends ChunkSource { } public ChunkGenerator getGenerator() { -+ Globals.setupGlobals(level); ++ net.edenor.foldenor.secureseed.Globals.setupGlobals(level); return this.chunkMap.generator(); } diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 81749b8da7182abd1bf35629f33388e813dbeac0..fab03f244f90e479ce7744a568e999327499b7fe 100644 +index b194f3448b5199e1204da31e1408f2e80803a77d..8d1d64841b8c6dbc7f34c6d1aab5d779764b8fad 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -178,6 +178,8 @@ import org.bukkit.event.weather.LightningStrikeEvent; - import org.bukkit.event.world.GenericGameEvent; - import org.bukkit.event.world.TimeSkipEvent; - // CraftBukkit end -+import it.unimi.dsi.fastutil.ints.IntArrayList; // Paper -+import net.edenor.foldenor.secureseed.Globals; - - public class ServerLevel extends Level implements WorldGenLevel { - -@@ -806,6 +808,7 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -812,6 +812,7 @@ public class ServerLevel extends Level implements WorldGenLevel { chunkgenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkgenerator, gen); } // CraftBukkit end -+ Globals.setupGlobals(this); ++ net.edenor.foldenor.secureseed.Globals.setupGlobals(this); boolean flag2 = minecraftserver.forceSynchronousWrites(); DataFixer datafixer = minecraftserver.getFixerUpper(); - this.entityStorage = new EntityRegionFileStorage(convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), flag2); // Paper - rewrite chunk system //EntityPersistentStorage entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver); + this.entityStorage = new EntityRegionFileStorage(new RegionStorageInfo(convertable_conversionsession.getLevelId(), resourcekey, "entities"), convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), flag2); // Paper - rewrite chunk system diff --git a/src/main/java/net/minecraft/world/entity/monster/Slime.java b/src/main/java/net/minecraft/world/entity/monster/Slime.java -index 3d9107d2c19a09215445aa0e0aacc32f9f82a536..14f6cb1a70a6446c907f418ee3289b2190219314 100644 +index f223e78eb1204bbf5f2de38a7ce5b663800f7dc4..d36a33993e5596f0b7b84934f614d2188ffce1d4 100644 --- a/src/main/java/net/minecraft/world/entity/monster/Slime.java +++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java -@@ -357,7 +357,7 @@ public class Slime extends Mob implements Enemy { +@@ -353,7 +353,7 @@ public class Slime extends Mob implements Enemy { } ChunkPos chunkcoordintpair = new ChunkPos(pos); @@ -469,35 +452,20 @@ index 3d9107d2c19a09215445aa0e0aacc32f9f82a536..14f6cb1a70a6446c907f418ee3289b21 // Paper start - Replace rules for Height in Slime Chunks final double maxHeightSlimeChunk = world.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.slimeChunk.maximum; diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -index f7e5e016a7028a9196e689e950805b0d5b31fe38..7167cbd0ad37742fbe104508860c13e56e8c5e96 100644 +index 1aac95b03a9e2e37c24f2a30bcb259c1424e1c78..2500e6f6f6f0fc5cb726f1145ec3d8d5af9c502b 100644 --- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java +++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -@@ -53,6 +53,7 @@ import net.minecraft.world.level.material.Fluid; - import net.minecraft.world.ticks.SerializableTickContainer; - import net.minecraft.world.ticks.TickContainerAccess; - import org.slf4j.Logger; -+import net.edenor.foldenor.secureseed.WorldgenCryptoRandom; - - public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiomeSource, LightChunk, StructureAccess { - -@@ -82,6 +83,9 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom - protected final LevelHeightAccessor levelHeightAccessor; - protected final LevelChunkSection[] sections; +@@ -175,6 +175,18 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom + return GameEventListenerRegistry.NOOP; + } + private boolean slimeChunk; + private boolean hasComputedSlimeChunk; + - // CraftBukkit start - SPIGOT-6814: move to IChunkAccess to account for 1.17 to 1.18 chunk upgrading. - private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); - public org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer persistentDataContainer = new org.bukkit.craftbukkit.persistence.DirtyCraftPersistentDataContainer(ChunkAccess.DATA_TYPE_REGISTRY); -@@ -172,6 +176,15 @@ public abstract class ChunkAccess implements BlockGetter, BiomeManager.NoiseBiom - return GameEventListenerRegistry.NOOP; - } - + public boolean isSlimeChunk() { + if (!hasComputedSlimeChunk) { + hasComputedSlimeChunk = true; -+ slimeChunk = WorldgenCryptoRandom.seedSlimeChunk(chunkPos.x, chunkPos.z).nextInt(10) == 0; ++ slimeChunk = net.edenor.foldenor.secureseed.WorldgenCryptoRandom.seedSlimeChunk(chunkPos.x, chunkPos.z).nextInt(10) == 0; + } + + return slimeChunk; @@ -507,10 +475,10 @@ index f7e5e016a7028a9196e689e950805b0d5b31fe38..7167cbd0ad37742fbe104508860c13e5 @Nullable public abstract BlockState setBlockState(BlockPos pos, BlockState state, boolean moved); diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -index b31f1e0522ff18fa5853af6104d46f980b916285..1fa710175069f570850071877b7c5987e279333a 100644 +index 9a17f3d0b53b2ab0358012e4238164f8af0a6c6e..39089f6a3472411dd73951aa79d95c828a0537e2 100644 --- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java +++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -76,6 +76,7 @@ import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStruct +@@ -78,6 +78,7 @@ import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStruct import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; import org.apache.commons.lang3.mutable.MutableBoolean; @@ -518,94 +486,80 @@ index b31f1e0522ff18fa5853af6104d46f980b916285..1fa710175069f570850071877b7c5987 public abstract class ChunkGenerator { -@@ -339,7 +340,9 @@ public abstract class ChunkGenerator { +@@ -345,7 +346,7 @@ public abstract class ChunkGenerator { return structure.step().ordinal(); })); List list = (List) this.featuresPerStep.get(); - WorldgenRandom seededrandom = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); -+ WorldgenRandom seededrandom = new WorldgenCryptoRandom( -+ blockposition.getX(), blockposition.getZ(), Globals.Salt.UNDEFINED, 0 -+ ); ++ WorldgenRandom seededrandom = new WorldgenCryptoRandom(blockposition.getX(), blockposition.getZ(), Globals.Salt.UNDEFINED, 0); long i = seededrandom.setDecorationSeed(generatoraccessseed.getSeed(), blockposition.getX(), blockposition.getZ()); Set> set = new ObjectArraySet(); -@@ -578,9 +581,9 @@ public abstract class ChunkGenerator { +@@ -584,9 +585,7 @@ public abstract class ChunkGenerator { ArrayList arraylist = new ArrayList(list.size()); arraylist.addAll(list); - WorldgenRandom seededrandom = new WorldgenRandom(new LegacyRandomSource(0L)); - - seededrandom.setLargeFeatureSeed(placementCalculator.getLevelSeed(), chunkcoordintpair.x, chunkcoordintpair.z); -+ WorldgenRandom seededrandom = new WorldgenCryptoRandom( -+ chunkcoordintpair.x, chunkcoordintpair.z, Globals.Salt.GENERATE_FEATURE, 0 -+ ); ++ WorldgenRandom seededrandom = new WorldgenCryptoRandom(chunkcoordintpair.x, chunkcoordintpair.z, Globals.Salt.GENERATE_FEATURE, 0); int i = 0; StructureSet.StructureSelectionEntry structureset_a1; diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java -index 798e22fb4d685b5845ebf687e8004e94f13a9751..3ad7297b0e50aa2fd3e13b2ff802d379f588cc76 100644 +index a6b6e5ea191c0e2cd7a2e4f01b89d8af40a83c1b..4d84bf98199e048430cc7560f9002b5ed2cff394 100644 --- a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java +++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java -@@ -37,6 +37,7 @@ import org.slf4j.Logger; - // Spigot start - import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; - import org.spigotmc.SpigotWorldConfig; -+import net.edenor.foldenor.secureseed.*; - // Spigot end - - public class ChunkGeneratorStructureState { -@@ -224,7 +225,7 @@ public class ChunkGeneratorStructureState { +@@ -224,7 +224,7 @@ public class ChunkGeneratorStructureState { List> list = new ArrayList(j); int k = placement.spread(); HolderSet holderset = placement.preferredBiomes(); - RandomSource randomsource = RandomSource.create(); -+ RandomSource randomsource = new WorldgenCryptoRandom(0, 0, Globals.Salt.STRONGHOLDS, 0); ++ RandomSource randomsource = new net.edenor.foldenor.secureseed.WorldgenCryptoRandom(0, 0, net.edenor.foldenor.secureseed.Globals.Salt.STRONGHOLDS, 0); // Paper start - Add missing structure set seed configs if (this.conf.strongholdSeed != null && structureSetEntry.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { -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 a907b79fd8291a0e92db138f37239d17424188a1..61cfd522f8a789beab24e07e5a812c487ac167a4 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkStatus.java -@@ -27,6 +27,7 @@ import net.minecraft.world.level.levelgen.GenerationStep; - import net.minecraft.world.level.levelgen.Heightmap; - import net.minecraft.world.level.levelgen.blending.Blender; - import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; -+import net.edenor.foldenor.secureseed.Globals; - - public class ChunkStatus { - -@@ -253,6 +254,7 @@ public class ChunkStatus { +diff --git a/src/main/java/net/minecraft/world/level/chunk/status/ChunkStatus.java b/src/main/java/net/minecraft/world/level/chunk/status/ChunkStatus.java +index b81c548c0e1ac53784e9c94b34b65db5f123309c..be2b6141c58089d9741c762d6a9f6789d98a0f9f 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/status/ChunkStatus.java ++++ b/src/main/java/net/minecraft/world/level/chunk/status/ChunkStatus.java +@@ -231,6 +231,7 @@ public class ChunkStatus { } - public CompletableFuture> generate(Executor executor, ServerLevel world, ChunkGenerator generator, StructureTemplateManager structureTemplateManager, ThreadedLevelLightEngine lightingProvider, Function>> fullChunkConverter, List chunks) { -+ Globals.setupGlobals(world); - ChunkAccess ichunkaccess = (ChunkAccess) chunks.get(chunks.size() / 2); - ProfiledDuration profiledduration = JvmProfiler.INSTANCE.onChunkGenerate(ichunkaccess.getPos(), world.dimension(), this.toString()); - + public CompletableFuture generate(WorldGenContext context, Executor executor, ToFullChunk fullChunkConverter, List chunks) { ++ net.edenor.foldenor.secureseed.Globals.setupGlobals(context.level()); + ChunkAccess chunkAccess = chunks.get(chunks.size() / 2); + ProfiledDuration profiledDuration = JvmProfiler.INSTANCE.onChunkGenerate(chunkAccess.getPos(), context.level().dimension(), this.toString()); + return this.generationTask.doWork(context, this, executor, fullChunkConverter, chunks, chunkAccess).thenApply(chunk -> { diff --git a/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java b/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java -index d38cabc9eeb45dd863e5f87b7df3b6327ea6a4a2..057e528d22def9ba1e6c4e8dc71049dcfaa8cd33 100644 +index 5ae04ec610a885e2ed73e942879a27fe8640471c..5a04ba253f1fd6327ef21ec2b70cfe0c8b47e86c 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java +++ b/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java -@@ -7,34 +7,54 @@ import java.util.Optional; +@@ -5,34 +5,43 @@ import com.mojang.serialization.MapCodec; + import com.mojang.serialization.codecs.RecordCodecBuilder; + import java.util.Optional; import java.util.OptionalLong; ++import java.util.stream.LongStream; ++ import net.minecraft.util.RandomSource; import org.apache.commons.lang3.StringUtils; -+import java.util.stream.LongStream; +import net.edenor.foldenor.secureseed.Globals; public class WorldOptions { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec((instance) -> { -- return instance.group(Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed), Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldOptions::generateStructures), Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldOptions::generateBonusChest), Codec.STRING.optionalFieldOf("legacy_custom_options").stable().forGetter((generatorOptions) -> { -- return generatorOptions.legacyCustomOptions; -+ return instance.group(Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed), -+ Codec.LONG_STREAM.fieldOf("feature_seed").stable().forGetter(WorldOptions::featureSeedStream), -+ Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldOptions::generateStructures), -+ Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldOptions::generateBonusChest), -+ Codec.STRING.optionalFieldOf("legacy_custom_options").stable().forGetter((generatorOptions) -> { -+ return generatorOptions.legacyCustomOptions; - })).apply(instance, instance.stable(WorldOptions::new)); - }); + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( +- instance -> instance.group( +- Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed), +- Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldOptions::generateStructures), +- Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldOptions::generateBonusChest), +- Codec.STRING.lenientOptionalFieldOf("legacy_custom_options").stable().forGetter(generatorOptions -> generatorOptions.legacyCustomOptions) ++ instance -> instance.group(Codec.LONG.fieldOf("seed").stable().forGetter(WorldOptions::seed), ++ Codec.LONG_STREAM.fieldOf("feature_seed").stable().forGetter(WorldOptions::featureSeedStream), ++ Codec.BOOL.fieldOf("generate_features").orElse(true).stable().forGetter(WorldOptions::generateStructures), ++ Codec.BOOL.fieldOf("bonus_chest").orElse(false).stable().forGetter(WorldOptions::generateBonusChest), ++ Codec.STRING.lenientOptionalFieldOf("legacy_custom_options").stable().forGetter(generatorOptions -> generatorOptions.legacyCustomOptions) + ) + .apply(instance, instance.stable(WorldOptions::new)) + ); - public static final WorldOptions DEMO_OPTIONS = new WorldOptions((long)"North Carolina".hashCode(), true, true); + public static final WorldOptions DEMO_OPTIONS = new WorldOptions((long)"North Carolina".hashCode(), Globals.createRandomWorldSeed(), true, true); private final long seed; @@ -623,33 +577,19 @@ index d38cabc9eeb45dd863e5f87b7df3b6327ea6a4a2..057e528d22def9ba1e6c4e8dc71049dc public static WorldOptions defaultWithRandomSeed() { - return new WorldOptions(randomSeed(), true, false); + return new WorldOptions(randomSeed(), Globals.createRandomWorldSeed(), true, false); ++ } ++ ++ private WorldOptions(long seed, LongStream featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { ++ this(seed, featureSeed.toArray(), generateStructures, bonusChest, legacyCustomOptions); } - private WorldOptions(long seed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { -+ private WorldOptions(long seed, LongStream featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { -+ this(seed, featureSeed.toArray(), generateStructures, bonusChest, legacyCustomOptions); -+ } -+ + private WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { + this.featureSeed = featureSeed; this.seed = seed; this.generateStructures = generateStructures; this.generateBonusChest = bonusChest; - this.legacyCustomOptions = legacyCustomOptions; - } - -+ public long[] featureSeed() { -+ return this.featureSeed; -+ } -+ -+ public LongStream featureSeedStream() { -+ return LongStream.of(this.featureSeed); -+ } -+ - public long seed() { - return this.seed; - } -@@ -52,15 +72,15 @@ public class WorldOptions { +@@ -56,15 +65,15 @@ public class WorldOptions { } public WorldOptions withBonusChest(boolean bonusChest) { @@ -668,102 +608,79 @@ index d38cabc9eeb45dd863e5f87b7df3b6327ea6a4a2..057e528d22def9ba1e6c4e8dc71049dc } public static OptionalLong parseSeed(String seed) { +@@ -83,4 +92,12 @@ public class WorldOptions { + public static long randomSeed() { + return RandomSource.create().nextLong(); + } ++ ++ public long[] featureSeed() { ++ return this.featureSeed; ++ } ++ ++ public LongStream featureSeedStream() { ++ return LongStream.of(this.featureSeed); ++ } + } diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/GeodeFeature.java b/src/main/java/net/minecraft/world/level/levelgen/feature/GeodeFeature.java -index f945fae50983424091b58f83ed14f2e8f2621619..b0bce93e634e6f2cd864beb9ca65febdc981f703 100644 +index 17d2bb3f7d158ec1230a1ad7c52b9feeda586630..f9b38654f223b14392c429d26c2cc2c032de25d6 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/feature/GeodeFeature.java +++ b/src/main/java/net/minecraft/world/level/levelgen/feature/GeodeFeature.java -@@ -5,6 +5,9 @@ import com.mojang.datafixers.util.Pair; - import com.mojang.serialization.Codec; - import java.util.List; - import java.util.function.Predicate; -+ -+import net.edenor.foldenor.secureseed.Globals; -+import net.edenor.foldenor.secureseed.WorldgenCryptoRandom; - import net.minecraft.Util; - import net.minecraft.core.BlockPos; - import net.minecraft.core.Direction; -@@ -42,7 +45,7 @@ public class GeodeFeature extends Feature { +@@ -42,7 +42,7 @@ public class GeodeFeature extends Feature { int j = geodeConfiguration.maxGenOffset; List> list = Lists.newLinkedList(); int k = geodeConfiguration.distributionPoints.sample(randomSource); - WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(worldGenLevel.getSeed())); -+ WorldgenRandom worldgenRandom = new WorldgenCryptoRandom(0, 0, Globals.Salt.GEODE_FEATURE, 0); - NormalNoise normalNoise = NormalNoise.create(worldgenRandom, -4, 1.0D); ++ WorldgenRandom worldgenRandom = new net.edenor.foldenor.secureseed.WorldgenCryptoRandom(0, 0, net.edenor.foldenor.secureseed.Globals.Salt.GEODE_FEATURE, 0); + NormalNoise normalNoise = NormalNoise.create(worldgenRandom, -4, 1.0); List list2 = Lists.newLinkedList(); double d = (double)k / (double)geodeConfiguration.outerWallDistance.getMaxValue(); diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java b/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java -index 8eaa1a57e904fe7e540b311c6c5c36b755f021fc..50e6b3e67821af228114283142390cec4cc07b36 100644 +index 4d7398cbe2c400791e6598c9924202a454cb56ce..1485f6600398929a71e89f7f6a1474c1ec5b15ad 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java +++ b/src/main/java/net/minecraft/world/level/levelgen/structure/Structure.java -@@ -37,6 +37,7 @@ import net.minecraft.world.level.levelgen.WorldgenRandom; - import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer; - import net.minecraft.world.level.levelgen.structure.pieces.StructurePiecesBuilder; - import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; -+import net.edenor.foldenor.secureseed.*; - - public abstract class Structure { - public static final Codec DIRECT_CODEC = BuiltInRegistries.STRUCTURE_TYPE.byNameCodec().dispatch(Structure::type, StructureType::codec); -@@ -164,9 +165,9 @@ public abstract class Structure { +@@ -233,9 +233,9 @@ public abstract class Structure { } private static WorldgenRandom makeRandom(long seed, ChunkPos chunkPos) { - WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); - worldgenRandom.setLargeFeatureSeed(seed, chunkPos.x, chunkPos.z); - return worldgenRandom; -+ return new WorldgenCryptoRandom( -+ chunkPos.x, chunkPos.z, Globals.Salt.GENERATE_FEATURE, seed ++ return new net.edenor.foldenor.secureseed.WorldgenCryptoRandom( ++ chunkPos.x, chunkPos.z, net.edenor.foldenor.secureseed.Globals.Salt.GENERATE_FEATURE, seed + ); } } diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java -index 41e24325dcb37e08936b1d13af34d338487929f3..f699e89371095ad7baa3f243c323d60fa8b6fc3c 100644 +index f873a0a0734b4fe74ba5b5f8ae0cc3c78fa76b9f..77aab9a1a10be0cfad1a3111dd4f935dee2ea15c 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java +++ b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java -@@ -4,6 +4,9 @@ import com.mojang.serialization.Codec; - import com.mojang.serialization.DataResult; - import com.mojang.serialization.codecs.RecordCodecBuilder; - import java.util.Optional; -+ -+import net.edenor.foldenor.secureseed.Globals; -+import net.edenor.foldenor.secureseed.WorldgenCryptoRandom; - import net.minecraft.core.Vec3i; - import net.minecraft.util.ExtraCodecs; - import net.minecraft.world.level.ChunkPos; -@@ -51,8 +54,9 @@ public class RandomSpreadStructurePlacement extends StructurePlacement { +@@ -71,8 +71,9 @@ public class RandomSpreadStructurePlacement extends StructurePlacement { public ChunkPos getPotentialStructureChunk(long seed, int chunkX, int chunkZ) { int i = Math.floorDiv(chunkX, this.spacing); int j = Math.floorDiv(chunkZ, this.spacing); - WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); - worldgenRandom.setLargeFeatureWithSalt(seed, i, j, this.salt()); -+ WorldgenRandom worldgenRandom = new WorldgenCryptoRandom( -+ i, j, Globals.Salt.POTENTIONAL_FEATURE, this.salt ++ WorldgenRandom worldgenRandom = new net.edenor.foldenor.secureseed.WorldgenCryptoRandom( ++ i, j, net.edenor.foldenor.secureseed.Globals.Salt.POTENTIONAL_FEATURE, this.salt + ); int k = this.spacing - this.separation; int l = this.spreadType.evaluate(worldgenRandom, k); int m = this.spreadType.evaluate(worldgenRandom, k); diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java -index 02e58161a0f5915084230831ee03050d762b67d2..75ef65ce0a0a6c9d709faa7f974b068ef2919e10 100644 +index cbf13e4f2da6a27619e9bc9a7cd73bb6e69cad2a..aaff08cb5fab9aca1ef2700fa295210d89b36649 100644 --- a/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java +++ b/src/main/java/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java -@@ -4,6 +4,8 @@ import com.mojang.datafixers.Products; - import com.mojang.serialization.Codec; - import com.mojang.serialization.codecs.RecordCodecBuilder; - import java.util.Optional; -+ -+import net.edenor.foldenor.secureseed.WorldgenCryptoRandom; - import net.minecraft.core.BlockPos; - import net.minecraft.core.Holder; - import net.minecraft.core.Vec3i; -@@ -17,6 +19,7 @@ import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; +@@ -19,6 +19,8 @@ import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; import net.minecraft.world.level.levelgen.LegacyRandomSource; import net.minecraft.world.level.levelgen.WorldgenRandom; import net.minecraft.world.level.levelgen.structure.StructureSet; +import net.edenor.foldenor.secureseed.Globals; ++import net.edenor.foldenor.secureseed.WorldgenCryptoRandom; public abstract class StructurePlacement { - public static final Codec CODEC = BuiltInRegistries.STRUCTURE_PLACEMENT.byNameCodec().dispatch(StructurePlacement::type, StructurePlacementType::codec); -@@ -92,34 +95,32 @@ public abstract class StructurePlacement { + public static final Codec CODEC = BuiltInRegistries.STRUCTURE_PLACEMENT +@@ -118,34 +120,32 @@ public abstract class StructurePlacement { public abstract StructurePlacementType type(); private static boolean probabilityReducer(long seed, int salt, int chunkX, int chunkZ, float frequency, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here @@ -811,7 +728,7 @@ index 02e58161a0f5915084230831ee03050d762b67d2..75ef65ce0a0a6c9d709faa7f974b068e return worldgenRandom.nextInt((int)(1.0F / frequency)) == 0; } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -index dca5f25cf331b5550e9be491b4e8a3466531e021..1393a0cf38f5b13dc5c97bc1984a7c552c8b78ff 100644 +index 82b4bd669c57b18fb0b443bcd94495023cd5a528..894f18d836143ffc76d5d4f48df5f3072b4cbe25 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java @@ -202,7 +202,7 @@ public class CraftChunk implements Chunk { @@ -824,25 +741,15 @@ index dca5f25cf331b5550e9be491b4e8a3466531e021..1393a0cf38f5b13dc5c97bc1984a7c55 @Override diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 0e5af78778cab677c24b508f293cfbdaffb433d6..e0d1a56bbc15b12326aaf55c7d00f068de93c458 100644 +index 4c8598c74f1f56389997e7ea7954318178c4ca69..a6ac60de61a5ea8412abafc8a7e830c9741d8729 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -43,6 +43,9 @@ import java.util.logging.Logger; - import java.util.stream.Collectors; - import javax.imageio.ImageIO; - // import jline.console.ConsoleReader; -+ -+import net.edenor.foldenor.secureseed.Globals; -+import net.minecraft.advancements.Advancement; - import net.minecraft.advancements.AdvancementHolder; - import net.minecraft.commands.CommandSourceStack; - import net.minecraft.commands.Commands; -@@ -1388,7 +1391,7 @@ public final class CraftServer implements Server { - iregistry = leveldataanddimensions.dimensions().dimensions(); +@@ -1390,7 +1390,7 @@ public final class CraftServer implements Server { + iregistrycustom_dimension = leveldataanddimensions.dimensions().dimensionsRegistryAccess(); } else { LevelSettings worldsettings; - WorldOptions worldoptions = new WorldOptions(creator.seed(), creator.generateStructures(), false); -+ WorldOptions worldoptions = new WorldOptions(creator.seed(), Globals.createRandomWorldSeed(), creator.generateStructures(), false); ++ WorldOptions worldoptions = new WorldOptions(creator.seed(), net.edenor.foldenor.secureseed.Globals.createRandomWorldSeed(), creator.generateStructures(), false); WorldDimensions worlddimensions; DedicatedServerProperties.WorldDimensionData properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse((creator.generatorSettings().isEmpty()) ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT)); diff --git a/patches/server/0003-Enable-a-lot-of-commands.patch b/patches/server/0003-Enable-a-lot-of-commands.patch index 6196ae7..bddeb5f 100644 --- a/patches/server/0003-Enable-a-lot-of-commands.patch +++ b/patches/server/0003-Enable-a-lot-of-commands.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Enable-a-lot-of-commands diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java -index 2f4986196f446b789f6a0bcefc9db13f3d34d698..d78af31a54ba38a0a32a6bd0b656e899c0992ce5 100644 +index e53dba6d265007d556d85f6007643b26ba77083b..dc71a4796a526d395ba310f5ab63d2b8db41ecca 100644 --- a/src/main/java/net/minecraft/commands/Commands.java +++ b/src/main/java/net/minecraft/commands/Commands.java -@@ -163,8 +163,8 @@ public class Commands { +@@ -164,8 +164,8 @@ public class Commands { ClearInventoryCommands.register(this.dispatcher, commandRegistryAccess); //CloneCommands.register(this.dispatcher, commandRegistryAccess); // Folia - region threading - TODO DamageCommand.register(this.dispatcher, commandRegistryAccess); @@ -19,7 +19,7 @@ index 2f4986196f446b789f6a0bcefc9db13f3d34d698..d78af31a54ba38a0a32a6bd0b656e899 //DebugCommand.register(this.dispatcher); // Folia - region threading - TODO DefaultGameModeCommands.register(this.dispatcher); DifficultyCommand.register(this.dispatcher); -@@ -175,7 +175,7 @@ public class Commands { +@@ -176,7 +176,7 @@ public class Commands { FillCommand.register(this.dispatcher, commandRegistryAccess); FillBiomeCommand.register(this.dispatcher, commandRegistryAccess); ForceLoadCommand.register(this.dispatcher); @@ -28,7 +28,7 @@ index 2f4986196f446b789f6a0bcefc9db13f3d34d698..d78af31a54ba38a0a32a6bd0b656e899 GameModeCommand.register(this.dispatcher); GameRuleCommand.register(this.dispatcher); GiveCommand.register(this.dispatcher, commandRegistryAccess); -@@ -194,19 +194,19 @@ public class Commands { +@@ -195,19 +195,19 @@ public class Commands { //ReloadCommand.register(this.dispatcher); // Folia - region threading RecipeCommand.register(this.dispatcher); //ReturnCommand.register(this.dispatcher); // Folia - region threading - TODO later @@ -36,8 +36,8 @@ index 2f4986196f446b789f6a0bcefc9db13f3d34d698..d78af31a54ba38a0a32a6bd0b656e899 + RideCommand.register(this.dispatcher); // Folia - region threading - TODO later SayCommand.register(this.dispatcher); //ScheduleCommand.register(this.dispatcher); // Folia - region threading -- //ScoreboardCommand.register(this.dispatcher); // Folia - region threading - TODO later -+ ScoreboardCommand.register(this.dispatcher); // Folia - region threading - TODO later +- //ScoreboardCommand.register(this.dispatcher, commandRegistryAccess); // Folia - region threading - TODO later ++ ScoreboardCommand.register(this.dispatcher, commandRegistryAccess); // Folia - region threading - TODO later SeedCommand.register(this.dispatcher, environment != Commands.CommandSelection.INTEGRATED); SetBlockCommand.register(this.dispatcher, commandRegistryAccess); SetSpawnCommand.register(this.dispatcher); @@ -49,6 +49,6 @@ index 2f4986196f446b789f6a0bcefc9db13f3d34d698..d78af31a54ba38a0a32a6bd0b656e899 SummonCommand.register(this.dispatcher, commandRegistryAccess); - //TagCommand.register(this.dispatcher); // Folia - region threading - TODO later + TagCommand.register(this.dispatcher); // Folia - region threading - TODO later - //TeamCommand.register(this.dispatcher); // Folia - region threading - TODO later + //TeamCommand.register(this.dispatcher, commandRegistryAccess); // Folia - region threading - TODO later //TeamMsgCommand.register(this.dispatcher); // Folia - region threading - TODO later TeleportCommand.register(this.dispatcher); diff --git a/patches/server/0005-Add-config.patch b/patches/server/0005-Add-config.patch index 826e6d9..27fc0bd 100644 --- a/patches/server/0005-Add-config.patch +++ b/patches/server/0005-Add-config.patch @@ -194,19 +194,18 @@ index 0000000000000000000000000000000000000000..748a78a092067ccb2c9ebb6508607651 + } +} diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index 6b3242117d62175de462d04e4d178b49ae2521e9..dfe708b4be2e05be568ba88058fe3dfee8757ba7 100644 +index 05bc06fcf508e74c717a7f71a2c7ed7c568e846a..14f0eaf4f579713b1bacbfa655aa1b46d7b0ac8a 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -19,6 +19,8 @@ import java.util.Locale; +@@ -19,6 +19,7 @@ import java.util.Locale; import java.util.Optional; import java.util.function.BooleanSupplier; import javax.annotation.Nullable; -+ +import net.edenor.foldenor.config.FoldenorConfig; import net.minecraft.DefaultUncaughtExceptionHandler; import net.minecraft.DefaultUncaughtExceptionHandlerWithName; import net.minecraft.SharedConstants; -@@ -221,6 +223,12 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface +@@ -234,6 +235,12 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // Paper - init PaperBrigadierProvider @@ -220,29 +219,21 @@ index 6b3242117d62175de462d04e4d178b49ae2521e9..dfe708b4be2e05be568ba88058fe3dfe this.setPvpAllowed(dedicatedserverproperties.pvp); this.setFlightAllowed(dedicatedserverproperties.allowFlight); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index e0d1a56bbc15b12326aaf55c7d00f068de93c458..e269e7449bedacba20708353c17d9d98e1bf501d 100644 +index a6ac60de61a5ea8412abafc8a7e830c9741d8729..fb36c72d34ff6b7c4c685cd84fca59b2a8f0fdb9 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -44,6 +44,7 @@ import java.util.stream.Collectors; - import javax.imageio.ImageIO; - // import jline.console.ConsoleReader; - -+import net.edenor.foldenor.config.FoldenorConfig; - import net.edenor.foldenor.secureseed.Globals; - import net.minecraft.advancements.Advancement; - import net.minecraft.advancements.AdvancementHolder; -@@ -326,6 +327,10 @@ public final class CraftServer implements Server { +@@ -325,6 +325,10 @@ public final class CraftServer implements Server { return this.asyncScheduler; } + public YamlConfiguration getFoldenorConfig() { -+ return FoldenorConfig.config; ++ return net.edenor.foldenor.config.FoldenorConfig.config; + } + @Override public final io.papermc.paper.threadedregions.scheduler.FoliaGlobalRegionScheduler getGlobalRegionScheduler() { return this.globalRegionScheduler; -@@ -1100,6 +1105,7 @@ public final class CraftServer implements Server { +@@ -1097,6 +1101,7 @@ public final class CraftServer implements Server { org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot this.console.paperConfigurations.reloadConfigs(this.console); @@ -251,10 +242,10 @@ index e0d1a56bbc15b12326aaf55c7d00f068de93c458..e269e7449bedacba20708353c17d9d98 // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index 8d626fadcd4743b6472a2954d2b1b2ec89669814..395ff7a63ea214e4b96be3a6274eb68b4a3cfb34 100644 +index c988afa496d25314451435eedd64079a0d87cef0..a89e24b3a69c613330f2976a69a4313fb707a9e5 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -174,6 +174,12 @@ public class Main { +@@ -175,6 +175,12 @@ public class Main { .describedAs("Jar file"); // Paper end diff --git a/patches/server/0006-Reenable-command-blocks.patch b/patches/server/0006-Reenable-command-blocks.patch index b7ffd5f..abde142 100644 --- a/patches/server/0006-Reenable-command-blocks.patch +++ b/patches/server/0006-Reenable-command-blocks.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Reenable-command-blocks diff --git a/src/main/java/net/minecraft/world/level/BaseCommandBlock.java b/src/main/java/net/minecraft/world/level/BaseCommandBlock.java -index b7ee6b31d03cd554b229b440b1d5ef6c9ddd2fb5..2eb0ef2ea2d86769bbbf87f767df3ea2c2574a83 100644 +index b8a571fcc44f67ac8f6089e039a8620bf6c40603..d39ed96d2edb4c7133312e54d296ea1da1a1bb4e 100644 --- a/src/main/java/net/minecraft/world/level/BaseCommandBlock.java +++ b/src/main/java/net/minecraft/world/level/BaseCommandBlock.java -@@ -111,7 +111,7 @@ public abstract class BaseCommandBlock implements CommandSource { +@@ -117,7 +117,7 @@ public abstract class BaseCommandBlock implements CommandSource { } public boolean performCommand(Level world) { diff --git a/patches/server/0007-Use-thread-unsafe-random-for-mob-spawning.patch b/patches/server/0007-Use-thread-unsafe-random-for-mob-spawning.patch index 5294429..0d923bb 100644 --- a/patches/server/0007-Use-thread-unsafe-random-for-mob-spawning.patch +++ b/patches/server/0007-Use-thread-unsafe-random-for-mob-spawning.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Use-thread-unsafe-random-for-mob-spawning diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index fab03f244f90e479ce7744a568e999327499b7fe..8f8f6d068058b15e7854b9f91ba2ce9a48f2c7a5 100644 +index 8d1d64841b8c6dbc7f34c6d1aab5d779764b8fad..5d9b9a04dc922ce6f940df3cfe47a0a9bcb92e40 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -1114,7 +1114,8 @@ public class ServerLevel extends Level implements WorldGenLevel { +@@ -1119,7 +1119,8 @@ public class ServerLevel extends Level implements WorldGenLevel { } // Paper start - optimise random block ticking private final ThreadLocal chunkTickMutablePosition = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos()); // Folia - region threading @@ -19,10 +19,10 @@ index fab03f244f90e479ce7744a568e999327499b7fe..8f8f6d068058b15e7854b9f91ba2ce9a public void tickChunk(LevelChunk chunk, int randomTickSpeed) { diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index 9c1263fdc3731e04ac28c6d0ed904ecdc3ea3038..a664a701abf2dd011027be157c5839163e3a8a70 100644 +index db64d88dc26f10304bd10f771f811fcbfaa34bc8..5ade8de03794ca19bd04cf29220bd56911110609 100644 --- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java +++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -425,12 +425,12 @@ public final class NaturalSpawner { +@@ -408,12 +408,12 @@ public final class NaturalSpawner { } } diff --git a/patches/server/0008-Do-not-send-chunk-if-it-was-already-sent.patch b/patches/server/0008-Do-not-send-chunk-if-it-was-already-sent.patch index de23f21..88b2ddf 100644 --- a/patches/server/0008-Do-not-send-chunk-if-it-was-already-sent.patch +++ b/patches/server/0008-Do-not-send-chunk-if-it-was-already-sent.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Do-not-send-chunk-if-it-was-already-sent diff --git a/src/main/java/io/papermc/paper/chunk/system/RegionizedPlayerChunkLoader.java b/src/main/java/io/papermc/paper/chunk/system/RegionizedPlayerChunkLoader.java -index 07abf5a326cc7aa8a449b74bd7ac8a43b98528c0..8c2357a79904da05bdb4fbbbc9230664aae809a5 100644 +index 239475187fdf0d05823b31e2068651559d3497e5..d4ded2324d4c1c2ec3a105fa24facef1d5d57cdc 100644 --- a/src/main/java/io/papermc/paper/chunk/system/RegionizedPlayerChunkLoader.java +++ b/src/main/java/io/papermc/paper/chunk/system/RegionizedPlayerChunkLoader.java -@@ -480,6 +480,10 @@ public class RegionizedPlayerChunkLoader { +@@ -421,6 +421,10 @@ public class RegionizedPlayerChunkLoader { } private void sendChunk(final int chunkX, final int chunkZ) { diff --git a/patches/server/0009-Teleport-async-if-we-cannot-move-entity-off-main.patch b/patches/server/0009-Teleport-async-if-we-cannot-move-entity-off-main.patch index 216e043..bd705a2 100644 --- a/patches/server/0009-Teleport-async-if-we-cannot-move-entity-off-main.patch +++ b/patches/server/0009-Teleport-async-if-we-cannot-move-entity-off-main.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Teleport-async-if-we-cannot-move-entity-off-main diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 1d77bc6bdb87de1ad6c8740376391dedcaa73089..051498d33e206fb8b90df3ceb9e66d6c9d7c366d 100644 +index d8be7c8dca41b6ad6d9a28590be9e33ef9045bdb..9999bcdd1ba91326c2a8ec3ac1d9246630ab86a2 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -1138,7 +1138,16 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S +@@ -1180,7 +1180,16 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } } @@ -26,7 +26,7 @@ index 1d77bc6bdb87de1ad6c8740376391dedcaa73089..051498d33e206fb8b90df3ceb9e66d6c } this.level().getProfiler().pop(); -@@ -3990,13 +3999,13 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S +@@ -4037,13 +4046,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess // check for same region if (destination == this.level()) { Vec3 currPos = this.position(); diff --git a/patches/server/0010-Send-null-entity-packets-and-vanilla-end-portal-tele.patch b/patches/server/0010-Send-null-entity-packets-and-vanilla-end-portal-tele.patch index f475ae6..184a9e3 100644 --- a/patches/server/0010-Send-null-entity-packets-and-vanilla-end-portal-tele.patch +++ b/patches/server/0010-Send-null-entity-packets-and-vanilla-end-portal-tele.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Send-null-entity-packets-and-vanilla-end-portal-teleportation diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index 16373e0c5ea38199fab3eb289bf2a5fcf0dd7439..900df4730d5c231197b4c509ee27fc170fcf9223 100644 +index f16a69775332a08ed0e87d27acd0fc959359694c..0e8d31d5e012a91ae4ea5027899e3b6969024eff 100644 --- a/src/main/java/net/minecraft/server/level/ServerEntity.java +++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -46,6 +46,7 @@ import net.minecraft.server.network.ServerPlayerConnection; +@@ -50,6 +50,7 @@ import net.minecraft.server.network.ServerPlayerConnection; import net.minecraft.util.Mth; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerVelocityEvent; @@ -26,7 +26,7 @@ index 16373e0c5ea38199fab3eb289bf2a5fcf0dd7439..900df4730d5c231197b4c509ee27fc17 } if ((this.trackDelta || this.entity.hasImpulse || this.entity instanceof LivingEntity && ((LivingEntity) this.entity).isFallFlying()) && this.tickCount > 0) { -@@ -278,6 +282,18 @@ public class ServerEntity { +@@ -285,6 +289,18 @@ public class ServerEntity { } @@ -46,10 +46,10 @@ index 16373e0c5ea38199fab3eb289bf2a5fcf0dd7439..900df4730d5c231197b4c509ee27fc17 return lastPassengers.stream().filter((entity) -> { return !passengers.contains(entity); diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 051498d33e206fb8b90df3ceb9e66d6c9d7c366d..b92734449af7917475a20c20a7833315af8e095b 100644 +index 9999bcdd1ba91326c2a8ec3ac1d9246630ab86a2..2ba0a47d1c77027ba64166bce2216de0871d97d1 100644 --- a/src/main/java/net/minecraft/world/entity/Entity.java +++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -24,6 +24,8 @@ import java.util.function.BiConsumer; +@@ -23,6 +23,8 @@ import java.util.function.BiConsumer; import java.util.function.Predicate; import java.util.stream.Stream; import javax.annotation.Nullable; @@ -58,7 +58,7 @@ index 051498d33e206fb8b90df3ceb9e66d6c9d7c366d..b92734449af7917475a20c20a7833315 import net.minecraft.BlockUtil; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; -@@ -4163,9 +4165,22 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S +@@ -4210,9 +4212,22 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess // the portal obsidian is placed at targetPos.y - 2, so if we want to place the entity // on the obsidian, we need to spawn at targetPos.y - 1 diff --git a/patches/server/0011-Add-Foldenor-commands.patch b/patches/server/0011-Add-Foldenor-commands.patch index fb6f86d..41ed908 100644 --- a/patches/server/0011-Add-Foldenor-commands.patch +++ b/patches/server/0011-Add-Foldenor-commands.patch @@ -158,10 +158,10 @@ index 0000000000000000000000000000000000000000..4188e8db6386fe1790192c9efd8c2fe2 + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index e269e7449bedacba20708353c17d9d98e1bf501d..51174f8969f264ed611fb5f68b8c3d0c73137f03 100644 +index fb36c72d34ff6b7c4c685cd84fca59b2a8f0fdb9..c07114a23257fb603c66de7e230705ed81d07be6 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1136,6 +1136,7 @@ public final class CraftServer implements Server { +@@ -1132,6 +1132,7 @@ public final class CraftServer implements Server { this.reloadData(); org.spigotmc.SpigotConfig.registerCommands(); // Spigot io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper diff --git a/patches/server/0012-Redirect-getTPS-function-to-tegion-tps-or-return-20-.patch b/patches/server/0012-Redirect-getTPS-function-to-tegion-tps-or-return-20-.patch index 88256f5..a53c47b 100644 --- a/patches/server/0012-Redirect-getTPS-function-to-tegion-tps-or-return-20-.patch +++ b/patches/server/0012-Redirect-getTPS-function-to-tegion-tps-or-return-20-.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Redirect-getTPS-function-to-tegion-tps-or-return-20-tps diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 51174f8969f264ed611fb5f68b8c3d0c73137f03..31422c71d0a6e88f9d61dea465bea49e7c5b1638 100644 +index c07114a23257fb603c66de7e230705ed81d07be6..935f753d7771336808309616c794d8b6c2c3453b 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -14,6 +14,9 @@ import com.mojang.brigadier.tree.CommandNode; @@ -18,7 +18,7 @@ index 51174f8969f264ed611fb5f68b8c3d0c73137f03..31422c71d0a6e88f9d61dea465bea49e import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; -@@ -3111,11 +3114,21 @@ public final class CraftServer implements Server { +@@ -3127,11 +3130,21 @@ public final class CraftServer implements Server { @Override public double[] getTPS() { diff --git a/patches/server/0014-Linear-Region-Format.patch b/patches/server/0014-Linear-Region-Format.patch deleted file mode 100644 index 92c14f9..0000000 --- a/patches/server/0014-Linear-Region-Format.patch +++ /dev/null @@ -1,1176 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Mon, 20 Nov 2023 18:03:37 +0400 -Subject: [PATCH] Linear-Region-Format - - -diff --git a/build.gradle.kts b/build.gradle.kts -index 4c01b60d00f5809dc36c0a6500b2abe3fa58fefd..19a70552d738cad8750547c10be550e223a9a9e1 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -19,6 +19,8 @@ dependencies { - exclude("io.papermc.paper", "paper-api") - } - // Folia end -+ implementation("com.github.luben:zstd-jni:1.5.4-1") -+ implementation("org.lz4:lz4-java:1.8.0") - // Paper start - implementation("org.jline:jline-terminal-jansi:3.21.0") - implementation("net.minecrell:terminalconsoleappender:1.3.0") -diff --git a/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java -index 2934f0cf0ef09c84739312b00186c2ef0019a165..3c987c1395a2791469552de6775f91d6c8061f37 100644 ---- a/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java -+++ b/src/main/java/io/papermc/paper/chunk/system/io/RegionFileIOThread.java -@@ -816,7 +816,7 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread { - final ChunkDataController taskController) { - final ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); - if (intendingToBlock) { -- return taskController.computeForRegionFile(chunkX, chunkZ, true, (final RegionFile file) -> { -+ return taskController.computeForRegionFile(chunkX, chunkZ, true, (final net.edenor.foldenor.region.AbstractRegionFile file) -> { - if (file == null) { // null if no regionfile exists - return Boolean.FALSE; - } -@@ -829,7 +829,7 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread { - return Boolean.FALSE; - } // else: it either exists or is not known, fall back to checking the loaded region file - -- return taskController.computeForRegionFileIfLoaded(chunkX, chunkZ, (final RegionFile file) -> { -+ return taskController.computeForRegionFileIfLoaded(chunkX, chunkZ, (final net.edenor.foldenor.region.AbstractRegionFile file) -> { - if (file == null) { // null if not loaded - // not sure at this point, let the I/O thread figure it out - return Boolean.TRUE; -@@ -1131,9 +1131,9 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread { - return this.getCache().doesRegionFileNotExistNoIO(new ChunkPos(chunkX, chunkZ)); - } - -- public T computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function function) { -+ public T computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function function) { - final RegionFileStorage cache = this.getCache(); -- final RegionFile regionFile; -+ final net.edenor.foldenor.region.AbstractRegionFile regionFile; - synchronized (cache) { - try { - regionFile = cache.getRegionFile(new ChunkPos(chunkX, chunkZ), existingOnly, true); -@@ -1146,19 +1146,19 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread { - return function.apply(regionFile); - } finally { - if (regionFile != null) { -- regionFile.fileLock.unlock(); -+ regionFile.getFileLock().unlock(); - } - } - } - -- public T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function function) { -+ public T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function function) { - final RegionFileStorage cache = this.getCache(); -- final RegionFile regionFile; -+ final net.edenor.foldenor.region.AbstractRegionFile regionFile; - - synchronized (cache) { - regionFile = cache.getRegionFileIfLoaded(new ChunkPos(chunkX, chunkZ)); - if (regionFile != null) { -- regionFile.fileLock.lock(); -+ regionFile.getFileLock().lock(); - } - } - -@@ -1166,7 +1166,7 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread { - return function.apply(regionFile); - } finally { - if (regionFile != null) { -- regionFile.fileLock.unlock(); -+ regionFile.getFileLock().unlock(); - } - } - } -diff --git a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java -index 9017907c0ec67a37a506f09b7e4499cef7885279..3a53ddb4ee0e2e70a045c84f84e690d9326a9cdc 100644 ---- a/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java -+++ b/src/main/java/io/papermc/paper/world/ThreadedWorldUpgrader.java -@@ -2,6 +2,7 @@ package io.papermc.paper.world; - - import com.mojang.datafixers.DataFixer; - import com.mojang.serialization.Codec; -+import net.edenor.foldenor.config.FoldenorConfig; - import net.minecraft.SharedConstants; - import net.minecraft.nbt.CompoundTag; - import net.minecraft.resources.ResourceKey; -@@ -84,8 +85,12 @@ public class ThreadedWorldUpgrader { - LOGGER.info("Found " + regionFiles.length + " regionfiles to convert"); - LOGGER.info("Starting conversion now for world " + this.worldName); - -+ net.edenor.foldenor.region.RegionFileFormat formatName = FoldenorConfig.regionFormatName; -+ int linearCompression = FoldenorConfig.regionFormatLinearCompressionLevel; -+ LOGGER.info("Using format " + formatName + " (" + linearCompression + ")"); -+ - final WorldInfo info = new WorldInfo(() -> worldPersistentData, -- new ChunkStorage(regionFolder.toPath(), this.dataFixer, false), this.removeCaches, this.dimensionType, this.generatorKey); -+ new ChunkStorage(formatName, linearCompression, regionFolder.toPath(), this.dataFixer, false), this.removeCaches, this.dimensionType, this.generatorKey); - - long expectedChunks = (long)regionFiles.length * (32L * 32L); - -diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -index 748a78a092067ccb2c9ebb650860765180703b8f..9a67b0107f4b79911b4d1e676e4d97c7ff8466a9 100644 ---- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -+++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -2,6 +2,7 @@ package net.edenor.foldenor.config; - - import com.google.common.base.Throwables; - import com.google.common.collect.ImmutableMap; -+import net.edenor.foldenor.region.RegionFileFormat; - import org.bukkit.Bukkit; - import org.bukkit.configuration.ConfigurationSection; - import org.bukkit.configuration.InvalidConfigurationException; -@@ -31,6 +32,11 @@ public class FoldenorConfig { - - public static int maxProjectileLoadsPerTick = 10; - -+ public static RegionFileFormat regionFormatName = RegionFileFormat.ANVIL; -+ public static int regionFormatLinearCompressionLevel = 1; -+ public static int linearFlushFrequency = 10; -+ public static int linearFlushThreads = 1; -+ - public static void init(File configFile) { - init(configFile, true); - } -@@ -77,6 +83,8 @@ public class FoldenorConfig { - - otherSettings(); - -+ worldSettings(); -+ - try { - config.save(CONFIG_FILE); - } catch (IOException ex) { -@@ -84,6 +92,28 @@ public class FoldenorConfig { - } - } - -+ private static void worldSettings(){ -+ regionFormatName = RegionFileFormat.fromString(getString("world.region.format", regionFormatName.name())); -+ if (regionFormatName.equals(RegionFileFormat.INVALID)) { -+ log(Level.SEVERE, "Unknown region format in foldenor.yml: " + regionFormatName); -+ log(Level.SEVERE, "Falling back to ANVIL region file format."); -+ regionFormatName = RegionFileFormat.ANVIL; -+ } -+ regionFormatLinearCompressionLevel = getInt("world.region.linear.compression-level", regionFormatLinearCompressionLevel); -+ if (regionFormatLinearCompressionLevel > 23 || regionFormatLinearCompressionLevel < 1) { -+ log(Level.SEVERE, "Linear region compression level should be between 1 and 22 in foldenor.yml: " + regionFormatLinearCompressionLevel); -+ log(Level.SEVERE, "Falling back to compression level 1."); -+ regionFormatLinearCompressionLevel = 1; -+ } -+ -+ linearFlushFrequency = getInt("world.region.linear.flush-frequency", linearFlushFrequency); -+ linearFlushThreads = getInt("world.region.linear.flush-max-threads", linearFlushThreads); -+ if (linearFlushThreads < 0) -+ linearFlushThreads = Math.max(Runtime.getRuntime().availableProcessors() + linearFlushThreads, 1); -+ else -+ linearFlushThreads = Math.max(linearFlushThreads, 1); -+ } -+ - private static void otherSettings() { - vanilaEndPortalTeleportation = getBoolean("other.vanila-end-portal-teleportation", vanilaEndPortalTeleportation); - } -diff --git a/src/main/java/net/edenor/foldenor/region/AbstractRegionFile.java b/src/main/java/net/edenor/foldenor/region/AbstractRegionFile.java -new file mode 100644 -index 0000000000000000000000000000000000000000..5d68263516d29500916135f2d124a712b9d2c578 ---- /dev/null -+++ b/src/main/java/net/edenor/foldenor/region/AbstractRegionFile.java -@@ -0,0 +1,31 @@ -+package net.edenor.foldenor.region; -+ -+import net.minecraft.nbt.CompoundTag; -+import net.minecraft.world.level.ChunkPos; -+import net.minecraft.world.level.chunk.ChunkStatus; -+ -+import java.io.DataInputStream; -+import java.io.DataOutputStream; -+import java.io.IOException; -+import java.nio.file.Path; -+import java.util.concurrent.locks.ReentrantLock; -+ -+public interface AbstractRegionFile { -+ void flush() throws IOException; -+ void clear(ChunkPos pos) throws IOException; -+ void close() throws IOException; -+ void setStatus(int x, int z, ChunkStatus status); -+ void setOversized(int x, int z, boolean b) throws IOException; -+ -+ boolean hasChunk(ChunkPos pos); -+ boolean doesChunkExist(ChunkPos pos) throws Exception; -+ boolean isOversized(int x, int z); -+ boolean recalculateHeader() throws IOException; -+ -+ DataOutputStream getChunkDataOutputStream(ChunkPos pos) throws IOException; -+ DataInputStream getChunkDataInputStream(ChunkPos pos) throws IOException; -+ CompoundTag getOversizedData(int x, int z) throws IOException; -+ ChunkStatus getStatusIfCached(int x, int z); -+ ReentrantLock getFileLock(); -+ Path getRegionFile(); -+} -diff --git a/src/main/java/net/edenor/foldenor/region/AbstractRegionFileFactory.java b/src/main/java/net/edenor/foldenor/region/AbstractRegionFileFactory.java -new file mode 100644 -index 0000000000000000000000000000000000000000..7769942b840641857614061a6fa834676dcb9721 ---- /dev/null -+++ b/src/main/java/net/edenor/foldenor/region/AbstractRegionFileFactory.java -@@ -0,0 +1,31 @@ -+package net.edenor.foldenor.region; -+ -+import net.minecraft.world.level.chunk.storage.RegionFile; -+import net.minecraft.world.level.chunk.storage.RegionFileVersion; -+ -+ -+import java.io.IOException; -+import java.nio.file.Path; -+ -+public class AbstractRegionFileFactory { -+ -+ public static AbstractRegionFile getAbstractRegionFile(int linearCompression, Path file, Path directory, boolean dsync) throws IOException { -+ return getAbstractRegionFile(linearCompression, file, directory, RegionFileVersion.VERSION_DEFLATE, dsync); -+ } -+ -+ public static AbstractRegionFile getAbstractRegionFile(int linearCompression, Path file, Path directory, boolean dsync, boolean canRecalcHeader) throws IOException { -+ return getAbstractRegionFile(linearCompression, file, directory, RegionFileVersion.VERSION_DEFLATE, dsync, canRecalcHeader); -+ } -+ -+ public static AbstractRegionFile getAbstractRegionFile(int linearCompression, Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync) throws IOException { -+ return getAbstractRegionFile(linearCompression, file, directory, outputChunkStreamVersion, dsync, false); -+ } -+ -+ public static AbstractRegionFile getAbstractRegionFile(int linearCompression, Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync, boolean canRecalcHeader) throws IOException { -+ if (file.toString().endsWith(".linear")) { -+ return new LinearRegionFile(file, linearCompression); -+ } else { -+ return new RegionFile(file, directory, outputChunkStreamVersion, dsync, canRecalcHeader); -+ } -+ } -+} -diff --git a/src/main/java/net/edenor/foldenor/region/LinearRegionFile.java b/src/main/java/net/edenor/foldenor/region/LinearRegionFile.java -new file mode 100644 -index 0000000000000000000000000000000000000000..7246ede75b9633c93049a5f010cb8cd92cd0e464 ---- /dev/null -+++ b/src/main/java/net/edenor/foldenor/region/LinearRegionFile.java -@@ -0,0 +1,317 @@ -+package net.edenor.foldenor.region; -+ -+import com.github.luben.zstd.ZstdInputStream; -+import com.github.luben.zstd.ZstdOutputStream; -+import com.mojang.logging.LogUtils; -+import net.jpountz.lz4.LZ4Compressor; -+import net.jpountz.lz4.LZ4Factory; -+import net.jpountz.lz4.LZ4FastDecompressor; -+import net.minecraft.nbt.CompoundTag; -+import net.minecraft.world.level.ChunkPos; -+import net.minecraft.world.level.chunk.ChunkStatus; -+import org.slf4j.Logger; -+ -+import javax.annotation.Nullable; -+import java.io.*; -+import java.nio.ByteBuffer; -+import java.nio.file.Files; -+import java.nio.file.Path; -+import java.nio.file.StandardCopyOption; -+import java.util.ArrayList; -+import java.util.Arrays; -+import java.util.List; -+import java.util.concurrent.atomic.AtomicBoolean; -+import java.util.concurrent.locks.ReentrantLock; -+ -+public class LinearRegionFile implements AbstractRegionFile, AutoCloseable { -+ private static final long SUPERBLOCK = -4323716122432332390L; -+ private static final byte VERSION = 2; -+ private static final int HEADER_SIZE = 32; -+ private static final int FOOTER_SIZE = 8; -+ private static final Logger LOGGER = LogUtils.getLogger(); -+ private static final List SUPPORTED_VERSIONS = Arrays.asList((byte) 1, (byte) 2); -+ private static final LinearRegionFileFlusher linearRegionFileFlusher = new LinearRegionFileFlusher(); -+ -+ private final byte[][] buffer = new byte[1024][]; -+ private final int[] bufferUncompressedSize = new int[1024]; -+ -+ private final int[] chunkTimestamps = new int[1024]; -+ private final ChunkStatus[] statuses = new ChunkStatus[1024]; -+ -+ private final LZ4Compressor compressor; -+ private final LZ4FastDecompressor decompressor; -+ -+ public final ReentrantLock fileLock = new ReentrantLock(true); -+ private final int compressionLevel; -+ -+ private AtomicBoolean markedToSave = new AtomicBoolean(false); -+ public boolean closed = false; -+ public Path path; -+ -+ -+ public LinearRegionFile(Path file, int compression) throws IOException { -+ this.path = file; -+ this.compressionLevel = compression; -+ this.compressor = LZ4Factory.fastestInstance().fastCompressor(); -+ this.decompressor = LZ4Factory.fastestInstance().fastDecompressor(); -+ -+ File regionFile = new File(this.path.toString()); -+ -+ Arrays.fill(this.bufferUncompressedSize, 0); -+ -+ if (!regionFile.canRead()) return; -+ -+ try (FileInputStream fileStream = new FileInputStream(regionFile); -+ DataInputStream rawDataStream = new DataInputStream(fileStream)) { -+ -+ long superBlock = rawDataStream.readLong(); -+ if (superBlock != SUPERBLOCK) -+ throw new RuntimeException("Invalid superblock: " + superBlock + " in " + file); -+ -+ byte version = rawDataStream.readByte(); -+ if (!SUPPORTED_VERSIONS.contains(version)) -+ throw new RuntimeException("Invalid version: " + version + " in " + file); -+ -+ // Skip newestTimestamp (Long) + Compression level (Byte) + Chunk count (Short): Unused. -+ rawDataStream.skipBytes(11); -+ -+ int dataCount = rawDataStream.readInt(); -+ long fileLength = file.toFile().length(); -+ if (fileLength != HEADER_SIZE + dataCount + FOOTER_SIZE) -+ throw new IOException("Invalid file length: " + this.path + " " + fileLength + " " + (HEADER_SIZE + dataCount + FOOTER_SIZE)); -+ -+ rawDataStream.skipBytes(8); // Skip data hash (Long): Unused. -+ -+ byte[] rawCompressed = new byte[dataCount]; -+ rawDataStream.readFully(rawCompressed, 0, dataCount); -+ -+ superBlock = rawDataStream.readLong(); -+ if (superBlock != SUPERBLOCK) -+ throw new IOException("Footer superblock invalid " + this.path); -+ -+ try (DataInputStream dataStream = new DataInputStream(new ZstdInputStream(new ByteArrayInputStream(rawCompressed)))) { -+ -+ int[] starts = new int[1024]; -+ for (int i = 0; i < 1024; i++) { -+ starts[i] = dataStream.readInt(); -+ dataStream.skipBytes(4); // Skip timestamps (Int): Unused. -+ } -+ -+ for (int i = 0; i < 1024; i++) { -+ if (starts[i] > 0) { -+ int size = starts[i]; -+ byte[] b = new byte[size]; -+ dataStream.readFully(b, 0, size); -+ -+ int maxCompressedLength = this.compressor.maxCompressedLength(size); -+ byte[] compressed = new byte[maxCompressedLength]; -+ int compressedLength = this.compressor.compress(b, 0, size, compressed, 0, maxCompressedLength); -+ b = new byte[compressedLength]; -+ System.arraycopy(compressed, 0, b, 0, compressedLength); -+ -+ this.buffer[i] = b; -+ this.bufferUncompressedSize[i] = size; -+ } -+ } -+ } -+ } -+ } -+ -+ public Path getRegionFile() { -+ return this.path; -+ } -+ -+ public ReentrantLock getFileLock() { -+ return this.fileLock; -+ } -+ -+ public void flush() throws IOException { -+ if (isMarkedToSave()) flushWrapper(); // sync -+ } -+ -+ private void markToSave() { -+ linearRegionFileFlusher.scheduleSave(this); -+ markedToSave.set(true); -+ } -+ -+ public boolean isMarkedToSave() { -+ return markedToSave.getAndSet(false); -+ } -+ -+ public void flushWrapper() { -+ try { -+ save(); -+ } catch (IOException e) { -+ LOGGER.error("Failed to flush region file " + path.toAbsolutePath(), e); -+ } -+ } -+ -+ public boolean doesChunkExist(ChunkPos pos) throws Exception { -+ throw new Exception("doesChunkExist is a stub"); -+ } -+ -+ private synchronized void save() throws IOException { -+ long timestamp = getTimestamp(); -+ short chunkCount = 0; -+ -+ File tempFile = new File(path.toString() + ".tmp"); -+ -+ try (FileOutputStream fileStream = new FileOutputStream(tempFile); -+ ByteArrayOutputStream zstdByteArray = new ByteArrayOutputStream(); -+ ZstdOutputStream zstdStream = new ZstdOutputStream(zstdByteArray, this.compressionLevel); -+ DataOutputStream zstdDataStream = new DataOutputStream(zstdStream); -+ DataOutputStream dataStream = new DataOutputStream(fileStream)) { -+ -+ dataStream.writeLong(SUPERBLOCK); -+ dataStream.writeByte(VERSION); -+ dataStream.writeLong(timestamp); -+ dataStream.writeByte(this.compressionLevel); -+ -+ ArrayList byteBuffers = new ArrayList<>(); -+ for (int i = 0; i < 1024; i++) { -+ if (this.bufferUncompressedSize[i] != 0) { -+ chunkCount += 1; -+ byte[] content = new byte[bufferUncompressedSize[i]]; -+ this.decompressor.decompress(buffer[i], 0, content, 0, bufferUncompressedSize[i]); -+ -+ byteBuffers.add(content); -+ } else byteBuffers.add(null); -+ } -+ for (int i = 0; i < 1024; i++) { -+ zstdDataStream.writeInt(this.bufferUncompressedSize[i]); // Write uncompressed size -+ zstdDataStream.writeInt(this.chunkTimestamps[i]); // Write timestamp -+ } -+ for (int i = 0; i < 1024; i++) { -+ if (byteBuffers.get(i) != null) -+ zstdDataStream.write(byteBuffers.get(i), 0, byteBuffers.get(i).length); -+ } -+ zstdDataStream.close(); -+ -+ dataStream.writeShort(chunkCount); -+ -+ byte[] compressed = zstdByteArray.toByteArray(); -+ -+ dataStream.writeInt(compressed.length); -+ dataStream.writeLong(0); -+ -+ dataStream.write(compressed, 0, compressed.length); -+ dataStream.writeLong(SUPERBLOCK); -+ -+ dataStream.flush(); -+ fileStream.getFD().sync(); -+ fileStream.getChannel().force(true); // Ensure atomicity on Btrfs -+ } -+ Files.move(tempFile.toPath(), this.path, StandardCopyOption.REPLACE_EXISTING); -+ } -+ -+ -+ public void setStatus(int x, int z, ChunkStatus status) { -+ this.statuses[getChunkIndex(x, z)] = status; -+ } -+ -+ public synchronized void write(ChunkPos pos, ByteBuffer buffer) { -+ try { -+ byte[] b = toByteArray(new ByteArrayInputStream(buffer.array())); -+ int uncompressedSize = b.length; -+ -+ int maxCompressedLength = this.compressor.maxCompressedLength(b.length); -+ byte[] compressed = new byte[maxCompressedLength]; -+ int compressedLength = this.compressor.compress(b, 0, b.length, compressed, 0, maxCompressedLength); -+ b = new byte[compressedLength]; -+ System.arraycopy(compressed, 0, b, 0, compressedLength); -+ -+ int index = getChunkIndex(pos.x, pos.z); -+ this.buffer[index] = b; -+ this.chunkTimestamps[index] = getTimestamp(); -+ this.bufferUncompressedSize[getChunkIndex(pos.x, pos.z)] = uncompressedSize; -+ } catch (IOException e) { -+ LOGGER.error("Chunk write IOException " + e + " " + this.path); -+ } -+ markToSave(); -+ } -+ -+ public DataOutputStream getChunkDataOutputStream(ChunkPos pos) { -+ return new DataOutputStream(new BufferedOutputStream(new LinearRegionFile.ChunkBuffer(pos))); -+ } -+ -+ private class ChunkBuffer extends ByteArrayOutputStream { -+ private final ChunkPos pos; -+ -+ public ChunkBuffer(ChunkPos chunkcoordintpair) { -+ super(); -+ this.pos = chunkcoordintpair; -+ } -+ -+ public void close() throws IOException { -+ ByteBuffer bytebuffer = ByteBuffer.wrap(this.buf, 0, this.count); -+ LinearRegionFile.this.write(this.pos, bytebuffer); -+ } -+ } -+ -+ private byte[] toByteArray(InputStream in) throws IOException { -+ ByteArrayOutputStream out = new ByteArrayOutputStream(); -+ byte[] tempBuffer = new byte[4096]; -+ -+ int length; -+ while ((length = in.read(tempBuffer)) >= 0) { -+ out.write(tempBuffer, 0, length); -+ } -+ -+ return out.toByteArray(); -+ } -+ -+ @Nullable -+ public synchronized DataInputStream getChunkDataInputStream(ChunkPos pos) { -+ if (this.bufferUncompressedSize[getChunkIndex(pos.x, pos.z)] != 0) { -+ byte[] content = new byte[bufferUncompressedSize[getChunkIndex(pos.x, pos.z)]]; -+ this.decompressor.decompress(this.buffer[getChunkIndex(pos.x, pos.z)], 0, content, 0, bufferUncompressedSize[getChunkIndex(pos.x, pos.z)]); -+ return new DataInputStream(new ByteArrayInputStream(content)); -+ } -+ return null; -+ } -+ -+ public ChunkStatus getStatusIfCached(int x, int z) { -+ return this.statuses[getChunkIndex(x, z)]; -+ } -+ -+ public void clear(ChunkPos pos) { -+ int i = getChunkIndex(pos.x, pos.z); -+ this.buffer[i] = null; -+ this.bufferUncompressedSize[i] = 0; -+ this.chunkTimestamps[i] = getTimestamp(); -+ markToSave(); -+ } -+ -+ public boolean hasChunk(ChunkPos pos) { -+ return this.bufferUncompressedSize[getChunkIndex(pos.x, pos.z)] > 0; -+ } -+ -+ public void close() throws IOException { -+ if (closed) return; -+ closed = true; -+ flush(); // sync -+ } -+ -+ private static int getChunkIndex(int x, int z) { -+ return (x & 31) + ((z & 31) << 5); -+ } -+ -+ private static int getTimestamp() { -+ return (int) (System.currentTimeMillis() / 1000L); -+ } -+ -+ public boolean recalculateHeader() { -+ return false; -+ } -+ -+ public void setOversized(int x, int z, boolean something) { -+ } -+ -+ public CompoundTag getOversizedData(int x, int z) throws IOException { -+ throw new IOException("getOversizedData is a stub " + this.path); -+ } -+ -+ public boolean isOversized(int x, int z) { -+ return false; -+ } -+} -diff --git a/src/main/java/net/edenor/foldenor/region/LinearRegionFileFlusher.java b/src/main/java/net/edenor/foldenor/region/LinearRegionFileFlusher.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d8dfcb77be8570045d960e410bb0fe70e8f900e9 ---- /dev/null -+++ b/src/main/java/net/edenor/foldenor/region/LinearRegionFileFlusher.java -@@ -0,0 +1,47 @@ -+package net.edenor.foldenor.region; -+ -+import com.google.common.util.concurrent.ThreadFactoryBuilder; -+ -+import java.util.Queue; -+import java.util.concurrent.*; -+ -+import net.edenor.foldenor.config.FoldenorConfig; -+import org.bukkit.Bukkit; -+ -+public class LinearRegionFileFlusher { -+ private final Queue savingQueue = new LinkedBlockingQueue<>(); -+ private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor( -+ new ThreadFactoryBuilder() -+ .setNameFormat("linear-flush-scheduler") -+ .build() -+ ); -+ private final ExecutorService executor = Executors.newFixedThreadPool( -+ FoldenorConfig.linearFlushThreads, -+ new ThreadFactoryBuilder() -+ .setNameFormat("linear-flusher-%d") -+ .build() -+ ); -+ -+ public LinearRegionFileFlusher() { -+ Bukkit.getLogger().info("Using " + FoldenorConfig.linearFlushThreads + " threads for linear region flushing."); -+ scheduler.scheduleAtFixedRate(this::pollAndFlush, 0L, FoldenorConfig.linearFlushFrequency, TimeUnit.SECONDS); -+ } -+ -+ public void scheduleSave(LinearRegionFile regionFile) { -+ if (savingQueue.contains(regionFile)) return; -+ savingQueue.add(regionFile); -+ } -+ -+ private void pollAndFlush() { -+ while (!savingQueue.isEmpty()) { -+ LinearRegionFile regionFile = savingQueue.poll(); -+ if (!regionFile.closed && regionFile.isMarkedToSave()) -+ executor.execute(regionFile::flushWrapper); -+ } -+ } -+ -+ public void shutdown() { -+ executor.shutdown(); -+ scheduler.shutdown(); -+ } -+} -diff --git a/src/main/java/net/edenor/foldenor/region/RegionFileFormat.java b/src/main/java/net/edenor/foldenor/region/RegionFileFormat.java -new file mode 100644 -index 0000000000000000000000000000000000000000..7492271e263bba9d302ef1d074e983c9cdb20a8a ---- /dev/null -+++ b/src/main/java/net/edenor/foldenor/region/RegionFileFormat.java -@@ -0,0 +1,16 @@ -+package net.edenor.foldenor.region; -+ -+public enum RegionFileFormat { -+ ANVIL, -+ LINEAR, -+ INVALID; -+ -+ public static RegionFileFormat fromString(String format) { -+ for (RegionFileFormat rff : values()) { -+ if (rff.name().equalsIgnoreCase(format)) { -+ return rff; -+ } -+ } -+ return RegionFileFormat.INVALID; -+ } -+} -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 34c145e343faff682c2a2d37c784fa8ba800b198..3bec684803e2d64457891ed4b987f04ad5a6c7cb 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -923,7 +923,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop mainThreadExecutor, LightChunkGetter chunkProvider, ChunkGenerator chunkGenerator, ChunkProgressListener worldGenerationProgressListener, ChunkStatusUpdateListener chunkStatusChangeListener, Supplier persistentStateManagerFactory, int viewDistance, boolean dsync) { -- super(session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); -+ super(FoldenorConfig.regionFormatName, FoldenorConfig.regionFormatLinearCompressionLevel, session.getDimensionPath(world.dimension()).resolve("region"), dataFixer, dsync); - // Paper - rewrite chunk system - this.tickingGenerated = new AtomicInteger(); - //this.playerMap = new PlayerMap(); // Folia - region threading -@@ -256,7 +258,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.lightEngine = new ThreadedLevelLightEngine(chunkProvider, this, this.level.dimensionType().hasSkyLight(), null, null); // Paper - rewrite chunk system - this.distanceManager = new ChunkMap.ChunkDistanceManager(executor, mainThreadExecutor); - this.overworldDataStorage = persistentStateManagerFactory; -- this.poiManager = new PoiManager(path.resolve("poi"), dataFixer, dsync, iregistrycustom, world); -+ this.poiManager = new PoiManager(net.edenor.foldenor.config.FoldenorConfig.regionFormatName, net.edenor.foldenor.config.FoldenorConfig.regionFormatLinearCompressionLevel, path.resolve("poi"), dataFixer, dsync, iregistrycustom, world); - this.setServerViewDistance(viewDistance); - // Paper start - this.dataRegionManager = new io.papermc.paper.chunk.SingleThreadChunkRegionManager(this.level, 2, (1.0 / 3.0), 1, 6, "Data", DataRegionData::new, DataRegionSectionData::new); -@@ -808,13 +810,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - public ChunkStatus getChunkStatusOnDiskIfCached(ChunkPos chunkPos) { -- net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFileIfLoaded(chunkPos); -+ net.edenor.foldenor.region.AbstractRegionFile regionFile = regionFileCache.getRegionFileIfLoaded(chunkPos); - - return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); - } - - public ChunkStatus getChunkStatusOnDisk(ChunkPos chunkPos) throws IOException { -- net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, true); -+ net.edenor.foldenor.region.AbstractRegionFile regionFile = regionFileCache.getRegionFile(chunkPos, true); - - if (regionFile == null || !regionFileCache.chunkExists(chunkPos)) { - return null; -@@ -832,7 +834,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - public void updateChunkStatusOnDisk(ChunkPos chunkPos, @Nullable CompoundTag compound) throws IOException { -- net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, false); -+ net.edenor.foldenor.region.AbstractRegionFile regionFile = regionFileCache.getRegionFile(chunkPos, false); - - regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkSerializer.getStatus(compound)); - } -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 62880be566774cd0da0630662579bc7ee6be4b7e..c0d1e2f0146f75aa264263a445013900564b497f 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -38,6 +38,8 @@ import java.util.stream.Collectors; - import java.util.stream.Stream; - import javax.annotation.Nonnull; - import javax.annotation.Nullable; -+ -+import net.edenor.foldenor.config.FoldenorConfig; - import net.minecraft.CrashReport; - import net.minecraft.CrashReportCategory; - import net.minecraft.Util; -@@ -448,8 +450,8 @@ public class ServerLevel extends Level implements WorldGenLevel { - - private static final class EntityRegionFileStorage extends net.minecraft.world.level.chunk.storage.RegionFileStorage { - -- public EntityRegionFileStorage(Path directory, boolean dsync) { -- super(directory, dsync); -+ public EntityRegionFileStorage(net.edenor.foldenor.region.RegionFileFormat format, int linearCompression, Path directory, boolean dsync) { // Kaiiju -+ super(format, linearCompression, directory, dsync); - } - - protected void write(ChunkPos pos, net.minecraft.nbt.CompoundTag nbt) throws IOException { -@@ -811,7 +813,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - Globals.setupGlobals(this); - boolean flag2 = minecraftserver.forceSynchronousWrites(); - DataFixer datafixer = minecraftserver.getFixerUpper(); -- this.entityStorage = new EntityRegionFileStorage(convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), flag2); // Paper - rewrite chunk system //EntityPersistentStorage entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver); -+ this.entityStorage = new EntityRegionFileStorage(FoldenorConfig.regionFormatName, FoldenorConfig.regionFormatLinearCompressionLevel, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), flag2); // Paper - rewrite chunk system //EntityPersistentStorage entitypersistentstorage = new EntityStorage(this, convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, minecraftserver); - - // this.entityManager = new PersistentEntitySectionManager<>(Entity.class, new ServerLevel.EntityCallbacks(), entitypersistentstorage, this.entitySliceManager); // Paper // Paper - rewrite chunk system - StructureTemplateManager structuretemplatemanager = minecraftserver.getStructureManager(); -diff --git a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java -index 77dd632a266f4abed30b87b7909d77857c01e316..1f43b046c0e9ca0c8698e7a90caae89726cb0ef7 100644 ---- a/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java -+++ b/src/main/java/net/minecraft/util/worldupdate/WorldUpgrader.java -@@ -24,6 +24,8 @@ import java.util.concurrent.ThreadFactory; - import java.util.regex.Matcher; - import java.util.regex.Pattern; - import java.util.stream.Collectors; -+ -+import net.edenor.foldenor.config.FoldenorConfig; - import net.minecraft.ReportedException; - import net.minecraft.SharedConstants; - import net.minecraft.Util; -@@ -61,7 +63,7 @@ public class WorldUpgrader { - private volatile int skipped; - private final Reference2FloatMap> progressMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap()); - private volatile Component status = Component.translatable("optimizeWorld.stage.counting"); -- public static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$"); -+ public static Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.(linear | mca)$"); - private final DimensionDataStorage overworldDataStorage; - - public WorldUpgrader(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, Registry dimensionOptionsRegistry, boolean eraseCache) { -@@ -116,7 +118,10 @@ public class WorldUpgrader { - ResourceKey resourcekey1 = (ResourceKey) iterator1.next(); - Path path = this.levelStorage.getDimensionPath(resourcekey1); - -- builder1.put(resourcekey1, new ChunkStorage(path.resolve("region"), this.dataFixer, true)); -+ String worldName = this.levelStorage.getLevelId(); -+ net.edenor.foldenor.region.RegionFileFormat formatName = FoldenorConfig.regionFormatName; -+ int linearCompression = FoldenorConfig.regionFormatLinearCompressionLevel; -+ builder1.put(resourcekey1, new ChunkStorage(formatName, linearCompression, path.resolve("region"), this.dataFixer, true)); - } - - ImmutableMap, ChunkStorage> immutablemap1 = builder1.build(); -@@ -241,7 +246,7 @@ public class WorldUpgrader { - File file = this.levelStorage.getDimensionPath(world).toFile(); - File file1 = new File(file, "region"); - File[] afile = file1.listFiles((file2, s) -> { -- return s.endsWith(".mca"); -+ return s.endsWith(".mca") || s.endsWith(".linear"); - }); - - if (afile == null) { -@@ -260,7 +265,9 @@ public class WorldUpgrader { - int l = Integer.parseInt(matcher.group(2)) << 5; - - try { -- RegionFile regionfile = new RegionFile(file2.toPath(), file1.toPath(), true); -+ String worldName = this.levelStorage.getLevelId(); -+ int linearCompression = FoldenorConfig.regionFormatLinearCompressionLevel; -+ net.edenor.foldenor.region.AbstractRegionFile regionfile = net.edenor.foldenor.region.AbstractRegionFileFactory.getAbstractRegionFile(linearCompression, file2.toPath(), file1.toPath(), true); - - try { - for (int i1 = 0; i1 < 32; ++i1) { -diff --git a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java -index 5150d447c9dc2f539446749c8bee102050bab4ed..8156bb7d69b06b6c08af02a0aab595fb6f0964cd 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java -+++ b/src/main/java/net/minecraft/world/entity/ai/village/poi/PoiManager.java -@@ -59,8 +59,8 @@ public class PoiManager extends SectionStorage { - // Paper end - rewrite chunk system - - -- public PoiManager(Path path, DataFixer dataFixer, boolean dsync, RegistryAccess registryManager, LevelHeightAccessor world) { -- super(path, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, registryManager, world); -+ public PoiManager(net.edenor.foldenor.region.RegionFileFormat formatName, int linearCompression, Path path, DataFixer dataFixer, boolean dsync, RegistryAccess registryManager, LevelHeightAccessor world) { // Kaiiju -+ super(formatName, linearCompression, path, PoiSection::codec, PoiSection::new, dataFixer, DataFixTypes.POI_CHUNK, dsync, registryManager, world); - this.world = (net.minecraft.server.level.ServerLevel)world; // Paper - rewrite chunk system - } - -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java -index d16d7c2fed89fb1347df7ddd95856e7f08c22e8a..04a38a9ae7b6b87b38e29c9abbce96704615a4d1 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java -@@ -36,9 +36,9 @@ public class ChunkStorage implements AutoCloseable { - @Nullable - private volatile LegacyStructureDataHandler legacyStructureHandler; - -- public ChunkStorage(Path directory, DataFixer dataFixer, boolean dsync) { -+ public ChunkStorage(net.edenor.foldenor.region.RegionFileFormat format, int linearCompression, Path directory, DataFixer dataFixer, boolean dsync) { - this.fixerUpper = dataFixer; -- this.regionFileCache = new RegionFileStorage(directory, dsync, true); // Paper - rewrite chunk system; async chunk IO & Attempt to recalculate regionfile header if it is corrupt -+ this.regionFileCache = new RegionFileStorage(format, linearCompression, directory, dsync, true); // Paper - rewrite chunk system; async chunk IO & Attempt to recalculate regionfile header if it is corrupt - } - - public boolean isOldChunkAround(ChunkPos chunkPos, int checkRadius) { -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java -index 6cf83502a954cce9c562ec036bfeddb477d38b73..3f2233881e0edac990023841df0440d61488b407 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java -@@ -26,7 +26,7 @@ import net.minecraft.nbt.NbtIo; // Paper - import net.minecraft.world.level.ChunkPos; - import org.slf4j.Logger; - --public class RegionFile implements AutoCloseable { -+public class RegionFile implements AutoCloseable, net.edenor.foldenor.region.AbstractRegionFile { - - private static final Logger LOGGER = LogUtils.getLogger(); - private static final int SECTOR_BYTES = 4096; -@@ -50,6 +50,14 @@ public class RegionFile implements AutoCloseable { - public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(); // Paper - public final Path regionFile; // Paper - -+ public Path getRegionFile() { -+ return this.regionFile; -+ } -+ -+ public java.util.concurrent.locks.ReentrantLock getFileLock() { -+ return this.fileLock; -+ } -+ - // Paper start - Attempt to recalculate regionfile header if it is corrupt - private static long roundToSectors(long bytes) { - long sectors = bytes >>> 12; // 4096 = 2^12 -@@ -128,7 +136,7 @@ public class RegionFile implements AutoCloseable { - } - - // note: only call for CHUNK regionfiles -- boolean recalculateHeader() throws IOException { -+ public boolean recalculateHeader() throws IOException { - if (!this.canRecalcHeader) { - return false; - } -@@ -955,10 +963,10 @@ public class RegionFile implements AutoCloseable { - private static int getChunkIndex(int x, int z) { - return (x & 31) + (z & 31) * 32; - } -- synchronized boolean isOversized(int x, int z) { -+ public synchronized boolean isOversized(int x, int z) { - return this.oversized[getChunkIndex(x, z)] == 1; - } -- synchronized void setOversized(int x, int z, boolean oversized) throws IOException { -+ public synchronized void setOversized(int x, int z, boolean oversized) throws IOException { - final int offset = getChunkIndex(x, z); - boolean previous = this.oversized[offset] == 1; - this.oversized[offset] = (byte) (oversized ? 1 : 0); -@@ -997,7 +1005,7 @@ public class RegionFile implements AutoCloseable { - return this.regionFile.getParent().resolve(this.regionFile.getFileName().toString().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt"); - } - -- synchronized CompoundTag getOversizedData(int x, int z) throws IOException { -+ public synchronized CompoundTag getOversizedData(int x, int z) throws IOException { - Path file = getOversizedFile(x, z); - try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new InflaterInputStream(Files.newInputStream(file))))) { - return NbtIo.read((java.io.DataInput) out); -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -index fe312b1aef579cb4bf81bdd967cf72ff880d7505..c0250b4a38beba1b0ab4baae676be007dcd4861c 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java -@@ -21,9 +21,12 @@ public class RegionFileStorage implements AutoCloseable { - - public static final String ANVIL_EXTENSION = ".mca"; - private static final int MAX_CACHE_SIZE = 256; -- public final Long2ObjectLinkedOpenHashMap regionCache = new Long2ObjectLinkedOpenHashMap(); -+ public final Long2ObjectLinkedOpenHashMap regionCache = new Long2ObjectLinkedOpenHashMap(); - private final Path folder; - private final boolean sync; -+ -+ public final net.edenor.foldenor.region.RegionFileFormat format; -+ public final int linearCompression; - private final boolean isChunkData; // Paper - - // Paper start - cache regionfile does not exist state -@@ -55,11 +58,14 @@ public class RegionFileStorage implements AutoCloseable { - } - // Paper end - cache regionfile does not exist state - -- protected RegionFileStorage(Path directory, boolean dsync) { // Paper - protected constructor -+ protected RegionFileStorage(net.edenor.foldenor.region.RegionFileFormat format, int linearCompression, Path directory, boolean dsync) { // Paper - protected constructor - // Paper start - add isChunkData param -- this(directory, dsync, false); -+ this(format, linearCompression, directory, dsync, false); - } -- RegionFileStorage(Path directory, boolean dsync, boolean isChunkData) { -+ RegionFileStorage(net.edenor.foldenor.region.RegionFileFormat format, int linearCompression, Path directory, boolean dsync, boolean isChunkData) { -+ this.format = format; -+ this.linearCompression = linearCompression; -+ - this.isChunkData = isChunkData; - // Paper end - add isChunkData param - this.folder = directory; -@@ -70,7 +76,7 @@ public class RegionFileStorage implements AutoCloseable { - @Nullable - public static ChunkPos getRegionFileCoordinates(Path file) { - String fileName = file.getFileName().toString(); -- if (!fileName.startsWith("r.") || !fileName.endsWith(".mca")) { -+ if (!fileName.startsWith("r.") || !fileName.endsWith(".mca") || !fileName.endsWith(".linear")) { - return null; - } - -@@ -90,29 +96,29 @@ public class RegionFileStorage implements AutoCloseable { - } - } - -- public synchronized RegionFile getRegionFileIfLoaded(ChunkPos chunkcoordintpair) { -+ public synchronized net.edenor.foldenor.region.AbstractRegionFile getRegionFileIfLoaded(ChunkPos chunkcoordintpair) { - return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ())); - } - - public synchronized boolean chunkExists(ChunkPos pos) throws IOException { -- RegionFile regionfile = getRegionFile(pos, true); -+ net.edenor.foldenor.region.AbstractRegionFile regionfile = getRegionFile(pos, true); - - return regionfile != null ? regionfile.hasChunk(pos) : false; - } - -- public synchronized RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit -+ public synchronized net.edenor.foldenor.region.AbstractRegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit - return this.getRegionFile(chunkcoordintpair, existingOnly, false); - } -- public synchronized RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly, boolean lock) throws IOException { -+ public synchronized net.edenor.foldenor.region.AbstractRegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly, boolean lock) throws IOException { - // Paper end - long i = ChunkPos.asLong(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()); final long regionPos = i; // Paper - OBFHELPER -- RegionFile regionfile = (RegionFile) this.regionCache.getAndMoveToFirst(i); -+ net.edenor.foldenor.region.AbstractRegionFile regionfile = (net.edenor.foldenor.region.AbstractRegionFile) this.regionCache.getAndMoveToFirst(i); - - if (regionfile != null) { - // Paper start - if (lock) { - // must be in this synchronized block -- regionfile.fileLock.lock(); -+ regionfile.getFileLock().lock(); - } - // Paper end - return regionfile; -@@ -123,28 +129,39 @@ public class RegionFileStorage implements AutoCloseable { - } - // Paper end - cache regionfile does not exist state - if (this.regionCache.size() >= io.papermc.paper.configuration.GlobalConfiguration.get().misc.regionFileCacheSize) { // Paper - Sanitise RegionFileCache and make configurable -- ((RegionFile) this.regionCache.removeLast()).close(); -+ this.regionCache.removeLast().close(); - } - - // Paper - only create directory if not existing only - moved down - Path path = this.folder; - int j = chunkcoordintpair.getRegionX(); -- Path path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca"); // Paper - diff on change -- if (existingOnly && !java.nio.file.Files.exists(path1)) { // Paper start - cache regionfile does not exist state -- this.markNonExisting(regionPos); -- return null; // CraftBukkit -+ Path path1; -+ if (existingOnly) { -+ Path anvil = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".mca"); -+ Path linear = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + ".linear"); -+ if (java.nio.file.Files.exists(anvil)) path1 = anvil; -+ else if (java.nio.file.Files.exists(linear)) path1 = linear; -+ else { -+ this.markNonExisting(regionPos); -+ return null; -+ } - } else { -+ String extension = switch (this.format) { -+ case LINEAR -> "linear"; -+ default -> "mca"; -+ }; -+ path1 = path.resolve("r." + j + "." + chunkcoordintpair.getRegionZ() + "." + extension); - this.createRegionFile(regionPos); - } - // Paper end - cache regionfile does not exist state - FileUtil.createDirectoriesSafe(this.folder); // Paper - only create directory if not existing only - moved from above -- RegionFile regionfile1 = new RegionFile(path1, this.folder, this.sync, this.isChunkData); // Paper - allow for chunk regionfiles to regen header -+ net.edenor.foldenor.region.AbstractRegionFile regionfile1 = net.edenor.foldenor.region.AbstractRegionFileFactory.getAbstractRegionFile(this.linearCompression, path1, this.folder, this.sync, this.isChunkData); // Paper - allow for chunk regionfiles to regen header - - this.regionCache.putAndMoveToFirst(i, regionfile1); - // Paper start - if (lock) { - // must be in this synchronized block -- regionfile1.fileLock.lock(); -+ regionfile1.getFileLock().lock(); - } - // Paper end - return regionfile1; -@@ -156,7 +173,23 @@ public class RegionFileStorage implements AutoCloseable { - org.apache.logging.log4j.LogManager.getLogger().fatal(msg + " (" + file.toString().replaceAll(".+[\\\\/]", "") + " - " + x + "," + z + ") Go clean it up to remove this message. /minecraft:tp " + (x<<4)+" 128 "+(z<<4) + " - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); - } - -- private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { -+ private static final int DEFAULT_SIZE_THRESHOLD = 1024 * 8; -+ private static final int OVERZEALOUS_TOTAL_THRESHOLD = 1024 * 64; -+ private static final int OVERZEALOUS_THRESHOLD = 1024; -+ private static int SIZE_THRESHOLD = DEFAULT_SIZE_THRESHOLD; -+ private static void resetFilterThresholds() { -+ SIZE_THRESHOLD = Math.max(1024 * 4, Integer.getInteger("Paper.FilterThreshhold", DEFAULT_SIZE_THRESHOLD)); -+ } -+ static { -+ resetFilterThresholds(); -+ } -+ -+ static boolean isOverzealous() { -+ return SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD; -+ } -+ -+ -+ private static CompoundTag readOversizedChunk(net.edenor.foldenor.region.AbstractRegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { - synchronized (regionfile) { - try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) { - CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z); -@@ -191,14 +224,14 @@ public class RegionFileStorage implements AutoCloseable { - @Nullable - public CompoundTag read(ChunkPos pos) throws IOException { - // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing -- RegionFile regionfile = this.getRegionFile(pos, true, true); // Paper -+ net.edenor.foldenor.region.AbstractRegionFile regionfile = this.getRegionFile(pos, true, true); // Paper - if (regionfile == null) { - return null; - } - // Paper start - Add regionfile parameter - return this.read(pos, regionfile); - } -- public CompoundTag read(ChunkPos pos, RegionFile regionfile) throws IOException { -+ public CompoundTag read(ChunkPos pos, net.edenor.foldenor.region.AbstractRegionFile regionfile) throws IOException { - // We add the regionfile parameter to avoid the potential deadlock (on fileLock) if we went back to obtain a regionfile - // if we decide to re-read - // Paper end -@@ -208,7 +241,7 @@ public class RegionFileStorage implements AutoCloseable { - - // Paper start - if (regionfile.isOversized(pos.x, pos.z)) { -- printOversizedLog("Loading Oversized Chunk!", regionfile.regionFile, pos.x, pos.z); -+ printOversizedLog("Loading Oversized Chunk!", regionfile.getRegionFile(), pos.x, pos.z); - return readOversizedChunk(regionfile, pos); - } - // Paper end -@@ -222,12 +255,12 @@ public class RegionFileStorage implements AutoCloseable { - if (this.isChunkData) { - ChunkPos chunkPos = ChunkSerializer.getChunkCoordinate(nbttagcompound); - if (!chunkPos.equals(pos)) { -- net.minecraft.server.MinecraftServer.LOGGER.error("Attempting to read chunk data at " + pos + " but got chunk data for " + chunkPos + " instead! Attempting regionfile recalculation for regionfile " + regionfile.regionFile.toAbsolutePath()); -+ net.minecraft.server.MinecraftServer.LOGGER.error("Attempting to read chunk data at " + pos + " but got chunk data for " + chunkPos + " instead! Attempting regionfile recalculation for regionfile " + regionfile.getRegionFile().toAbsolutePath()); - if (regionfile.recalculateHeader()) { -- regionfile.fileLock.lock(); // otherwise we will unlock twice and only lock once. -+ regionfile.getFileLock().lock(); // otherwise we will unlock twice and only lock once. - return this.read(pos, regionfile); - } -- net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + pos + " for " + regionfile.regionFile.toAbsolutePath()); -+ net.minecraft.server.MinecraftServer.LOGGER.error("Can't recalculate regionfile header, regenerating chunk " + pos + " for " + regionfile.getRegionFile().toAbsolutePath()); - return null; - } - } -@@ -261,13 +294,13 @@ public class RegionFileStorage implements AutoCloseable { - - return nbttagcompound; - } finally { // Paper start -- regionfile.fileLock.unlock(); -+ regionfile.getFileLock().unlock(); - } // Paper end - } - - public void scanChunk(ChunkPos chunkPos, StreamTagVisitor scanner) throws IOException { - // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing -- RegionFile regionfile = this.getRegionFile(chunkPos, true); -+ net.edenor.foldenor.region.AbstractRegionFile regionfile = this.getRegionFile(chunkPos, true); - if (regionfile == null) { - return; - } -@@ -298,7 +331,7 @@ public class RegionFileStorage implements AutoCloseable { - - protected void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException { - // Paper start - rewrite chunk system -- RegionFile regionfile = this.getRegionFile(pos, nbt == null, true); // CraftBukkit -+ net.edenor.foldenor.region.AbstractRegionFile regionfile = this.getRegionFile(pos, nbt == null, true); // CraftBukkit - if (nbt == null && regionfile == null) { - return; - } -@@ -353,7 +386,7 @@ public class RegionFileStorage implements AutoCloseable { - // Paper end - Chunk save reattempt - // Paper start - rewrite chunk system - } finally { -- regionfile.fileLock.unlock(); -+ regionfile.getFileLock().unlock(); - } - // Paper end - rewrite chunk system - } -@@ -363,7 +396,7 @@ public class RegionFileStorage implements AutoCloseable { - ObjectIterator objectiterator = this.regionCache.values().iterator(); - - while (objectiterator.hasNext()) { -- RegionFile regionfile = (RegionFile) objectiterator.next(); -+ net.edenor.foldenor.region.AbstractRegionFile regionfile = (net.edenor.foldenor.region.AbstractRegionFile) objectiterator.next(); - - try { - regionfile.close(); -@@ -379,7 +412,7 @@ public class RegionFileStorage implements AutoCloseable { - ObjectIterator objectiterator = this.regionCache.values().iterator(); - - while (objectiterator.hasNext()) { -- RegionFile regionfile = (RegionFile) objectiterator.next(); -+ net.edenor.foldenor.region.AbstractRegionFile regionfile = (net.edenor.foldenor.region.AbstractRegionFile) objectiterator.next(); - - regionfile.flush(); - } -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java -index 4aac1979cf57300825a999c876fcf24d3170e68e..e1436f956f74b93d0a7bf96388f8af5b3f36321a 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/SectionStorage.java -@@ -47,8 +47,8 @@ public class SectionStorage extends RegionFileStorage implements AutoCloseabl - public final RegistryAccess registryAccess; // Paper - rewrite chunk system - protected final LevelHeightAccessor levelHeightAccessor; - -- public SectionStorage(Path path, Function> codecFactory, Function factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, RegistryAccess dynamicRegistryManager, LevelHeightAccessor world) { -- super(path, dsync); // Paper - remove mojang I/O thread -+ public SectionStorage(net.edenor.foldenor.region.RegionFileFormat format, int linearCompression, Path path, Function> codecFactory, Function factory, DataFixer dataFixer, DataFixTypes dataFixTypes, boolean dsync, RegistryAccess dynamicRegistryManager, LevelHeightAccessor world) { -+ super(format, linearCompression, path, dsync); // Paper - remove mojang I/O thread - this.codec = codecFactory; - this.factory = factory; - this.fixerUpper = dataFixer; -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index becb3b07876715d7c39ba4e7289cc5ac85f84412..0715f4502ed7e0c1bfe8515f710d9c4bc6b037f3 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -576,7 +576,7 @@ public class CraftWorld extends CraftRegionAccessor implements World { - world.getChunk(x, z); // make sure we're at ticket level 32 or lower - return true; - } -- net.minecraft.world.level.chunk.storage.RegionFile file; -+ net.edenor.foldenor.region.AbstractRegionFile file; - try { - file = world.getChunkSource().chunkMap.regionFileCache.getRegionFile(chunkPos, false); - } catch (java.io.IOException ex) { diff --git a/patches/server/0015-Redirect-CraftScheduler-to-GlobalRegionScheduler.patch b/patches/server/0014-Redirect-CraftScheduler-to-GlobalRegionScheduler.patch similarity index 100% rename from patches/server/0015-Redirect-CraftScheduler-to-GlobalRegionScheduler.patch rename to patches/server/0014-Redirect-CraftScheduler-to-GlobalRegionScheduler.patch diff --git a/patches/server/0021-Kaiiju-Don-t-pathfind-outside-region.patch b/patches/server/0015-Kaiiju-Don-t-pathfind-outside-region.patch similarity index 86% rename from patches/server/0021-Kaiiju-Don-t-pathfind-outside-region.patch rename to patches/server/0015-Kaiiju-Don-t-pathfind-outside-region.patch index 0d988eb..7b6f2e4 100644 --- a/patches/server/0021-Kaiiju-Don-t-pathfind-outside-region.patch +++ b/patches/server/0015-Kaiiju-Don-t-pathfind-outside-region.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Kaiiju-Don-t-pathfind-outside-region diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java -index 1ab77f3518d1df30f66ae44d7d4fa69e5b32d93a..c09bb4018cbae6406408b3e763f756aac006fcaa 100644 +index 2a7a26ca447cc78f24e61a2bf557411c31eb16b2..ea1f4391fd2d4dc6e7c17adc6dd27e31b2fe2203 100644 --- a/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java +++ b/src/main/java/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java -@@ -107,7 +107,9 @@ public class MoveToTargetSink extends Behavior { +@@ -120,7 +120,9 @@ public class MoveToTargetSink extends Behavior { private boolean tryComputePath(Mob entity, WalkTarget walkTarget, long time) { BlockPos blockPos = walkTarget.getTarget().currentBlockPosition(); diff --git a/patches/server/0022-Pufferfish-Optimize-entity-coordinate-key.patch b/patches/server/0016-Pufferfish-Optimize-entity-coordinate-key.patch similarity index 90% rename from patches/server/0022-Pufferfish-Optimize-entity-coordinate-key.patch rename to patches/server/0016-Pufferfish-Optimize-entity-coordinate-key.patch index 253b37a..d4cf6f0 100644 --- a/patches/server/0022-Pufferfish-Optimize-entity-coordinate-key.patch +++ b/patches/server/0016-Pufferfish-Optimize-entity-coordinate-key.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Pufferfish-Optimize-entity-coordinate-key diff --git a/src/main/java/io/papermc/paper/util/MCUtil.java b/src/main/java/io/papermc/paper/util/MCUtil.java -index 08d282400644be5949f28eb879b333cc221da2cc..bada1ff427be6d5892a3eb9b3af0a3d9e79cd2ee 100644 +index bb915f30997059f22c9002b1ddc1c3aecbf4e979..4ea23172dbb9ec4a524df166814c261d9fe464fb 100644 --- a/src/main/java/io/papermc/paper/util/MCUtil.java +++ b/src/main/java/io/papermc/paper/util/MCUtil.java @@ -215,7 +215,7 @@ public final class MCUtil { diff --git a/patches/server/0023-Pufferfish-Cache-climbing-check-for-activation.patch b/patches/server/0017-Pufferfish-Cache-climbing-check-for-activation.patch similarity index 92% rename from patches/server/0023-Pufferfish-Cache-climbing-check-for-activation.patch rename to patches/server/0017-Pufferfish-Cache-climbing-check-for-activation.patch index 9836d6e..19a3b1c 100644 --- a/patches/server/0023-Pufferfish-Cache-climbing-check-for-activation.patch +++ b/patches/server/0017-Pufferfish-Cache-climbing-check-for-activation.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Pufferfish-Cache-climbing-check-for-activation diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index b9f6e599afd4bdfb97e7f1b1881730cbed781bba..9ae7519f2a0e97ed83776a788ae26a18509ce2da 100644 +index a1e90547816ebf0811e7b22af793f40015d6c4f3..2f67e332ea0feec67c33f55b98a5187675b4dbf3 100644 --- a/src/main/java/net/minecraft/world/entity/LivingEntity.java +++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2043,6 +2043,17 @@ public abstract class LivingEntity extends Entity implements Attackable { +@@ -2069,6 +2069,17 @@ public abstract class LivingEntity extends Entity implements Attackable { return this.lastClimbablePos; } diff --git a/patches/server/0018-Alternative-Keepalive-Handling.patch b/patches/server/0018-Alternative-Keepalive-Handling.patch deleted file mode 100644 index 5332a39..0000000 --- a/patches/server/0018-Alternative-Keepalive-Handling.patch +++ /dev/null @@ -1,133 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Fri, 22 Dec 2023 20:54:37 +0400 -Subject: [PATCH] Alternative-Keepalive-Handling - - -diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -index 3e4af65b58540520a7cde6da06aedadff6e2369c..645345766a0a3f9e92d079c7e2eb8394a216e9f1 100644 ---- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -+++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -26,6 +26,7 @@ public class FoldenorConfig { - - public static boolean sendNullEntityPackets = true; - public static boolean appleskinProtocol = false; -+ public static boolean alternateKeepAlive = false; - - public static boolean vanilaEndPortalTeleportation = false; - -@@ -127,6 +128,7 @@ public class FoldenorConfig { - private static void networkSettings() { - sendNullEntityPackets = getBoolean("network.send-null-entity-packets", sendNullEntityPackets); - appleskinProtocol = getBoolean("network.appleskin-protocol", appleskinProtocol); -+ alternateKeepAlive = getBoolean("network.alternate-keepalive", alternateKeepAlive); - } - - protected static void set(String path, Object val) { -diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -index c5742b642e543587390ba6f521161a4c7a0725f3..a9a54fada91c7ba3afb331344139328f2ba2465f 100644 ---- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -@@ -4,6 +4,8 @@ import com.mojang.authlib.GameProfile; - import com.mojang.logging.LogUtils; - import java.util.Objects; - import javax.annotation.Nullable; -+ -+import net.edenor.foldenor.config.FoldenorConfig; - import net.minecraft.ChatFormatting; - import net.minecraft.CrashReport; - import net.minecraft.CrashReportCategory; -@@ -51,6 +53,8 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - private long keepAliveTime = Util.getMillis(); // Paper - private boolean keepAlivePending; - private long keepAliveChallenge; -+ -+ private it.unimi.dsi.fastutil.longs.LongList keepAlives = new it.unimi.dsi.fastutil.longs.LongArrayList(); - private int latency; - private volatile boolean suspendFlushingOnServerThread = false; - public final java.util.Map packCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - adventure resource pack callbacks -@@ -103,20 +107,27 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - - @Override - public void handleKeepAlive(ServerboundKeepAlivePacket packet) { -- //PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // CraftBukkit // Paper - handle ServerboundKeepAlivePacket async -- if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) { -- int i = (int) (Util.getMillis() - this.keepAliveTime); -- -- this.latency = (this.latency * 3 + i) / 4; -- this.keepAlivePending = false; -- } else if (!this.isSingleplayerOwner()) { -- // Paper start - This needs to be handled on the main thread for plugins -- // Folia - region threading - do not schedule to main anymore, there is no main -- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause -- // Folia - region threading - do not schedule to main anymore, there is no main -- // Paper end - This needs to be handled on the main thread for plugins -- } -- -+ if (FoldenorConfig.alternateKeepAlive) { -+ long id = packet.getId(); -+ if (keepAlives.size() > 0 && keepAlives.contains(id)) { -+ int ping = (int) (Util.getMillis() - id); -+ this.latency = (this.latency * 3 + ping) / 4; -+ keepAlives.clear(); // we got a valid response, lets roll with it and forget the rest -+ } -+ } else -+ //PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // CraftBukkit // Paper - This shouldn't be on the main thread -+ if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) { -+ int i = (int) (Util.getMillis() - this.keepAliveTime); -+ -+ this.latency = (this.latency * 3 + i) / 4; -+ this.keepAlivePending = false; -+ } else if (!this.isSingleplayerOwner()) { -+ // Paper start - This needs to be handled on the main thread for plugins -+ // Folia - region threading - do not schedule to main anymore, there is no main -+ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause -+ // Folia - region threading - do not schedule to main anymore, there is no main -+ // Paper endg -+ } - } - - @Override -@@ -223,19 +234,31 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - long currentTime = Util.getMillis(); - long elapsedTime = currentTime - this.keepAliveTime; - -- if (this.keepAlivePending) { -- if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected -- ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName()); // more info -- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause -+ if (FoldenorConfig.alternateKeepAlive) { -+ if (elapsedTime >= 1000L) { // 1 second -+ if (!processedDisconnect && keepAlives.size() * 1000L >= KEEPALIVE_LIMIT) { -+ LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName()); -+ disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); -+ } else { -+ keepAliveTime = currentTime; // hijack this field for 1 second intervals -+ keepAlives.add(currentTime); // currentTime is ID -+ send(new ClientboundKeepAlivePacket(currentTime)); -+ } - } -- } else { -- if (elapsedTime >= 15000L) { // 15 seconds -- this.keepAlivePending = true; -- this.keepAliveTime = currentTime; -- this.keepAliveChallenge = currentTime; -- this.send(new ClientboundKeepAlivePacket(this.keepAliveChallenge)); -+ } else -+ if (this.keepAlivePending) { -+ if (!this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // check keepalive limit, don't fire if already disconnected -+ ServerGamePacketListenerImpl.LOGGER.warn("{} was kicked due to keepalive timeout!", this.player.getScoreboardName()); // more info -+ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause -+ } -+ } else { -+ if (elapsedTime >= 15000L) { // 15 seconds -+ this.keepAlivePending = true; -+ this.keepAliveTime = currentTime; -+ this.keepAliveChallenge = currentTime; -+ this.send(new ClientboundKeepAlivePacket(this.keepAliveChallenge)); -+ } - } -- } - // Paper end - give clients a longer time to respond to pings as per pre 1.12.2 timings - - this.server.getProfiler().pop(); diff --git a/patches/server/0024-Pufferfish-Improve-fluid-direction-caching.patch b/patches/server/0018-Pufferfish-Improve-fluid-direction-caching.patch similarity index 98% rename from patches/server/0024-Pufferfish-Improve-fluid-direction-caching.patch rename to patches/server/0018-Pufferfish-Improve-fluid-direction-caching.patch index 12c6e5b..afcb89a 100644 --- a/patches/server/0024-Pufferfish-Improve-fluid-direction-caching.patch +++ b/patches/server/0018-Pufferfish-Improve-fluid-direction-caching.patch @@ -147,7 +147,7 @@ index 0000000000000000000000000000000000000000..805d5e7e27f99eafc16c1cbd61049b23 + } +} diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -index 6d8ff6c06af5545634f255ed17dc1e489ece2548..172e385fe6d1ba5f58481a58f8a3847b97a28488 100644 +index c2943d892b067b3f1fb3b93301a092e912d71f08..17d18dca43d806b69327685c21c497ea103854c2 100644 --- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java +++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java @@ -45,13 +45,17 @@ public abstract class FlowingFluid extends Fluid { diff --git a/patches/server/0026-Pufferfish-Early-return-optimization-for-target-find.patch b/patches/server/0019-Pufferfish-Early-return-optimization-for-target-find.patch similarity index 78% rename from patches/server/0026-Pufferfish-Early-return-optimization-for-target-find.patch rename to patches/server/0019-Pufferfish-Early-return-optimization-for-target-find.patch index 363996a..aa54a96 100644 --- a/patches/server/0026-Pufferfish-Early-return-optimization-for-target-find.patch +++ b/patches/server/0019-Pufferfish-Early-return-optimization-for-target-find.patch @@ -5,26 +5,26 @@ Subject: [PATCH] Pufferfish-Early-return-optimization-for-target-find diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -index c8a80c1b2fedff22e8a877d466062375ffb2f0d7..de735aab1f1fece21b429b94e6f05115d8dfa9c0 100644 +index d2f0c3b26d4beedb49d86e0242d843590d469d02..82fff2cdc61248cab611f96790baa0eeb8517f06 100644 --- a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java +++ b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java -@@ -75,9 +75,17 @@ public class TargetingConditions { +@@ -76,9 +76,17 @@ public class TargetingConditions { } - if (this.range > 0.0D) { -- double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D; -- double e = Math.max((this.useFollowRange ? this.getFollowRange(baseEntity) : this.range) * d, 2.0D); // Paper - Fix MC-145656 -+ //double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D; -+ //double e = Math.max((this.useFollowRange ? this.getFollowRange(baseEntity) : this.range) * d, 2.0D); // Paper - Fix MC-145656 + if (this.range > 0.0) { +- double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0; +- double e = Math.max((this.useFollowRange ? this.getFollowRange(baseEntity) : this.range) * d, 2.0); // Paper - Fix MC-145656 ++ //double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0; ++ //double e = Math.max((this.useFollowRange ? this.getFollowRange(baseEntity) : this.range) * d, 2.0); // Paper - Fix MC-145656 double f = baseEntity.distanceToSqr(targetEntity.getX(), targetEntity.getY(), targetEntity.getZ()); + double followRangeRaw = this.useFollowRange ? this.getFollowRange(baseEntity) : this.range; -+ + if (f > followRangeRaw * followRangeRaw) { // the actual follow range will always be this value or smaller, so if the distance is larger then it never will return true after getting invis + return false; + } + + double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D; + double e = Math.max((followRangeRaw) * d, 2.0D); // Paper ++ if (f > e * e) { return false; } diff --git a/patches/server/0020-POI-changes.patch b/patches/server/0020-POI-changes.patch deleted file mode 100644 index da48926..0000000 --- a/patches/server/0020-POI-changes.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Thu, 4 Jan 2024 02:51:04 +0400 -Subject: [PATCH] POI-changes - - -diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -index 645345766a0a3f9e92d079c7e2eb8394a216e9f1..b873a692a13549ec39d0350703b2fafb6964e334 100644 ---- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -+++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -34,6 +34,8 @@ public class FoldenorConfig { - - public static int maxProjectileLoadsPerTick = 10; - -+ public static int acquirePoiForStuckEntityInterval = 60; -+ - public static RegionFileFormat regionFormatName = RegionFileFormat.ANVIL; - public static int regionFormatLinearCompressionLevel = 1; - public static int linearFlushFrequency = 10; -@@ -123,6 +125,7 @@ public class FoldenorConfig { - private static void optimizationSettings() { - maxProjectileLoadsPerProjectile = getInt("optimizations.max_projectile_loads_per_projectile",maxProjectileLoadsPerProjectile); - maxProjectileLoadsPerTick = getInt("optimizations.max_projectile_loads_per_tick",maxProjectileLoadsPerTick); -+ acquirePoiForStuckEntityInterval = getInt("optimizations.acquire_poi_for_stuck_entity_interval", acquirePoiForStuckEntityInterval); - } - - private static void networkSettings() { -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -index abcc3ef59475ac170fd10b4dd4a4f3371faf17e0..274d3d20afee40ea5108adff0bb3932e34580325 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java -@@ -9,10 +9,13 @@ import java.util.Set; - import java.util.function.Predicate; - import java.util.stream.Collectors; - import javax.annotation.Nullable; -+ -+import net.edenor.foldenor.config.FoldenorConfig; - import net.minecraft.core.BlockPos; - import net.minecraft.core.GlobalPos; - import net.minecraft.core.Holder; - import net.minecraft.network.protocol.game.DebugPackets; -+import net.minecraft.server.level.ServerLevel; - import net.minecraft.util.RandomSource; - import net.minecraft.world.entity.Mob; - import net.minecraft.world.entity.PathfinderMob; -@@ -26,6 +29,11 @@ import org.apache.commons.lang3.mutable.MutableLong; - public class AcquirePoi { - public static final int SCAN_RANGE = 48; - -+ public static void addAdditionalTimeToMutableLongIfEntityIsStuck(MutableLong mutableLong, ServerLevel world, PathfinderMob entity) { -+ long stuckEntityAdditionalWaitTime = FoldenorConfig.acquirePoiForStuckEntityInterval; -+ mutableLong.add(stuckEntityAdditionalWaitTime <= 0L ? 0L : entity.getNavigation().isStuck() ? stuckEntityAdditionalWaitTime : 0L); -+ } -+ - public static BehaviorControl create(Predicate> poiPredicate, MemoryModuleType poiPosModule, boolean onlyRunIfChild, Optional entityStatus) { - return create(poiPredicate, poiPosModule, poiPosModule, onlyRunIfChild, entityStatus); - } -@@ -42,12 +50,14 @@ public class AcquirePoi { - return false; - } else if (mutableLong.getValue() == 0L) { - mutableLong.setValue(world.getGameTime() + (long)world.random.nextInt(20)); -+ addAdditionalTimeToMutableLongIfEntityIsStuck(mutableLong, world, entity); - return false; - } else if (world.getGameTime() < mutableLong.getValue()) { - return false; - } else { - mutableLong.setValue(time + 20L + (long)world.getRandom().nextInt(20)); -- if (entity.getNavigation().isStuck()) mutableLong.add(200); // Paper - Perf: Wait an additional 10s to check again if they're stuck -+ addAdditionalTimeToMutableLongIfEntityIsStuck(mutableLong, world, entity); -+ //if (entity.getNavigation().isStuck()) mutableLong.add(200); // Paper - Wait an additional 10s to check again if they're stuck - PoiManager poiManager = world.getPoiManager(); - long2ObjectMap.long2ObjectEntrySet().removeIf((entry) -> { - return !entry.getValue().isStillValid(time); -diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java -index cb1d91f9fe98f21c2afbe3894dfd9bca3bdd3ba6..6a371bc357ca58979ef79e903762e0d26ab8da69 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java -+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/SecondaryPoiSensor.java -@@ -22,6 +22,11 @@ public class SecondaryPoiSensor extends Sensor { - - @Override - protected void doTick(ServerLevel world, Villager entity) { -+ var secondaryPoi = entity.getVillagerData().getProfession().secondaryPoi(); -+ if (secondaryPoi.isEmpty()) { -+ entity.getBrain().eraseMemory(MemoryModuleType.SECONDARY_JOB_SITE); -+ return; -+ } - ResourceKey resourceKey = world.dimension(); - BlockPos blockPos = entity.blockPosition(); - List list = Lists.newArrayList(); diff --git a/patches/server/0027-Pufferfish-Reduce-chunk-loading-lookups.patch b/patches/server/0020-Pufferfish-Reduce-chunk-loading-lookups.patch similarity index 92% rename from patches/server/0027-Pufferfish-Reduce-chunk-loading-lookups.patch rename to patches/server/0020-Pufferfish-Reduce-chunk-loading-lookups.patch index 345002f..eb26f7f 100644 --- a/patches/server/0027-Pufferfish-Reduce-chunk-loading-lookups.patch +++ b/patches/server/0020-Pufferfish-Reduce-chunk-loading-lookups.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Pufferfish-Reduce-chunk-loading-lookups diff --git a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -index f33c03e81b7ff643741f56eea055e6af260de618..cf2922f692cc5dbc7e46c69f4ca968d16f761681 100644 +index 260202fab3ac300552c557b44dcf251f083c6a78..815af2adf1902dede676389d6fc29376c5cc1df2 100644 --- a/src/main/java/net/minecraft/world/entity/monster/EnderMan.java +++ b/src/main/java/net/minecraft/world/entity/monster/EnderMan.java -@@ -333,11 +333,18 @@ public class EnderMan extends Monster implements NeutralMob { +@@ -318,11 +318,18 @@ public class EnderMan extends Monster implements NeutralMob { private boolean teleport(double x, double y, double z) { BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(x, y, z); diff --git a/patches/server/0030-Pufferfish-Only-check-for-spooky-season-once-an-hour.patch b/patches/server/0021-Pufferfish-Only-check-for-spooky-season-once-an-hour.patch similarity index 64% rename from patches/server/0030-Pufferfish-Only-check-for-spooky-season-once-an-hour.patch rename to patches/server/0021-Pufferfish-Only-check-for-spooky-season-once-an-hour.patch index 1abfa65..8fa0231 100644 --- a/patches/server/0030-Pufferfish-Only-check-for-spooky-season-once-an-hour.patch +++ b/patches/server/0021-Pufferfish-Only-check-for-spooky-season-once-an-hour.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Pufferfish-Only-check-for-spooky-season-once-an-hour diff --git a/src/main/java/net/minecraft/world/entity/ambient/Bat.java b/src/main/java/net/minecraft/world/entity/ambient/Bat.java -index 44fa2d4f90389f5526746bd94a2450c03340bd0b..b1932466a8cb301f86b1244ed6caa47b341c301d 100644 +index dc27ddf5131e7398a5390a5187261d4c7fb6ccaa..daf1ca43b154dbff5ceb16c2366473fa3de260f8 100644 --- a/src/main/java/net/minecraft/world/entity/ambient/Bat.java +++ b/src/main/java/net/minecraft/world/entity/ambient/Bat.java @@ -3,6 +3,10 @@ package net.minecraft.world.entity.ambient; @@ -19,32 +19,7 @@ index 44fa2d4f90389f5526746bd94a2450c03340bd0b..b1932466a8cb301f86b1244ed6caa47b import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.syncher.EntityDataAccessor; -@@ -95,10 +99,12 @@ public class Bat extends AmbientCreature { - } - - @Override -- protected void doPush(Entity entity) {} -+ protected void doPush(Entity entity) { -+ } - - @Override -- protected void pushEntities() {} -+ protected void pushEntities() { -+ } - - public static AttributeSupplier.Builder createAttributes() { - return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D); -@@ -192,7 +198,8 @@ public class Bat extends AmbientCreature { - } - - @Override -- protected void checkFallDamage(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition) {} -+ protected void checkFallDamage(double heightDifference, boolean onGround, BlockState state, BlockPos landedPosition) { -+ } - - @Override - public boolean isIgnoringBlockTriggers() { -@@ -241,12 +248,24 @@ public class Bat extends AmbientCreature { +@@ -239,12 +243,24 @@ public class Bat extends AmbientCreature { } } @@ -63,8 +38,8 @@ index 44fa2d4f90389f5526746bd94a2450c03340bd0b..b1932466a8cb301f86b1244ed6caa47b + final long tickCount = region.getData().getCurrentTick(); + if (tickCount - lastSpookyCheck > ONE_HOUR) { + LocalDate localdate = LocalDate.now(); -+ int i = localdate.get(ChronoField.DAY_OF_MONTH); -+ int j = localdate.get(ChronoField.MONTH_OF_YEAR); ++ int i = localdate.getDayOfMonth(); ++ int j = localdate.getMonth().getValue(); + + //return j == 10 && i >= 20 || j == 11 && i <= 3; + isSpookySeason = j == 10 && i >= 20 || j == 11 && i <= 3; @@ -73,4 +48,4 @@ index 44fa2d4f90389f5526746bd94a2450c03340bd0b..b1932466a8cb301f86b1244ed6caa47b + return isSpookySeason; } - @Override + private void setupAnimationStates() { diff --git a/patches/server/0037-Allow-null-plugins-in-region-scheduler.patch b/patches/server/0022-Allow-null-plugins-in-region-scheduler.patch similarity index 100% rename from patches/server/0037-Allow-null-plugins-in-region-scheduler.patch rename to patches/server/0022-Allow-null-plugins-in-region-scheduler.patch diff --git a/patches/server/0025-Pufferfish-Optimize-suffocation.patch b/patches/server/0025-Pufferfish-Optimize-suffocation.patch deleted file mode 100644 index 9d92372..0000000 --- a/patches/server/0025-Pufferfish-Optimize-suffocation.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Thu, 4 Jan 2024 15:08:05 +0400 -Subject: [PATCH] Pufferfish-Optimize-suffocation - - -diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -index b873a692a13549ec39d0350703b2fafb6964e334..5aea39f46bdc4d61c817b37b61f2ff9ab1cb349f 100644 ---- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -+++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -35,6 +35,7 @@ public class FoldenorConfig { - public static int maxProjectileLoadsPerTick = 10; - - public static int acquirePoiForStuckEntityInterval = 60; -+ public static boolean enableSuffocationOptimization = true; - - public static RegionFileFormat regionFormatName = RegionFileFormat.ANVIL; - public static int regionFormatLinearCompressionLevel = 1; -@@ -126,6 +127,7 @@ public class FoldenorConfig { - maxProjectileLoadsPerProjectile = getInt("optimizations.max_projectile_loads_per_projectile",maxProjectileLoadsPerProjectile); - maxProjectileLoadsPerTick = getInt("optimizations.max_projectile_loads_per_tick",maxProjectileLoadsPerTick); - acquirePoiForStuckEntityInterval = getInt("optimizations.acquire_poi_for_stuck_entity_interval", acquirePoiForStuckEntityInterval); -+ enableSuffocationOptimization = getBoolean("optimizations.optimize_suffocation_check",enableSuffocationOptimization); - } - - private static void networkSettings() { -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 02bdcff43918be037255d6050497d01c1e7d697a..f1171eaa889edae0e30ea8ec47a93c344623cf96 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -18,6 +18,8 @@ import java.util.Optional; - import java.util.UUID; - import java.util.function.Predicate; - import javax.annotation.Nullable; -+ -+import net.edenor.foldenor.config.FoldenorConfig; - import net.minecraft.BlockUtil; - import net.minecraft.advancements.CriteriaTriggers; - import net.minecraft.commands.arguments.EntityAnchorArgument; -@@ -429,7 +431,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - boolean flag = this instanceof net.minecraft.world.entity.player.Player; - - if (!this.level().isClientSide) { -- if (this.isInWall()) { -+ if (shouldCheckForSuffocation() && this.isInWall()) { - this.hurt(this.damageSources().inWall(), 1.0F); - } else if (flag && !this.level().getWorldBorder().isWithinBounds(this.getBoundingBox())) { - double d0 = this.level().getWorldBorder().getDistanceToBorder(this) + this.level().getWorldBorder().getDamageSafeZone(); -@@ -1437,6 +1439,17 @@ public abstract class LivingEntity extends Entity implements Attackable { - return this.getHealth() <= 0.0F; - } - -+ public boolean couldPossiblyBeHurt(float amount) { -+ if ((float) this.invulnerableTime > (float) this.invulnerableDuration / 2.0F && amount <= this.lastHurt) { -+ return false; -+ } -+ return true; -+ } -+ -+ public boolean shouldCheckForSuffocation() { -+ return !FoldenorConfig.enableSuffocationOptimization || (tickCount % 10 == 0 && couldPossiblyBeHurt(1.0F)); -+ } -+ - @Override - public boolean hurt(DamageSource source, float amount) { - if (this.isInvulnerableTo(source)) { -diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -index 12440ee2dccc0a697fb403765f2e1b987ccc0283..f6ffb290ed6feec61250f062314776be4dc3f5a1 100644 ---- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -+++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java -@@ -151,6 +151,11 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob - this.bossEvent.setName(this.getDisplayName()); - } - -+ @Override -+ public boolean shouldCheckForSuffocation() { -+ return true; -+ } -+ - @Override - protected SoundEvent getAmbientSound() { - return SoundEvents.WITHER_AMBIENT; -diff --git a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -index 172e385fe6d1ba5f58481a58f8a3847b97a28488..f49c34d0aac0985d64a456f22d3d78512b8a69e9 100644 ---- a/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -+++ b/src/main/java/net/minecraft/world/level/material/FlowingFluid.java -@@ -308,7 +308,7 @@ public abstract class FlowingFluid extends Fluid { - object2bytelinkedopenhashmap.putAndMoveToFirst(block_a, (byte) (flag ? 1 : 0)); - } - */ -- -+ - if (cache != null) { - cache.putValue(block_a, flag); - } diff --git a/patches/server/0028-Pufferfish-Improve-container-checking-with-a-bitset.patch b/patches/server/0028-Pufferfish-Improve-container-checking-with-a-bitset.patch deleted file mode 100644 index 7e6a440..0000000 --- a/patches/server/0028-Pufferfish-Improve-container-checking-with-a-bitset.patch +++ /dev/null @@ -1,514 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Thu, 4 Jan 2024 15:35:36 +0400 -Subject: [PATCH] Pufferfish-Improve-container-checking-with-a-bitset - - -diff --git a/src/main/java/gg/airplane/structs/ItemListWithBitset.java b/src/main/java/gg/airplane/structs/ItemListWithBitset.java -new file mode 100644 -index 0000000000000000000000000000000000000000..adb3d7acd510c8336e67bc8cc9041fa9e0bc6b95 ---- /dev/null -+++ b/src/main/java/gg/airplane/structs/ItemListWithBitset.java -@@ -0,0 +1,114 @@ -+package gg.airplane.structs; -+ -+import net.minecraft.core.NonNullList; -+import net.minecraft.world.item.ItemStack; -+import org.apache.commons.lang.Validate; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.AbstractList; -+import java.util.Arrays; -+import java.util.List; -+ -+public class ItemListWithBitset extends AbstractList { -+ public static ItemListWithBitset fromList(List list) { -+ if (list instanceof ItemListWithBitset ours) { -+ return ours; -+ } -+ return new ItemListWithBitset(list); -+ } -+ -+ private static ItemStack[] createArray(int size) { -+ ItemStack[] array = new ItemStack[size]; -+ Arrays.fill(array, ItemStack.EMPTY); -+ return array; -+ } -+ -+ private final ItemStack[] items; -+ -+ private long bitSet = 0; -+ private final long allBits; -+ -+ private static class OurNonNullList extends NonNullList { -+ protected OurNonNullList(List delegate) { -+ super(delegate, ItemStack.EMPTY); -+ } -+ } -+ -+ public final NonNullList nonNullList = new OurNonNullList(this); -+ -+ private ItemListWithBitset(List list) { -+ this(list.size()); -+ -+ for (int i = 0; i < list.size(); i++) { -+ this.set(i, list.get(i)); -+ } -+ } -+ -+ public ItemListWithBitset(int size) { -+ Validate.isTrue(size < Long.BYTES * 8, "size is too large"); -+ -+ this.items = createArray(size); -+ this.allBits = ((1L << size) - 1); -+ } -+ -+ public boolean isCompletelyEmpty() { -+ return this.bitSet == 0; -+ } -+ -+ public boolean hasFullStacks() { -+ return (this.bitSet & this.allBits) == allBits; -+ } -+ -+ @Override -+ public ItemStack set(int index, @NotNull ItemStack itemStack) { -+ ItemStack existing = this.items[index]; -+ -+ this.items[index] = itemStack; -+ -+ if (itemStack == ItemStack.EMPTY) { -+ this.bitSet &= ~(1L << index); -+ } else { -+ this.bitSet |= 1L << index; -+ } -+ -+ return existing; -+ } -+ -+ @NotNull -+ @Override -+ public ItemStack get(int var0) { -+ return this.items[var0]; -+ } -+ -+ @Override -+ public int size() { -+ return this.items.length; -+ } -+ -+ @Override -+ public void clear() { -+ Arrays.fill(this.items, ItemStack.EMPTY); -+ } -+ -+ // these are unsupported for block inventories which have a static size -+ @Override -+ public void add(int var0, ItemStack var1) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public ItemStack remove(int var0) { -+ throw new UnsupportedOperationException(); -+ } -+ -+ @Override -+ public String toString() { -+ return "ItemListWithBitset{" + -+ "items=" + Arrays.toString(items) + -+ ", bitSet=" + Long.toString(bitSet, 2) + -+ ", allBits=" + Long.toString(allBits, 2) + -+ ", size=" + this.items.length + -+ '}'; -+ } -+} -diff --git a/src/main/java/net/minecraft/world/CompoundContainer.java b/src/main/java/net/minecraft/world/CompoundContainer.java -index 241fec02e6869c638d3a160819b32173a081467b..816e40961669dac6870702c741b432c9b8b4567a 100644 ---- a/src/main/java/net/minecraft/world/CompoundContainer.java -+++ b/src/main/java/net/minecraft/world/CompoundContainer.java -@@ -1,11 +1,13 @@ - package net.minecraft.world; - -+import net.minecraft.core.Direction; - import net.minecraft.world.entity.player.Player; - import net.minecraft.world.item.ItemStack; - - // CraftBukkit start - import java.util.ArrayList; - import java.util.List; -+ - import org.bukkit.Location; - - import org.bukkit.craftbukkit.entity.CraftHumanEntity; -@@ -64,6 +66,21 @@ public class CompoundContainer implements Container { - this.container2 = second; - } - -+ @Override -+ public boolean hasEmptySlot(Direction enumdirection) { -+ return this.container1.hasEmptySlot(null) || this.container2.hasEmptySlot(null); -+ } -+ -+ @Override -+ public boolean isCompletelyFull(Direction enumdirection) { -+ return this.container1.isCompletelyFull(null) && this.container2.isCompletelyFull(null); -+ } -+ -+ @Override -+ public boolean isCompletelyEmpty(Direction enumdirection) { -+ return this.container1.isCompletelyEmpty(null) && this.container2.isCompletelyEmpty(null); -+ } -+ - @Override - public int getContainerSize() { - return this.container1.getContainerSize() + this.container2.getContainerSize(); -diff --git a/src/main/java/net/minecraft/world/Container.java b/src/main/java/net/minecraft/world/Container.java -index d6cbe98e67fdbf8db46338a88ab1356dd63b50a3..42f1f8170501b84b00d90328560caea0e8d47098 100644 ---- a/src/main/java/net/minecraft/world/Container.java -+++ b/src/main/java/net/minecraft/world/Container.java -@@ -2,6 +2,8 @@ package net.minecraft.world; - - import java.util.Set; - import java.util.function.Predicate; -+ -+import net.minecraft.core.Direction; - import net.minecraft.core.BlockPos; - import net.minecraft.world.entity.player.Player; - import net.minecraft.world.item.Item; -@@ -15,6 +17,62 @@ import org.bukkit.craftbukkit.entity.CraftHumanEntity; - - public interface Container extends Clearable { - -+ default boolean hasEmptySlot(@org.jetbrains.annotations.Nullable Direction enumdirection) { // there is a slot with 0 items in it -+ if (this instanceof WorldlyContainer worldlyContainer) { -+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { -+ if (this.getItem(i).isEmpty()) { -+ return true; -+ } -+ } -+ } else { -+ int size = this.getContainerSize(); -+ for (int i = 0; i < size; i++) { -+ if (this.getItem(i).isEmpty()) { -+ return true; -+ } -+ } -+ } -+ return false; -+ } -+ -+ default boolean isCompletelyFull(@org.jetbrains.annotations.Nullable Direction enumdirection) { // every stack is maxed -+ if (this instanceof WorldlyContainer worldlyContainer) { -+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { -+ ItemStack itemStack = this.getItem(i); -+ if (itemStack.getCount() < itemStack.getMaxStackSize()) { -+ return false; -+ } -+ } -+ } else { -+ int size = this.getContainerSize(); -+ for (int i = 0; i < size; i++) { -+ ItemStack itemStack = this.getItem(i); -+ if (itemStack.getCount() < itemStack.getMaxStackSize()) { -+ return false; -+ } -+ } -+ } -+ return true; -+ } -+ -+ default boolean isCompletelyEmpty(@org.jetbrains.annotations.Nullable Direction enumdirection) { -+ if (this instanceof WorldlyContainer worldlyContainer) { -+ for (int i : worldlyContainer.getSlotsForFace(enumdirection)) { -+ if (!this.getItem(i).isEmpty()) { -+ return false; -+ } -+ } -+ } else { -+ int size = this.getContainerSize(); -+ for (int i = 0; i < size; i++) { -+ if (!this.getItem(i).isEmpty()) { -+ return false; -+ } -+ } -+ } -+ return true; -+ } -+ - int LARGE_MAX_STACK_SIZE = 64; - int DEFAULT_DISTANCE_LIMIT = 8; - -@@ -36,9 +94,11 @@ public interface Container extends Clearable { - - boolean stillValid(Player player); - -- default void startOpen(Player player) {} -+ default void startOpen(Player player) { -+ } - -- default void stopOpen(Player player) {} -+ default void stopOpen(Player player) { -+ } - - default boolean canPlaceItem(int slot, ItemStack stack) { - return true; -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java -index 756d0434472921992c9d84597d7c9c824e93614c..2b3cd3bb854b928f3a3d5a1a100b03ccc3e450ac 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecartContainer.java -@@ -29,6 +29,7 @@ import org.bukkit.inventory.InventoryHolder; - public abstract class AbstractMinecartContainer extends AbstractMinecart implements ContainerEntity { - - private NonNullList itemStacks; -+ private gg.airplane.structs.ItemListWithBitset itemStacksOptimized; - @Nullable - public ResourceLocation lootTable; - public long lootTableSeed; -@@ -90,7 +91,9 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme - - protected AbstractMinecartContainer(EntityType type, Level world) { - super(type, world); -- this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513 -+ //this.itemStacks = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); // CraftBukkit - SPIGOT-3513 -+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); // CraftBukkit - SPIGOT-3513 -+ this.itemStacks = this.itemStacksOptimized.nonNullList; - } - - protected AbstractMinecartContainer(EntityType type, double x, double y, double z, Level world) { -@@ -164,6 +167,8 @@ public abstract class AbstractMinecartContainer extends AbstractMinecart impleme - protected void readAdditionalSaveData(CompoundTag nbt) { - super.readAdditionalSaveData(nbt); - this.lootableData.loadNbt(nbt); // Paper -+ this.itemStacksOptimized = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); -+ this.itemStacks = this.itemStacksOptimized.nonNullList; - this.readChestVehicleSaveData(nbt); - } - -diff --git a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java -index 9b1243d96e0694c62fc9e82e9be540bce0d2b3ad..cac09c4563d54dbd4098d0a4a113b5cee31eba41 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/ChestBlockEntity.java -@@ -32,6 +32,7 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - - private static final int EVENT_SET_OPEN_COUNT = 1; - private NonNullList items; -+ private gg.airplane.structs.ItemListWithBitset optimizedItems; - public final ContainerOpenersCounter openersCounter; - private final ChestLidController chestLidController; - -@@ -65,9 +66,12 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - } - // CraftBukkit end - -+ private final boolean isNative = getClass().equals(ChestBlockEntity.class); - protected ChestBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { - super(type, pos, state); -- this.items = NonNullList.withSize(27, ItemStack.EMPTY); -+ //this.items = NonNullList.withSize(27, ItemStack.EMPTY); -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(27); -+ this.items = this.optimizedItems.nonNullList; - this.openersCounter = new ContainerOpenersCounter() { - @Override - protected void onOpen(Level world, BlockPos pos, BlockState state) { -@@ -98,6 +102,21 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - this.chestLidController = new ChestLidController(); - } - -+ @Override -+ public boolean hasEmptySlot(Direction enumdirection) { -+ return isNative ? !this.optimizedItems.hasFullStacks() : super.hasEmptySlot(enumdirection); -+ } -+ -+ @Override -+ public boolean isCompletelyFull(Direction enumdirection) { -+ return isNative ? this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection) : super.isCompletelyFull(enumdirection); -+ } -+ -+ @Override -+ public boolean isCompletelyEmpty(Direction enumdirection) { -+ return isNative && this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection); -+ } -+ - public ChestBlockEntity(BlockPos pos, BlockState state) { - this(BlockEntityType.CHEST, pos, state); - } -@@ -115,7 +134,9 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - @Override - public void load(CompoundTag nbt) { - super.load(nbt); -- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); -+ //this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); -+ this.items = this.optimizedItems.nonNullList; - if (!this.tryLoadLootTable(nbt)) { - ContainerHelper.loadAllItems(nbt, this.items); - } -@@ -187,7 +208,9 @@ public class ChestBlockEntity extends RandomizableContainerBlockEntity implement - - @Override - protected void setItems(NonNullList list) { -- this.items = list; -+ //this.items = list; -+ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list); -+ this.items = this.optimizedItems.nonNullList; - } - - @Override -diff --git a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java -index 82e7e76fecceb55522b5828a56f036e42ef55201..58715a71cdea57777a37acbc0faf940798487fd5 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/HopperBlockEntity.java -@@ -6,6 +6,7 @@ import java.util.function.BooleanSupplier; - import java.util.stream.Collectors; - import java.util.stream.IntStream; - import javax.annotation.Nullable; -+ - import net.minecraft.core.BlockPos; - import net.minecraft.core.Direction; - import net.minecraft.core.NonNullList; -@@ -51,6 +52,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - private NonNullList items; - public int cooldownTime; - private long tickedGameTime = Long.MIN_VALUE; // Folia - region threading -+ private gg.airplane.structs.ItemListWithBitset optimizedItems; - - // CraftBukkit start - add fields and methods - public List transaction = new java.util.ArrayList(); -@@ -94,14 +96,33 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - public HopperBlockEntity(BlockPos pos, BlockState state) { - super(BlockEntityType.HOPPER, pos, state); -- this.items = NonNullList.withSize(5, ItemStack.EMPTY); -+ //this.items = NonNullList.withSize(5, ItemStack.EMPTY); -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(5); -+ this.items = this.optimizedItems.nonNullList; - this.cooldownTime = -1; - } - -+ @Override -+ public boolean hasEmptySlot(Direction enumdirection) { -+ return !this.optimizedItems.hasFullStacks(); -+ } -+ -+ @Override -+ public boolean isCompletelyFull(Direction enumdirection) { -+ return this.optimizedItems.hasFullStacks() && super.isCompletelyFull(enumdirection); -+ } -+ -+ @Override -+ public boolean isCompletelyEmpty(Direction enumdirection) { -+ return this.optimizedItems.isCompletelyEmpty() || super.isCompletelyEmpty(enumdirection); -+ } -+ - @Override - public void load(CompoundTag nbt) { - super.load(nbt); -- this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); -+ //this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); -+ this.optimizedItems = new gg.airplane.structs.ItemListWithBitset(this.getContainerSize()); -+ this.items = this.optimizedItems.nonNullList; - if (!this.tryLoadLootTable(nbt)) { - ContainerHelper.loadAllItems(nbt, this.items); - } -@@ -437,6 +458,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - } - return true; - } -+ - private static final java.util.function.BiPredicate STACK_SIZE_TEST = (itemstack, i) -> itemstack.getCount() >= itemstack.getMaxStackSize(); - private static final java.util.function.BiPredicate IS_EMPTY_TEST = (itemstack, i) -> itemstack.isEmpty(); - // Paper end - Perf: Optimize Hoppers -@@ -481,7 +503,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - // } - // int origCount = event.getItem().getAmount(); // Spigot - // ItemStack itemstack1 = HopperBlockEntity.addItem(iinventory, iinventory1, CraftItemStack.asNMSCopy(event.getItem()), enumdirection); -- // CraftBukkit end -+ // CraftBukkit end - - // if (itemstack1.isEmpty()) { - // iinventory1.setChanged(); -@@ -526,7 +548,8 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - } - - private static boolean isEmptyContainer(Container inv, Direction facing) { -- return allMatch(inv, facing, IS_EMPTY_TEST); // Paper - Perf: Optimize Hoppers -+ //return allMatch(inv, facing, IS_EMPTY_TEST); // Paper - Perf: Optimize Hoppers -+ return inv.isCompletelyEmpty(facing); - } - - public static boolean suckInItems(Level world, Hopper hopper) { -@@ -608,11 +631,11 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - // if (event.isCancelled()) { - // iinventory.setItem(i, itemstack1); - -- // if (ihopper instanceof HopperBlockEntity) { -- // ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot -- // } -+ // if (ihopper instanceof HopperBlockEntity) { -+ // ((HopperBlockEntity) ihopper).setCooldown(world.spigotConfig.hopperTransfer); // Spigot -+ // } - -- // return false; -+ // return false; - // } - // int origCount = event.getItem().getAmount(); // Spigot - // ItemStack itemstack2 = HopperBlockEntity.addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null); -@@ -727,7 +750,8 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - if (HopperBlockEntity.canPlaceItemInContainer(to, stack, slot, side)) { - boolean flag = false; -- boolean flag1 = to.isEmpty(); -+ //boolean flag1 = to.isEmpty(); -+ boolean flag1 = to.isCompletelyEmpty(side); - - if (itemstack1.isEmpty()) { - // Spigot start - SPIGOT-6693, InventorySubcontainer#setItem -@@ -851,12 +875,13 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - // Paper start - Perf: Optimize Hoppers - return HopperBlockEntity.getContainerAt(world, x, y, z, false); - } -+ - @Nullable - private static Container getContainerAt(Level world, double x, double y, double z, final boolean optimizeEntities) { - // Paper end - Perf: Optimize Hoppers - Object object = null; - BlockPos blockposition = BlockPos.containing(x, y, z); -- if ( !world.spigotConfig.hopperCanLoadChunks && !world.hasChunkAt( blockposition ) ) return null; // Spigot -+ if (!world.spigotConfig.hopperCanLoadChunks && !world.hasChunkAt(blockposition)) return null; // Spigot - BlockState iblockdata = world.getBlockState(blockposition); - Block block = iblockdata.getBlock(); - -@@ -922,7 +947,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - - @Override - protected void setItems(NonNullList list) { -- this.items = list; -+ //this.items = list; -+ this.optimizedItems = gg.airplane.structs.ItemListWithBitset.fromList(list); -+ this.items = this.optimizedItems.nonNullList; - } - - public static void entityInside(Level world, BlockPos pos, BlockState state, Entity entity, HopperBlockEntity blockEntity) { -diff --git a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -index f52ccd4f3e062af3c7cc6eaea5b074a3bbd21690..13ebc0415276c4866435ab852ab42baf763635b5 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/RandomizableContainerBlockEntity.java -@@ -94,13 +94,13 @@ public abstract class RandomizableContainerBlockEntity extends BaseContainerBloc - public boolean isEmpty() { - this.unpackLootTable((Player)null); - // Paper start - Perf: Optimize Hoppers -- for (final ItemStack itemStack : this.getItems()) { -+ /*for (final ItemStack itemStack : this.getItems()) { - if (!itemStack.isEmpty()) { - return false; - } - } -- return true; -- // Paper end - Perf: Optimize Hoppers -+ return true;*/ -+ return this.isCompletelyEmpty(null); - } - - @Override diff --git a/patches/server/0029-Pufferfish-Fix-Paper-6045-block-goal-shouldn-t-load-.patch b/patches/server/0029-Pufferfish-Fix-Paper-6045-block-goal-shouldn-t-load-.patch deleted file mode 100644 index 8aa3a12..0000000 --- a/patches/server/0029-Pufferfish-Fix-Paper-6045-block-goal-shouldn-t-load-.patch +++ /dev/null @@ -1,18 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Thu, 4 Jan 2024 15:37:48 +0400 -Subject: [PATCH] Pufferfish-Fix-Paper#6045-block-goal-shouldn-t-load-chunks - - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -index 07519c817cc6de04a98198c43a0c2b02ba3141eb..99fe207b4eac08191b58c13e6af77311a2237356 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/MoveToBlockGoal.java -@@ -120,6 +120,7 @@ public abstract class MoveToBlockGoal extends Goal { - for(int m = 0; m <= l; m = m > 0 ? -m : 1 - m) { - for(int n = m < l && m > -l ? l : 0; n <= l; n = n > 0 ? -n : 1 - n) { - mutableBlockPos.setWithOffset(blockPos, m, k - 1, n); -+ if (!this.mob.level().hasChunkAt(mutableBlockPos)) continue; - if (this.mob.isWithinRestriction(mutableBlockPos) && this.isValidTarget(this.mob.level(), mutableBlockPos)) { - this.blockPos = mutableBlockPos; - this.mob.movingTarget = mutableBlockPos == BlockPos.ZERO ? null : mutableBlockPos.immutable(); // Paper diff --git a/patches/server/0031-Gale-Variable-entity-wake-up-duration.patch b/patches/server/0031-Gale-Variable-entity-wake-up-duration.patch deleted file mode 100644 index fed6527..0000000 --- a/patches/server/0031-Gale-Variable-entity-wake-up-duration.patch +++ /dev/null @@ -1,83 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Thu, 4 Jan 2024 15:58:12 +0400 -Subject: [PATCH] Gale-Variable-entity-wake-up-duration - - -diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -index 5aea39f46bdc4d61c817b37b61f2ff9ab1cb349f..3a531564559bbd333b7bb56b9896c34056576a94 100644 ---- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -+++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -33,6 +33,7 @@ public class FoldenorConfig { - public static int maxProjectileLoadsPerProjectile = 10; - - public static int maxProjectileLoadsPerTick = 10; -+ public static double entityWakeUpDurationRatioStandardDeviation = 0.2; - - public static int acquirePoiForStuckEntityInterval = 60; - public static boolean enableSuffocationOptimization = true; -@@ -128,6 +129,7 @@ public class FoldenorConfig { - maxProjectileLoadsPerTick = getInt("optimizations.max_projectile_loads_per_tick",maxProjectileLoadsPerTick); - acquirePoiForStuckEntityInterval = getInt("optimizations.acquire_poi_for_stuck_entity_interval", acquirePoiForStuckEntityInterval); - enableSuffocationOptimization = getBoolean("optimizations.optimize_suffocation_check",enableSuffocationOptimization); -+ entityWakeUpDurationRatioStandardDeviation = getDouble("optimizations.entity_wakeup_duration_ratio_standard_deviation",entityWakeUpDurationRatioStandardDeviation); - } - - private static void networkSettings() { -diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index 877e6286fd4860e4270fee3ddb79a5f4041343b7..cd46b1131a33fd84b44909a3f50aa95026418bef 100644 ---- a/src/main/java/org/spigotmc/ActivationRange.java -+++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -1,5 +1,6 @@ - package org.spigotmc; - -+import net.edenor.foldenor.config.FoldenorConfig; - import net.minecraft.core.BlockPos; - import net.minecraft.server.MinecraftServer; - import net.minecraft.server.level.ServerChunkCache; -@@ -71,22 +72,22 @@ public class ActivationRange - if (entity.activationType == ActivationType.VILLAGER) { - if (inactiveFor > config.wakeUpInactiveVillagersEvery && worldData.wakeupInactiveRemainingVillagers > 0) { // Folia - threaded regions - worldData.wakeupInactiveRemainingVillagers--; // Folia - threaded regions -- return config.wakeUpInactiveVillagersFor; -+ return getWakeUpDurationWithVariance(entity, config.wakeUpInactiveVillagersFor); - } - } else if (entity.activationType == ActivationType.ANIMAL) { - if (inactiveFor > config.wakeUpInactiveAnimalsEvery && worldData.wakeupInactiveRemainingAnimals > 0) { // Folia - threaded regions - worldData.wakeupInactiveRemainingAnimals--; // Folia - threaded regions -- return config.wakeUpInactiveAnimalsFor; -+ return getWakeUpDurationWithVariance(entity, config.wakeUpInactiveAnimalsFor); - } - } else if (entity.activationType == ActivationType.FLYING_MONSTER) { - if (inactiveFor > config.wakeUpInactiveFlyingEvery && worldData.wakeupInactiveRemainingFlying > 0) { // Folia - threaded regions - worldData.wakeupInactiveRemainingFlying--; // Folia - threaded regions -- return config.wakeUpInactiveFlyingFor; -+ return getWakeUpDurationWithVariance(entity, config.wakeUpInactiveFlyingFor); - } - } else if (entity.activationType == ActivationType.MONSTER || entity.activationType == ActivationType.RAIDER) { - if (inactiveFor > config.wakeUpInactiveMonstersEvery && worldData.wakeupInactiveRemainingMonsters > 0) { // Folia - threaded regions - worldData.wakeupInactiveRemainingMonsters--; // Folia - threaded regions -- return config.wakeUpInactiveMonstersFor; -+ return getWakeUpDurationWithVariance(entity, config.wakeUpInactiveMonstersFor); - } - } - return -1; -@@ -95,6 +96,18 @@ public class ActivationRange - - // Folia - threaded regions - replaced by local variable - -+ private static final java.util.Random wakeUpDurationRandom = new java.util.Random(); -+ -+ private static int getWakeUpDurationWithVariance(Entity entity, int wakeUpDuration) { -+ double deviation = FoldenorConfig.entityWakeUpDurationRatioStandardDeviation; -+ if (deviation <= 0) { -+ return wakeUpDuration; -+ } -+ return (int) Math.min(Integer.MAX_VALUE, Math.max(1, Math.round(wakeUpDuration * wakeUpDurationRandom.nextGaussian(1, deviation)))); -+ } -+ -+ static AABB maxBB = new AABB( 0, 0, 0, 0, 0, 0 ); -+ - /** - * Initializes an entities type on construction to specify what group this - * entity is in for activation ranges. diff --git a/patches/server/0032-Gale-Don-t-load-chunks-to-activate-climbing-entities.patch b/patches/server/0032-Gale-Don-t-load-chunks-to-activate-climbing-entities.patch deleted file mode 100644 index b0918ca..0000000 --- a/patches/server/0032-Gale-Don-t-load-chunks-to-activate-climbing-entities.patch +++ /dev/null @@ -1,89 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Thu, 4 Jan 2024 16:01:40 +0400 -Subject: [PATCH] Gale-Don-t-load-chunks-to-activate-climbing-entities - - -diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -index 3a531564559bbd333b7bb56b9896c34056576a94..c668d7e2c64379e48a12a4c93dbdf18a5ed8cbe7 100644 ---- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -+++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -34,6 +34,7 @@ public class FoldenorConfig { - - public static int maxProjectileLoadsPerTick = 10; - public static double entityWakeUpDurationRatioStandardDeviation = 0.2; -+ public static boolean loadChunksToActiveClimbingEntities = false; - - public static int acquirePoiForStuckEntityInterval = 60; - public static boolean enableSuffocationOptimization = true; -@@ -130,6 +131,7 @@ public class FoldenorConfig { - acquirePoiForStuckEntityInterval = getInt("optimizations.acquire_poi_for_stuck_entity_interval", acquirePoiForStuckEntityInterval); - enableSuffocationOptimization = getBoolean("optimizations.optimize_suffocation_check",enableSuffocationOptimization); - entityWakeUpDurationRatioStandardDeviation = getDouble("optimizations.entity_wakeup_duration_ratio_standard_deviation",entityWakeUpDurationRatioStandardDeviation); -+ loadChunksToActiveClimbingEntities = getBoolean("optimizations.load_chunks_to_active_climbing_entities",loadChunksToActiveClimbingEntities); - } - - private static void networkSettings() { -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index b92734449af7917475a20c20a7833315af8e095b..479a52491b884ac99738a67b187e1f12ba7eb6b7 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -5367,6 +5367,14 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S - return this.feetBlockState; - } - -+ public @Nullable BlockState getFeetBlockStateIfLoaded() { -+ if (this.feetBlockState == null) { -+ this.feetBlockState = this.level.getBlockStateIfLoaded(this.blockPosition()); -+ } -+ -+ return this.feetBlockState; -+ } -+ - public ChunkPos chunkPosition() { - return this.chunkPosition; - } -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index f1171eaa889edae0e30ea8ec47a93c344623cf96..d54d45de011325bdf87eff6b1716852eeecea3b7 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2061,18 +2061,36 @@ public abstract class LivingEntity extends Entity implements Attackable { - - public boolean onClimableCached() { - if (!this.blockPosition().equals(this.lastClimbingPosition)) { -- this.cachedOnClimable = this.onClimbable(); -- this.lastClimbingPosition = this.blockPosition(); -+ Boolean onClimbableIfLoaded = this.onClimbable(FoldenorConfig.loadChunksToActiveClimbingEntities); -+ if (onClimbableIfLoaded != null) { -+ this.cachedOnClimable = onClimbableIfLoaded; -+ this.lastClimbingPosition = this.blockPosition(); -+ } else { -+ this.cachedOnClimable = false; -+ this.lastClimbingPosition = null; -+ } - } - return this.cachedOnClimable; - } - - public boolean onClimbable() { -+ return onClimbable(true); -+ } -+ -+ public Boolean onClimbable(boolean loadChunk) { - if (this.isSpectator()) { - return false; - } else { - BlockPos blockposition = this.blockPosition(); -- BlockState iblockdata = this.getFeetBlockState(); -+ BlockState iblockdata; -+ if (loadChunk) { -+ iblockdata = this.getFeetBlockState(); -+ } else { -+ iblockdata = this.getFeetBlockStateIfLoaded(); -+ if (iblockdata == null) { -+ return null; -+ } -+ } - - if (iblockdata.is(BlockTags.CLIMBABLE)) { - this.lastClimbablePos = Optional.of(blockposition); diff --git a/patches/server/0033-Gale-Optimize-sun-burn-tick.patch b/patches/server/0033-Gale-Optimize-sun-burn-tick.patch deleted file mode 100644 index 818a871..0000000 --- a/patches/server/0033-Gale-Optimize-sun-burn-tick.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Thu, 4 Jan 2024 16:04:18 +0400 -Subject: [PATCH] Gale-Optimize-sun-burn-tick - - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 479a52491b884ac99738a67b187e1f12ba7eb6b7..8dfe609d58b44f5ea3c28b7956182aa2d5a46404 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -2036,7 +2036,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S - /** @deprecated */ - @Deprecated - public float getLightLevelDependentMagicValue() { -- return this.level().hasChunkAt(this.getBlockX(), this.getBlockZ()) ? this.level().getLightLevelDependentMagicValue(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())) : 0.0F; -+ return this.getLightLevelDependentMagicValue(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())); -+ } -+ -+ public float getLightLevelDependentMagicValue(BlockPos pos) { -+ return this.level().hasChunkAt(this.getBlockX(), this.getBlockZ()) ? this.level.getLightLevelDependentMagicValue(pos) : 0.0F; - } - - public void absMoveTo(double x, double y, double z, float yaw, float pitch) { -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 54297b9dde40fe853d873d60373cd8c0a3c9466b..36560f3a3154353ae6e921365bae394b06a464c5 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -1742,13 +1742,25 @@ public abstract class Mob extends LivingEntity implements Targeting { - - } - -+ private BlockPos cached_eye_blockpos; -+ private int cached_position_hashcode; -+ - public boolean isSunBurnTick() { - if (this.level().isDay() && !this.level().isClientSide) { -- float f = this.getLightLevelDependentMagicValue(); -- BlockPos blockposition = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ()); -+ int positionHashCode = this.position().hashCode(); -+ if (this.cached_position_hashcode != positionHashCode) { -+ this.cached_eye_blockpos = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ()); -+ this.cached_position_hashcode = positionHashCode; -+ } -+ -+ float f = this.getLightLevelDependentMagicValue(cached_eye_blockpos); // Pass BlockPos to getBrightness -+ -+ // Check brightness first -+ if (f <= 0.5F) return false; -+ if (this.random.nextFloat() * 30.0F >= (f - 0.4F) * 2.0F) return false; - boolean flag = this.isInWaterRainOrBubble() || this.isInPowderSnow || this.wasInPowderSnow; - -- if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && !flag && this.level().canSeeSky(blockposition)) { -+ if (!flag && this.level().canSeeSky(this.cached_eye_blockpos)) { - return true; - } - } diff --git a/patches/server/0034-Change-reload-config-work.patch b/patches/server/0034-Change-reload-config-work.patch deleted file mode 100644 index 03d26e9..0000000 --- a/patches/server/0034-Change-reload-config-work.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Thu, 4 Jan 2024 16:39:26 +0400 -Subject: [PATCH] Change-reload-config-work - - -diff --git a/src/main/java/net/edenor/foldenor/commands/FoldenorReloadConfigCommand.java b/src/main/java/net/edenor/foldenor/commands/FoldenorReloadConfigCommand.java -index 4188e8db6386fe1790192c9efd8c2fe256ce6418..9b49dafe2706a41200b1f2ee74f4b857346e5418 100644 ---- a/src/main/java/net/edenor/foldenor/commands/FoldenorReloadConfigCommand.java -+++ b/src/main/java/net/edenor/foldenor/commands/FoldenorReloadConfigCommand.java -@@ -38,7 +38,7 @@ public class FoldenorReloadConfigCommand extends Command { - if (!testPermission(sender)) return true; - - try { -- FoldenorConfig.reload((java.io.File) DedicatedServer.getServer().options.valueOf("foldenor-settings")); -+ FoldenorConfig.readConfig(); - Command.broadcastCommandMessage(sender, text("Foldenor config reload complete.", GREEN)); - return true; - } catch (Exception e) { -diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -index c668d7e2c64379e48a12a4c93dbdf18a5ed8cbe7..085495a2d82f2a514a01602c8aece5ace57eb240 100644 ---- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -+++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -83,7 +83,7 @@ public class FoldenorConfig { - Bukkit.getLogger().log(level, s); - } - -- static void readConfig() { -+ public static void readConfig() { - networkSettings(); - - optimizationSettings(); diff --git a/patches/server/0035-Pufferfish-Dynamic-Activation-of-Brain.patch b/patches/server/0035-Pufferfish-Dynamic-Activation-of-Brain.patch deleted file mode 100644 index eb402ec..0000000 --- a/patches/server/0035-Pufferfish-Dynamic-Activation-of-Brain.patch +++ /dev/null @@ -1,447 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Fri, 5 Jan 2024 14:20:43 +0400 -Subject: [PATCH] Pufferfish-Dynamic-Activation-of-Brain - - -diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -index 085495a2d82f2a514a01602c8aece5ace57eb240..5067fabb3a565d424248ce73212d9542d21bf06b 100644 ---- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -+++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -3,6 +3,9 @@ package net.edenor.foldenor.config; - import com.google.common.base.Throwables; - import com.google.common.collect.ImmutableMap; - import net.edenor.foldenor.region.RegionFileFormat; -+import net.minecraft.core.registries.BuiltInRegistries; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.world.entity.EntityType; - import org.bukkit.Bukkit; - import org.bukkit.configuration.ConfigurationSection; - import org.bukkit.configuration.InvalidConfigurationException; -@@ -13,9 +16,11 @@ import java.io.IOException; - import java.lang.reflect.InvocationTargetException; - import java.lang.reflect.Method; - import java.lang.reflect.Modifier; -+import java.util.Collections; - import java.util.List; - import java.util.Map; - import java.util.logging.Level; -+ - public class FoldenorConfig { - protected static final String HEADER = "This is the main configuration file for Foldenor."; - protected static File CONFIG_FILE; -@@ -44,6 +49,12 @@ public class FoldenorConfig { - public static int linearFlushFrequency = 10; - public static int linearFlushThreads = 1; - -+ public static boolean dearEnabled; -+ public static int startDistance; -+ public static int startDistanceSquared; -+ public static int maximumActivationPrio; -+ public static int activationDistanceMod; -+ - public static void init(File configFile) { - init(configFile, true); - } -@@ -58,10 +69,11 @@ public class FoldenorConfig { - if (configFile.exists()) { - try { - config.load(CONFIG_FILE); -- } catch (InvalidConfigurationException ex){ -+ } catch (InvalidConfigurationException ex) { - Bukkit.getLogger().log(Level.SEVERE, "Could not load foldenor.yml, please correct your syntax errors", ex); - throw Throwables.propagate(ex); -- } catch (IOException ignore) {} -+ } catch (IOException ignore) { -+ } - } - config.options().header(HEADER); - config.options().copyDefaults(true); -@@ -92,6 +104,8 @@ public class FoldenorConfig { - - worldSettings(); - -+ initDAB(); -+ - try { - config.save(CONFIG_FILE); - } catch (IOException ex) { -@@ -99,7 +113,7 @@ public class FoldenorConfig { - } - } - -- private static void worldSettings(){ -+ private static void worldSettings() { - regionFormatName = RegionFileFormat.fromString(getString("world.region.format", regionFormatName.name())); - if (regionFormatName.equals(RegionFileFormat.INVALID)) { - log(Level.SEVERE, "Unknown region format in foldenor.yml: " + regionFormatName); -@@ -126,12 +140,22 @@ public class FoldenorConfig { - } - - private static void optimizationSettings() { -- maxProjectileLoadsPerProjectile = getInt("optimizations.max_projectile_loads_per_projectile",maxProjectileLoadsPerProjectile); -- maxProjectileLoadsPerTick = getInt("optimizations.max_projectile_loads_per_tick",maxProjectileLoadsPerTick); -+ maxProjectileLoadsPerProjectile = getInt("optimizations.max_projectile_loads_per_projectile", maxProjectileLoadsPerProjectile); -+ maxProjectileLoadsPerTick = getInt("optimizations.max_projectile_loads_per_tick", maxProjectileLoadsPerTick); - acquirePoiForStuckEntityInterval = getInt("optimizations.acquire_poi_for_stuck_entity_interval", acquirePoiForStuckEntityInterval); -+<<<<<<< HEAD - enableSuffocationOptimization = getBoolean("optimizations.optimize_suffocation_check",enableSuffocationOptimization); - entityWakeUpDurationRatioStandardDeviation = getDouble("optimizations.entity_wakeup_duration_ratio_standard_deviation",entityWakeUpDurationRatioStandardDeviation); - loadChunksToActiveClimbingEntities = getBoolean("optimizations.load_chunks_to_active_climbing_entities",loadChunksToActiveClimbingEntities); -+======= -+ enableSuffocationOptimization = getBoolean("optimizations.optimize_suffocation_check", enableSuffocationOptimization); -+ entityWakeUpDurationRatioStandardDeviation = getDouble("optimizations.entity_wakeup_duration_ratio_standard_deviation", entityWakeUpDurationRatioStandardDeviation); -+ loadChunksToActiveClimbingEntities = getBoolean("optimizations.load_chunks_to_active_climbing_entities", loadChunksToActiveClimbingEntities); -+ -+ -+ enableAsyncMobSpawning = getBoolean("optimizations.enable_async_mob_spawning", enableAsyncMobSpawning); -+ RegionizedWorldData.initMobSpawningExecutor(); -+>>>>>>> Pufferfish-Dynamic-Activation-of-Brain - } - - private static void networkSettings() { -@@ -140,6 +164,31 @@ public class FoldenorConfig { - alternateKeepAlive = getBoolean("network.alternate-keepalive", alternateKeepAlive); - } - -+ private static void initDAB() { -+ dearEnabled = getBoolean("optimizations.dab.enabled", true); -+ startDistance = getInt("optimizations.dab.start-distance", 12, -+ "This value determines how far away an entity has to be\n" + -+ "from the player to start being effected by DEAR."); -+ startDistanceSquared = startDistance * startDistance; -+ maximumActivationPrio = getInt("optimizations.dab.max-tick-freq", 20, -+ "This value defines how often in ticks, the furthest entity\n" + -+ "will get their pathfinders and behaviors ticked. 20 = 1s"); -+ activationDistanceMod = getInt("optimizations.dab.activation-dist-mod", 8, -+ """ -+ This value defines how much distance modifies an entity's -+ tick frequency. freq = (distanceToPlayer^2) / (2^value) -+ If you want further away entities to tick less often, use 7. -+ If you want further away entities to tick more often, try 9."""); -+ -+ for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { -+ entityType.dabEnabled = true; // reset all, before setting the ones to true -+ } -+ getList("optimizations.dab.blacklisted-entities", Collections.emptyList(), "A list of entities to ignore for activation") -+ .forEach(name -> EntityType.byString((String) name).ifPresentOrElse(entityType -> { -+ entityType.dabEnabled = false; -+ }, () -> MinecraftServer.LOGGER.warn("Unknown entity \"" + name + "\""))); -+ } -+ - protected static void set(String path, Object val) { - config.addDefault(path, val); - config.set(path, val); -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index c0d1e2f0146f75aa264263a445013900564b497f..bf3332b3156ccbfddb161ad887eab5584f8693bd 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -992,6 +992,7 @@ public class ServerLevel extends Level implements WorldGenLevel { - this.timings.entityTick.startTiming(); // Spigot - profiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.ENTITY_TICK); try { // Folia - profiler - regionizedWorldData.forEachTickingEntity((entity) -> { // Folia - regionised ticking -+ entity.activatedPriorityReset = false; - if (!entity.isRemoved()) { - if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed - entity.discard(); -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 5333ba63c9f79e16667e4c7056b53566d5771d5f..be9e19e5ab4e184f66ea46049067a141b9e4344b 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -450,7 +450,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S - ServerLevel world = this.portalWorld; - this.portalBlock = null; - this.portalWorld = null; -- -+ - if (pos == null || world == null || world != this.level) { - return; - } -@@ -483,6 +483,10 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S - - // Folia - region ticking - // Paper end - optimise entity tracking -+ public boolean activatedPriorityReset = false; -+ public int activatedPriority = FoldenorConfig.maximumActivationPrio; -+ -+ // Paper end - public float getBukkitYaw() { - return this.yRot; - } -diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java -index f921c159c4f7556daf3c8405241de3607ba251ad..29e9bd15cf63da7b1574530cedee90b96c3b0839 100644 ---- a/src/main/java/net/minecraft/world/entity/EntityType.java -+++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -305,6 +305,7 @@ public class EntityType implements FeatureElement, EntityTypeT - private final boolean canSpawnFarFromPlayer; - private final int clientTrackingRange; - private final int updateInterval; -+ public boolean dabEnabled = false; - @Nullable - private String descriptionId; - @Nullable -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 36560f3a3154353ae6e921365bae394b06a464c5..17487b5c3e67eff6a3860aa74329e1adf172f535 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -234,10 +234,10 @@ public abstract class Mob extends LivingEntity implements Targeting { - @Override - public void inactiveTick() { - super.inactiveTick(); -- if (this.goalSelector.inactiveTick()) { -+ if (this.goalSelector.inactiveTick(this.activatedPriority, true)) { - this.goalSelector.tick(); - } -- if (this.targetSelector.inactiveTick()) { -+ if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { - this.targetSelector.tick(); - } - } -@@ -933,16 +933,20 @@ public abstract class Mob extends LivingEntity implements Targeting { - - if (i % 2 != 0 && this.tickCount > 1) { - this.level().getProfiler().push("targetSelector"); -+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) - this.targetSelector.tickRunningGoals(false); - this.level().getProfiler().pop(); - this.level().getProfiler().push("goalSelector"); -+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) - this.goalSelector.tickRunningGoals(false); - this.level().getProfiler().pop(); - } else { - this.level().getProfiler().push("targetSelector"); -+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) - this.targetSelector.tick(); - this.level().getProfiler().pop(); - this.level().getProfiler().push("goalSelector"); -+ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) - this.goalSelector.tick(); - this.level().getProfiler().pop(); - } -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -index 676f5485a4ca9252e911213dcda8d51776b637b6..54cc01f16bd7962e5c01c548ce70a08bb1264276 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java -@@ -11,6 +11,8 @@ import java.util.Set; - import java.util.function.Predicate; - import java.util.function.Supplier; - import java.util.stream.Stream; -+ -+import net.edenor.foldenor.config.FoldenorConfig; - import net.minecraft.util.profiling.ProfilerFiller; - import org.slf4j.Logger; - -@@ -53,9 +55,12 @@ public class GoalSelector { - } - - // Paper start -- public boolean inactiveTick() { -+ //public boolean inactiveTick() { -+ public boolean inactiveTick(int tickRate, boolean inactive) { // Pufferfish start -+ if (inactive && !FoldenorConfig.dearEnabled) tickRate = 4; // reset to Paper's -+ tickRate = Math.min(tickRate, this.newGoalRate); - this.curRate++; -- return this.curRate % this.newGoalRate == 0; -+ return this.curRate % tickRate == 0; - } - public boolean hasTasks() { - for (WrappedGoal task : this.availableGoals) { -diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -index 5ad5f22e5aa26445e5eb229958e7bf356bdd460e..6bfac7285195a31b3b857e07936b78840b190c75 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -+++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java -@@ -221,9 +221,11 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS - return 0.4F; - } - -+ private int behaviorTick = 0; - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("allayBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("allayActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -index b21e180641d17438997a80e5bcb0ec7998d24a2e..e1ed4f03e776850c610fb47a3c59692185a8d6ff 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -+++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java -@@ -275,9 +275,11 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder { - - } - -+ private int behaviorTick = 0; - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("frogBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) - this.getBrain().tick((ServerLevel)this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("frogActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -index 958816ce2166248b542c96c10c398a52d769b4db..b60ffa5f9c5933bb42bad9b48332a0742050ea8e 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java -@@ -80,9 +80,11 @@ public class Tadpole extends AbstractFish { - return SoundEvents.TADPOLE_FLOP; - } - -+ private int behaviorTick = 0; - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("tadpoleBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("tadpoleActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -index 5d247ac38fe8a61603b3d934f3000bcda773142b..0d32a7cd1581b56ccc95038c703a22b904950f07 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java -@@ -191,9 +191,11 @@ public class Goat extends Animal { - return (Brain) super.getBrain(); // CraftBukkit - decompile error - } - -+ private int behaviorTick = 0; - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("goatBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - this.level().getProfiler().push("goatActivityUpdate"); -diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -index 01a2016ac82807d28ffe407b7dbb74bdbcde503e..0016c3aa6a189d2bfc6433a15624ab6ccb90da12 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java -@@ -128,9 +128,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { - return (Brain) super.getBrain(); // Paper - decompile fix - } - -+ private int behaviorTick; - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("hoglinBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) - this.getBrain().tick((ServerLevel)this.level(), this); - this.level().getProfiler().pop(); - HoglinAi.updateActivity(this); -diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -index a9813da7f2b248f98f22e0ad2e7842915025ec12..24a17ef210f57c6812122d7ce1e9a0e8b46537a4 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java -@@ -300,9 +300,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento - return !this.cannotHunt; - } - -+ private int behaviorTick; - @Override - protected void customServerAiStep() { - this.level().getProfiler().push("piglinBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) - this.getBrain().tick((ServerLevel) this.level(), this); - this.level().getProfiler().pop(); - PiglinAi.updateActivity(this); -diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -index 937f81a859953498abe73bea560c86e6560e1c33..fdd34eae5ccbd43c29a2404e7b2bb15c1bf60376 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -+++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java -@@ -273,11 +273,13 @@ public class Warden extends Monster implements VibrationSystem { - - } - -+ private int behaviorTick = 0; - @Override - protected void customServerAiStep() { - ServerLevel worldserver = (ServerLevel) this.level(); - - worldserver.getProfiler().push("wardenBrain"); -+ if (this.behaviorTick++ % this.activatedPriority == 0) - this.getBrain().tick(worldserver, this); - this.level().getProfiler().pop(); - super.customServerAiStep(); -diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java -index 3eeff0cd8563939c656b5ded8470cab016903f25..1d33146c4a8d3c2c0df5ef04f373396181f35f3b 100644 ---- a/src/main/java/net/minecraft/world/entity/npc/Villager.java -+++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java -@@ -143,6 +143,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - return holder.is(PoiTypes.MEETING); - }); - -+ public long nextGolemPanic = -1; -+ - public Villager(EntityType entityType, Level world) { - this(entityType, world, VillagerType.PLAINS); - } -@@ -246,6 +248,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - } - // Spigot End - -+ -+ private int behaviorTick = 0; - @Override - @Deprecated // Paper - protected void customServerAiStep() { -@@ -255,7 +259,10 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - protected void customServerAiStep(final boolean inactive) { - // Paper end - this.level().getProfiler().push("villagerBrain"); -- if (!inactive) this.getBrain().tick((ServerLevel) this.level(), this); // Paper -+ //if (!inactive) this.getBrain().tick((ServerLevel) this.level(), this); // Paper -+ if (!inactive && this.behaviorTick++ % this.activatedPriority == 0) { -+ this.getBrain().tick((ServerLevel) this.level(), this); // Paper -+ } - this.level().getProfiler().pop(); - if (this.assignProfessionWhenSpawned) { - this.assignProfessionWhenSpawned = false; -diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index a27e400c5db9dddcaa307758ea7100893dd3819d..e3b3860df83ba5fca232c3b1f217f41c00ca073e 100644 ---- a/src/main/java/org/spigotmc/ActivationRange.java -+++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -39,6 +39,7 @@ import co.aikar.timings.MinecraftTimings; - import net.minecraft.world.entity.schedule.Activity; - import net.minecraft.world.level.Level; - import net.minecraft.world.phys.AABB; -+import net.minecraft.world.phys.Vec3; - - public class ActivationRange - { -@@ -245,6 +246,22 @@ public class ActivationRange - } - // Paper end - Configurable marker ticking - ActivationRange.activateEntity(entity, bbByType); // Folia - threaded regions -+ -+ if (FoldenorConfig.dearEnabled && entity.getType().dabEnabled) { -+ if (!entity.activatedPriorityReset) { -+ entity.activatedPriorityReset = true; -+ entity.activatedPriority = FoldenorConfig.maximumActivationPrio; -+ } -+ Vec3 playerVec = player.position(); -+ Vec3 entityVec = entity.position(); -+ double diffX = playerVec.x - entityVec.x, diffY = playerVec.y - entityVec.y, diffZ = playerVec.z - entityVec.z; -+ int squaredDistance = (int) (diffX * diffX + diffY * diffY + diffZ * diffZ); -+ entity.activatedPriority = squaredDistance > FoldenorConfig.startDistanceSquared ? -+ Math.max(1, Math.min(squaredDistance >> FoldenorConfig.activationDistanceMod, entity.activatedPriority)) : -+ 1; -+ } else { -+ entity.activatedPriority = 1; -+ } - } - // Paper end - } diff --git a/patches/server/0036-Different-crash-fixes.patch b/patches/server/0036-Different-crash-fixes.patch deleted file mode 100644 index 13ca49c..0000000 --- a/patches/server/0036-Different-crash-fixes.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Tue, 6 Feb 2024 14:38:01 +0400 -Subject: [PATCH] Different-crash-fixes - - -diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -index 5067fabb3a565d424248ce73212d9542d21bf06b..4260ce4f8d4550b8214503820271ffd4d093d7bb 100644 ---- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -+++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -143,19 +143,9 @@ public class FoldenorConfig { - maxProjectileLoadsPerProjectile = getInt("optimizations.max_projectile_loads_per_projectile", maxProjectileLoadsPerProjectile); - maxProjectileLoadsPerTick = getInt("optimizations.max_projectile_loads_per_tick", maxProjectileLoadsPerTick); - acquirePoiForStuckEntityInterval = getInt("optimizations.acquire_poi_for_stuck_entity_interval", acquirePoiForStuckEntityInterval); --<<<<<<< HEAD - enableSuffocationOptimization = getBoolean("optimizations.optimize_suffocation_check",enableSuffocationOptimization); - entityWakeUpDurationRatioStandardDeviation = getDouble("optimizations.entity_wakeup_duration_ratio_standard_deviation",entityWakeUpDurationRatioStandardDeviation); - loadChunksToActiveClimbingEntities = getBoolean("optimizations.load_chunks_to_active_climbing_entities",loadChunksToActiveClimbingEntities); --======= -- enableSuffocationOptimization = getBoolean("optimizations.optimize_suffocation_check", enableSuffocationOptimization); -- entityWakeUpDurationRatioStandardDeviation = getDouble("optimizations.entity_wakeup_duration_ratio_standard_deviation", entityWakeUpDurationRatioStandardDeviation); -- loadChunksToActiveClimbingEntities = getBoolean("optimizations.load_chunks_to_active_climbing_entities", loadChunksToActiveClimbingEntities); -- -- -- enableAsyncMobSpawning = getBoolean("optimizations.enable_async_mob_spawning", enableAsyncMobSpawning); -- RegionizedWorldData.initMobSpawningExecutor(); -->>>>>>> Pufferfish-Dynamic-Activation-of-Brain - } - - private static void networkSettings() { -diff --git a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java -index 21b6f90cf5bd7087d1a0f512289d971f2c3e1afa..a37e80e8bd014a32c9fcda494ad7c92ccf967862 100644 ---- a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java -+++ b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java -@@ -10,6 +10,8 @@ import com.mojang.brigadier.suggestion.SuggestionProvider; - import com.mojang.brigadier.suggestion.Suggestions; - import com.mojang.brigadier.suggestion.SuggestionsBuilder; - import com.mojang.brigadier.tree.LiteralCommandNode; -+ -+import java.util.Iterator; - import java.util.List; - import java.util.concurrent.CompletableFuture; - import java.util.function.Predicate; -@@ -76,8 +78,11 @@ public class BukkitCommandWrapper implements com.mojang.brigadier.Command it = results.iterator(); -+ while (it.hasNext()) { -+ String item = it.next(); -+ if (item != null) -+ builder.suggest(item); - } - - return builder.buildFuture(); diff --git a/patches/server/0016-Leaves-Protocol-Core.patch b/patches/wip/0016-Leaves-Protocol-Core.patch similarity index 100% rename from patches/server/0016-Leaves-Protocol-Core.patch rename to patches/wip/0016-Leaves-Protocol-Core.patch diff --git a/patches/server/0017-Leaves-Appleskin-Protocol.patch b/patches/wip/0017-Leaves-Appleskin-Protocol.patch similarity index 100% rename from patches/server/0017-Leaves-Appleskin-Protocol.patch rename to patches/wip/0017-Leaves-Appleskin-Protocol.patch diff --git a/patches/server/0019-Villagers-dispawn-fix.patch b/patches/wip/0019-Villagers-dispawn-fix.patch similarity index 100% rename from patches/server/0019-Villagers-dispawn-fix.patch rename to patches/wip/0019-Villagers-dispawn-fix.patch diff --git a/patches/wip/0040-New-mob-spawn-system.patch b/patches/wip/0040-New-mob-spawn-system.patch deleted file mode 100644 index b9afcb0..0000000 --- a/patches/wip/0040-New-mob-spawn-system.patch +++ /dev/null @@ -1,967 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Sun, 7 Jan 2024 15:33:08 +0400 -Subject: [PATCH] New-mob-spawn-system - - -diff --git a/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java b/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java -index d3b39d88a72ca25057fd8574d32f28db0d420818..44dde5583191ed06ab5d963af2294aaae770cf35 100644 ---- a/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java -+++ b/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java -@@ -164,10 +164,10 @@ public final class MobcapsCommand implements PaperSubcommand { - final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); - final ServerLevel level = serverPlayer.serverLevel(); - -- if (!level.paperConfig().entities.spawning.perPlayerMobSpawns) { -+ /*if (!level.paperConfig().entities.spawning.perPlayerMobSpawns) { - sender.sendMessage(Component.text("Use '/paper mobcaps' for worlds where per-player mob spawning is disabled.", NamedTextColor.RED)); - return; -- } -+ }*/ - - sender.sendMessage(Component.join(JoinConfiguration.noSeparators(), Component.text("Mobcaps for player: "), Component.text(player.getName(), NamedTextColor.GREEN))); - sender.sendMessage(createMobcapsComponent( -diff --git a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java -index f8a2a4494edccb25a4403a69581fe7920d9d0de3..f2c8d0e729d22d820565789428cebaf6dbb7a95d 100644 ---- a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java -+++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java -@@ -177,6 +177,7 @@ public class WorldConfiguration extends ConfigurationPart { - public boolean filterBadTileEntityNbtFromFallingBlocks = true; - public List filteredEntityTagNbtPaths = NbtPathSerializer.fromString(List.of("Pos", "Motion", "SleepingX", "SleepingY", "SleepingZ")); - public boolean disableMobSpawnerSpawnEggTransformation = false; -+ @Deprecated - public boolean perPlayerMobSpawns = true; - public boolean scanForLegacyEnderDragon = true; - @MergeMap -diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -index 5067fabb3a565d424248ce73212d9542d21bf06b..eb362fc15b780641ed3b4985a0441c10a1d051f7 100644 ---- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -+++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -43,6 +43,7 @@ public class FoldenorConfig { - - public static int acquirePoiForStuckEntityInterval = 60; - public static boolean enableSuffocationOptimization = true; -+ public static boolean enableNewMobSpawnSystem = true; - - public static RegionFileFormat regionFormatName = RegionFileFormat.ANVIL; - public static int regionFormatLinearCompressionLevel = 1; -@@ -114,6 +115,7 @@ public class FoldenorConfig { - } - - private static void worldSettings() { -+ enableNewMobSpawnSystem = getBoolean("world.enable-new-mob-spawn-system", enableNewMobSpawnSystem); - regionFormatName = RegionFileFormat.fromString(getString("world.region.format", regionFormatName.name())); - if (regionFormatName.equals(RegionFileFormat.INVALID)) { - log(Level.SEVERE, "Unknown region format in foldenor.yml: " + regionFormatName); -@@ -143,19 +145,9 @@ public class FoldenorConfig { - maxProjectileLoadsPerProjectile = getInt("optimizations.max_projectile_loads_per_projectile", maxProjectileLoadsPerProjectile); - maxProjectileLoadsPerTick = getInt("optimizations.max_projectile_loads_per_tick", maxProjectileLoadsPerTick); - acquirePoiForStuckEntityInterval = getInt("optimizations.acquire_poi_for_stuck_entity_interval", acquirePoiForStuckEntityInterval); --<<<<<<< HEAD - enableSuffocationOptimization = getBoolean("optimizations.optimize_suffocation_check",enableSuffocationOptimization); - entityWakeUpDurationRatioStandardDeviation = getDouble("optimizations.entity_wakeup_duration_ratio_standard_deviation",entityWakeUpDurationRatioStandardDeviation); - loadChunksToActiveClimbingEntities = getBoolean("optimizations.load_chunks_to_active_climbing_entities",loadChunksToActiveClimbingEntities); --======= -- enableSuffocationOptimization = getBoolean("optimizations.optimize_suffocation_check", enableSuffocationOptimization); -- entityWakeUpDurationRatioStandardDeviation = getDouble("optimizations.entity_wakeup_duration_ratio_standard_deviation", entityWakeUpDurationRatioStandardDeviation); -- loadChunksToActiveClimbingEntities = getBoolean("optimizations.load_chunks_to_active_climbing_entities", loadChunksToActiveClimbingEntities); -- -- -- enableAsyncMobSpawning = getBoolean("optimizations.enable_async_mob_spawning", enableAsyncMobSpawning); -- RegionizedWorldData.initMobSpawningExecutor(); -->>>>>>> Pufferfish-Dynamic-Activation-of-Brain - } - - private static void networkSettings() { -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 8082c189fc414b741bff9ca9b259b448e8adf0d2..de5d06a5cb458fd8bd31ca7e188280db00d11ee3 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -27,12 +27,7 @@ import net.minecraft.util.profiling.ProfilerFiller; - import net.minecraft.util.thread.BlockableEventLoop; - import net.minecraft.world.entity.Entity; - import net.minecraft.world.entity.ai.village.poi.PoiManager; --import net.minecraft.world.level.ChunkPos; --import net.minecraft.world.level.GameRules; --import net.minecraft.world.level.Level; --import net.minecraft.world.level.LightLayer; --import net.minecraft.world.level.LocalMobCapCalculator; --import net.minecraft.world.level.NaturalSpawner; -+import net.minecraft.world.level.*; - import net.minecraft.world.level.chunk.ChunkAccess; - import net.minecraft.world.level.chunk.ChunkGenerator; - import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -@@ -490,7 +485,7 @@ public class ServerChunkCache extends ChunkSource { - int naturalSpawnChunkCount = k; - NaturalSpawner.SpawnState spawnercreature_d; // moved down - profiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.MOB_SPAWN_ENTITY_COUNT); try { // Folia - profiler -- if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled -+ /*if ((this.spawnFriendlies || this.spawnEnemies) && this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { // don't count mobs when animals and monsters are disabled - // re-set mob counts - for (ServerPlayer player : this.level.getLocalPlayers()) { // Folia - region threading - // Paper start - per player mob spawning backoff -@@ -506,9 +501,9 @@ public class ServerChunkCache extends ChunkSource { - // Paper end - per player mob spawning backoff - } - spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, regionizedWorldData.getLoadedEntities(), this::getFullChunk, null, true); // Folia - region threading - note: function only cares about loaded entities, doesn't need all -- } else { -- spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, regionizedWorldData.getLoadedEntities(), this::getFullChunk, !this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? new LocalMobCapCalculator(this.chunkMap) : null, false); // Folia - region threading - note: function only cares about loaded entities, doesn't need all -- } -+ } else {*/ -+ spawnercreature_d = NaturalSpawner.createState(naturalSpawnChunkCount, regionizedWorldData.getLoadedEntities(), this::getFullChunk, /*!this.level.paperConfig().entities.spawning.perPlayerMobSpawns ? */new LocalMobCapCalculator(this.chunkMap)/* : null*/, false); // Folia - region threading - note: function only cares about loaded entities, doesn't need all -+ //} - } finally { profiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.MOB_SPAWN_ENTITY_COUNT); } // Folia - profiler - // Paper end - this.level.timings.countNaturalMobs.stopTiming(); // Paper - timings -@@ -560,9 +555,9 @@ public class ServerChunkCache extends ChunkSource { - // Paper start - optimise chunk tick iteration - io.papermc.paper.util.player.NearbyPlayers nearbyPlayers = this.chunkMap.getNearbyPlayers(); // Paper - optimise chunk tick iteration - Iterator chunkIterator; -- if (this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { -+ /*if (this.level.paperConfig().entities.spawning.perPlayerMobSpawns) { - chunkIterator = regionizedWorldData.getTickingChunks().iterator(); // Folia - region threading -- } else { -+ } else {*/ - chunkIterator = regionizedWorldData.getTickingChunks().unsafeIterator(); // Folia - region threading - List shuffled = Lists.newArrayListWithCapacity(regionizedWorldData.getTickingChunks().size()); // Folia - region threading - while (chunkIterator.hasNext()) { -@@ -570,7 +565,7 @@ public class ServerChunkCache extends ChunkSource { - } - Util.shuffle(shuffled, this.level.random); - chunkIterator = shuffled.iterator(); -- } -+ //} - try { - // Paper end - optimise chunk tick iteration - long spawnChunkCount = 0L; // Folia - profiler -diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -index 554844feaf8cb0e43461e1c142bb766c6116fce9..76444f581abb95bcab5f68176915ac55cb2e7bb7 100644 ---- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java -+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java -@@ -10,6 +10,8 @@ import java.util.Optional; - import java.util.function.Consumer; - import java.util.stream.Stream; - import javax.annotation.Nullable; -+ -+import net.edenor.foldenor.config.FoldenorConfig; - import net.minecraft.core.BlockPos; - import net.minecraft.core.Direction; - import net.minecraft.core.Holder; -@@ -48,6 +50,7 @@ import net.minecraft.world.level.material.FluidState; - import net.minecraft.world.level.pathfinder.PathComputationType; - import net.minecraft.world.level.storage.LevelData; - import net.minecraft.world.phys.Vec3; -+import org.checkerframework.checker.units.qual.N; - import org.slf4j.Logger; - import org.bukkit.craftbukkit.util.CraftSpawnCategory; - import org.bukkit.entity.SpawnCategory; -@@ -70,11 +73,13 @@ public final class NaturalSpawner { - private NaturalSpawner() {} - - public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.createState(spawningChunkCount, entities, chunkSource, densityCapper); - // Paper start - add countMobs parameter - return createState(spawningChunkCount, entities, chunkSource, densityCapper, false); - } - - public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper, boolean countMobs) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.createState(spawningChunkCount, entities, chunkSource, densityCapper, countMobs); - // Paper end - PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator(); - Object2IntOpenHashMap object2intopenhashmap = new Object2IntOpenHashMap(); -@@ -128,10 +133,16 @@ public final class NaturalSpawner { - } - - static Biome getRoughBiome(BlockPos pos, ChunkAccess chunk) { -- return (Biome) chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value(); -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.getRoughBiome(pos, chunk); -+ return chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value(); - } - - public static void spawnForChunk(ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnState info, boolean spawnAnimals, boolean spawnMonsters, boolean rareSpawn) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) { -+ NewNaturalSpawner.spawnForChunk(world, chunk, info, spawnAnimals, spawnMonsters, rareSpawn); -+ return; -+ } -+ - world.getProfiler().push("spawner"); - world.timings.mobSpawn.startTiming(); // Spigot - MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES; -@@ -159,7 +170,7 @@ public final class NaturalSpawner { - int k1 = limit * info.getSpawnableChunkCount() / NaturalSpawner.MAGIC_NUMBER; - int difference = k1 - currEntityCount; - -- if (world.paperConfig().entities.spawning.perPlayerMobSpawns) { -+ /*if (world.paperConfig().entities.spawning.perPlayerMobSpawns) { - int minDiff = Integer.MAX_VALUE; - final com.destroystokyo.paper.util.maplist.ReferenceList inRange = - world.chunkSource.chunkMap.getNearbyPlayers().getPlayers(chunk.getPos(), io.papermc.paper.util.player.NearbyPlayers.NearbyMapType.TICK_VIEW_DISTANCE); -@@ -170,7 +181,7 @@ public final class NaturalSpawner { - } - } - difference = (minDiff == Integer.MAX_VALUE) ? 0 : minDiff; -- } -+ }*/ - if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && difference > 0) { - // Paper end - // CraftBukkit end -@@ -180,7 +191,7 @@ public final class NaturalSpawner { - Objects.requireNonNull(info); - // Paper start - int spawnCount = NaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn, -- difference, world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : null); -+ difference, /*world.paperConfig().entities.spawning.perPlayerMobSpawns ? world.getChunkSource().chunkMap::updatePlayerMobTypeMap : */null); - info.mobCategoryCounts.mergeInt(enumcreaturetype, spawnCount, Integer::sum); - // Paper end - } -@@ -192,6 +203,7 @@ public final class NaturalSpawner { - - // Paper start - public static int globalLimitForCategory(final ServerLevel level, final MobCategory category, final int spawnableChunkCount) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.globalLimitForCategory(level, category, spawnableChunkCount); - final int categoryLimit = level.getWorld().getSpawnLimitUnsafe(CraftSpawnCategory.toBukkit(category)); - if (categoryLimit < 1) { - return categoryLimit; -@@ -201,10 +213,15 @@ public final class NaturalSpawner { - // Paper end - - public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) { -+ NewNaturalSpawner.spawnCategoryForChunk(group, world, chunk, checker, runner); -+ return; -+ } - // Paper start - add parameters and int ret type - spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null); - } - public static int spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer trackEntity) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.spawnCategoryForChunk(group, world, chunk, checker, runner, maxSpawns/*, trackEntity*/); - // Paper end - add parameters and int ret type - BlockPos blockposition = NaturalSpawner.getRandomPosWithin(world, chunk); - -@@ -216,6 +233,10 @@ public final class NaturalSpawner { - - @VisibleForDebug - public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, BlockPos pos) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) { -+ NewNaturalSpawner.spawnCategoryForPosition(group, world, pos); -+ return; -+ } - NaturalSpawner.spawnCategoryForPosition(group, world, world.getChunk(pos), pos, (entitytypes, blockposition1, ichunkaccess) -> { - return true; - }, (entityinsentient, ichunkaccess) -> { -@@ -224,10 +245,15 @@ public final class NaturalSpawner { - - // Paper start - add maxSpawns parameter and return spawned mobs - public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) { -+ NewNaturalSpawner.spawnCategoryForPosition(group, world, chunk, pos, checker, runner); -+ return; -+ } - spawnCategoryForPosition(group, world,chunk, pos, checker, runner, Integer.MAX_VALUE, null); - } - public static int spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns, Consumer trackEntity) { -- // Paper end - add maxSpawns parameter and return spawned mobs -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.spawnCategoryForPosition(group, world, chunk, pos, checker, runner, maxSpawns/*, trackEntity*/); -+ // Paper end - add maxSpawns parameter and return spawned mobs - StructureManager structuremanager = world.structureManager(); - ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator(); - int i = pos.getY(); -@@ -337,17 +363,19 @@ public final class NaturalSpawner { - } - - private static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.isRightDistanceToPlayerAndSpawnPoint(world, chunk, pos, squaredDistance); - return squaredDistance <= 576.0D ? false : (world.getSharedSpawnPos().closerToCenterThan(new Vec3((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D), 24.0D) ? false : Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isNaturalSpawningAllowed((BlockPos) pos)); - } - - // Paper start -- private enum PreSpawnStatus { -+ public enum PreSpawnStatus { - FAIL, - SUCCESS, - CANCELLED, - ABORT - } - private static PreSpawnStatus isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.isValidSpawnPostitionForType(world, group,structureAccessor,chunkGenerator,spawnEntry, pos,squaredDistance); - // Paper end - EntityType entitytypes = spawnEntry.type; - -@@ -383,6 +411,7 @@ public final class NaturalSpawner { - - @Nullable - private static Mob getMobForSpawn(ServerLevel world, EntityType type) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.getMobForSpawn(world, type); - try { - Entity entity = type.create(world); - -@@ -402,24 +431,29 @@ public final class NaturalSpawner { - } - - private static boolean isValidPositionForMob(ServerLevel world, Mob entity, double squaredDistance) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.isValidPositionForMob(world, entity, squaredDistance); - return squaredDistance > (double) (entity.getType().getCategory().getDespawnDistance() * entity.getType().getCategory().getDespawnDistance()) && entity.removeWhenFarAway(squaredDistance) ? false : entity.checkSpawnRules(world, MobSpawnType.NATURAL) && entity.checkSpawnObstruction(world); - } - - private static Optional getRandomSpawnMobAt(ServerLevel world, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobCategory spawnGroup, RandomSource random, BlockPos pos) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.getRandomSpawnMobAt(world, structureAccessor, chunkGenerator, spawnGroup, random, pos); - Holder holder = world.getBiome(pos); - - return spawnGroup == MobCategory.WATER_AMBIENT && holder.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && random.nextFloat() < 0.98F ? Optional.empty() : NaturalSpawner.mobsAt(world, structureAccessor, chunkGenerator, spawnGroup, pos, holder).getRandom(random); - } - - private static boolean canSpawnMobAt(ServerLevel world, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobCategory spawnGroup, MobSpawnSettings.SpawnerData spawnEntry, BlockPos pos) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) NewNaturalSpawner.canSpawnMobAt(world, structureAccessor, chunkGenerator, spawnGroup, spawnEntry, pos); - return NaturalSpawner.mobsAt(world, structureAccessor, chunkGenerator, spawnGroup, pos, (Holder) null).unwrap().contains(spawnEntry); - } - - private static WeightedRandomList mobsAt(ServerLevel world, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobCategory spawnGroup, BlockPos pos, @Nullable Holder biomeEntry) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.mobsAt(world, structureAccessor, chunkGenerator, spawnGroup, pos, biomeEntry); - return NaturalSpawner.isInNetherFortressBounds(pos, world, spawnGroup, structureAccessor) ? NetherFortressStructure.FORTRESS_ENEMIES : chunkGenerator.getMobsAt(biomeEntry != null ? biomeEntry : world.getBiome(pos), structureAccessor, spawnGroup, pos); - } - - public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel world, MobCategory spawnGroup, StructureManager structureAccessor) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.isInNetherFortressBounds(pos, world, spawnGroup, structureAccessor); - if (spawnGroup == MobCategory.MONSTER && world.getBlockState(pos.below()).is(Blocks.NETHER_BRICKS)) { - Structure structure = (Structure) structureAccessor.registryAccess().registryOrThrow(Registries.STRUCTURE).get(BuiltinStructures.FORTRESS); - -@@ -430,6 +464,7 @@ public final class NaturalSpawner { - } - - private static BlockPos getRandomPosWithin(ServerLevel world, LevelChunk chunk) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.getRandomPosWithin(world, chunk); - ChunkPos chunkcoordintpair = chunk.getPos(); - int i = chunkcoordintpair.getMinBlockX() + world.getThreadUnsafeRandom().nextInt(16); - int j = chunkcoordintpair.getMinBlockZ() + world.getThreadUnsafeRandom().nextInt(16); -@@ -440,10 +475,12 @@ public final class NaturalSpawner { - } - - public static boolean isValidEmptySpawnBlock(BlockGetter blockView, BlockPos pos, BlockState state, FluidState fluidState, EntityType entityType) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.isValidEmptySpawnBlock(blockView, pos, state, fluidState, entityType); - return state.isCollisionShapeFullBlock(blockView, pos) ? false : (state.isSignalSource() ? false : (!fluidState.isEmpty() ? false : (state.is(BlockTags.PREVENT_MOB_SPAWNING_INSIDE) ? false : !entityType.isBlockDangerous(state)))); - } - - public static boolean isSpawnPositionOk(SpawnPlacements.Type location, LevelReader world, BlockPos pos, @Nullable EntityType entityType) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.isSpawnPositionOk(location, world, pos, entityType); - if (location == SpawnPlacements.Type.NO_RESTRICTIONS) { - return true; - } else if (entityType != null && world.getWorldBorder().isWithinBounds(pos)) { -@@ -469,6 +506,10 @@ public final class NaturalSpawner { - } - - public static void spawnMobsForChunkGeneration(ServerLevelAccessor world, Holder biomeEntry, ChunkPos chunkPos, RandomSource random) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) { -+ NewNaturalSpawner.spawnMobsForChunkGeneration(world,biomeEntry,chunkPos,random); -+ return; -+ } - MobSpawnSettings biomesettingsmobs = ((Biome) biomeEntry.value()).getMobSettings(); - WeightedRandomList weightedrandomlist = biomesettingsmobs.getMobs(MobCategory.CREATURE); - -@@ -543,6 +584,7 @@ public final class NaturalSpawner { - } - - private static BlockPos getTopNonCollidingPos(LevelReader world, EntityType entityType, int x, int z) { -+ if (FoldenorConfig.enableNewMobSpawnSystem) return NewNaturalSpawner.getTopNonCollidingPos(world, entityType, x, z); - int k = world.getHeight(SpawnPlacements.getHeightmapType(entityType), x, z); - BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(x, k, z); - -@@ -573,7 +615,7 @@ public final class NaturalSpawner { - void query(long pos, Consumer chunkConsumer); - } - -- public static class SpawnState { -+ public static class SpawnState{ - - private final int spawnableChunkCount; - private final Object2IntOpenHashMap mobCategoryCounts; -@@ -594,25 +636,22 @@ public final class NaturalSpawner { - this.unmodifiableMobCategoryCounts = Object2IntMaps.unmodifiable(groupToCount); - } - -- private boolean canSpawn(EntityType type, BlockPos pos, ChunkAccess chunk) { -+ public boolean canSpawn(EntityType type, BlockPos pos, ChunkAccess chunk) { - this.lastCheckedPos = pos; - this.lastCheckedType = type; -- MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = NaturalSpawner.getRoughBiome(pos, chunk).getMobSettings().getMobSpawnCost(type); -+ MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = NewNaturalSpawner.getRoughBiome(pos, chunk).getMobSettings().getMobSpawnCost(type); - - if (biomesettingsmobs_b == null) { - this.lastCharge = 0.0D; - return true; - } else { -- double d0 = biomesettingsmobs_b.charge(); -- -- this.lastCharge = d0; -- double d1 = this.spawnPotential.getPotentialEnergyChange(pos, d0); -+ this.lastCharge = biomesettingsmobs_b.charge(); - -- return d1 <= biomesettingsmobs_b.energyBudget(); -+ return this.spawnPotential.getPotentialEnergyChange(pos, this.lastCharge) <= biomesettingsmobs_b.energyBudget(); - } - } - -- private void afterSpawn(Mob entity, ChunkAccess chunk) { -+ public void afterSpawn(Mob entity, ChunkAccess chunk) { - EntityType entitytypes = entity.getType(); - BlockPos blockposition = entity.blockPosition(); - double d0; -@@ -620,13 +659,9 @@ public final class NaturalSpawner { - if (blockposition.equals(this.lastCheckedPos) && entitytypes == this.lastCheckedType) { - d0 = this.lastCharge; - } else { -- MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = NaturalSpawner.getRoughBiome(blockposition, chunk).getMobSettings().getMobSpawnCost(entitytypes); -+ MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = NewNaturalSpawner.getRoughBiome(blockposition, chunk).getMobSettings().getMobSpawnCost(entitytypes); - -- if (biomesettingsmobs_b != null) { -- d0 = biomesettingsmobs_b.charge(); -- } else { -- d0 = 0.0D; -- } -+ d0 = biomesettingsmobs_b != null ? biomesettingsmobs_b.charge() : 0.0D; - } - - this.spawnPotential.addCharge(blockposition, d0); -@@ -640,17 +675,21 @@ public final class NaturalSpawner { - return this.spawnableChunkCount; - } - -+ public Object2IntOpenHashMap getModifiableMobCategoryCounts(){ -+ return this.mobCategoryCounts; -+ } -+ - public Object2IntMap getMobCategoryCounts() { - return this.unmodifiableMobCategoryCounts; - } - - // CraftBukkit start - boolean canSpawnForCategory(MobCategory enumcreaturetype, ChunkPos chunkcoordintpair, int limit) { -- int i = limit * this.spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER; -+ int i = limit * this.spawnableChunkCount / NewNaturalSpawner.MAGIC_NUMBER; - // CraftBukkit end - - if (this.localMobCapCalculator == null) return this.mobCategoryCounts.getInt(enumcreaturetype) < i; // Paper -- return this.mobCategoryCounts.getInt(enumcreaturetype) >= i ? false : this.localMobCapCalculator.canSpawn(enumcreaturetype, chunkcoordintpair); -+ return this.mobCategoryCounts.getInt(enumcreaturetype) < i && this.localMobCapCalculator.canSpawn(enumcreaturetype, chunkcoordintpair); - } - } - -diff --git a/src/main/java/net/minecraft/world/level/NewNaturalSpawner.java b/src/main/java/net/minecraft/world/level/NewNaturalSpawner.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d7e1b6616d936f3a6717b257101989f83d3ed87f ---- /dev/null -+++ b/src/main/java/net/minecraft/world/level/NewNaturalSpawner.java -@@ -0,0 +1,514 @@ -+package net.minecraft.world.level; -+ -+import com.mojang.logging.LogUtils; -+import io.papermc.paper.threadedregions.RegionizedServer; -+import io.papermc.paper.util.TickThread; -+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -+import net.minecraft.core.BlockPos; -+import net.minecraft.core.Direction; -+import net.minecraft.core.Holder; -+import net.minecraft.core.QuartPos; -+import net.minecraft.core.registries.BuiltInRegistries; -+import net.minecraft.core.registries.Registries; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.tags.BiomeTags; -+import net.minecraft.tags.BlockTags; -+import net.minecraft.tags.FluidTags; -+import net.minecraft.util.Mth; -+import net.minecraft.util.RandomSource; -+import net.minecraft.util.VisibleForDebug; -+import net.minecraft.util.random.WeightedRandomList; -+import net.minecraft.world.entity.*; -+import net.minecraft.world.entity.player.Player; -+import net.minecraft.world.level.NaturalSpawner.PreSpawnStatus; -+import net.minecraft.world.level.biome.Biome; -+import net.minecraft.world.level.biome.MobSpawnSettings; -+import net.minecraft.world.level.block.Blocks; -+import net.minecraft.world.level.block.state.BlockState; -+import net.minecraft.world.level.chunk.ChunkAccess; -+import net.minecraft.world.level.chunk.ChunkGenerator; -+import net.minecraft.world.level.chunk.LevelChunk; -+import net.minecraft.world.level.levelgen.Heightmap; -+import net.minecraft.world.level.levelgen.structure.BuiltinStructures; -+import net.minecraft.world.level.levelgen.structure.Structure; -+import net.minecraft.world.level.levelgen.structure.structures.NetherFortressStructure; -+import net.minecraft.world.level.material.FluidState; -+import net.minecraft.world.level.pathfinder.PathComputationType; -+import net.minecraft.world.phys.Vec3; -+import org.bukkit.craftbukkit.util.CraftSpawnCategory; -+import org.bukkit.entity.SpawnCategory; -+import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; -+import org.slf4j.Logger; -+ -+import javax.annotation.Nullable; -+import java.util.Objects; -+import java.util.Optional; -+import java.util.stream.Stream; -+// CraftBukkit end -+ -+public final class NewNaturalSpawner { -+ -+ private static final Logger LOGGER = LogUtils.getLogger(); -+ static final int MAGIC_NUMBER = (int) Math.pow(17.0D, 2.0D); -+ public static final MobCategory[] SPAWNING_CATEGORIES = Stream.of(MobCategory.values()).filter((enumcreaturetype) -> enumcreaturetype != MobCategory.MISC).toArray(MobCategory[]::new); -+ -+ private NewNaturalSpawner() {} -+ -+ public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper) { -+ // Paper start - add countMobs parameter -+ return createState(spawningChunkCount, entities, chunkSource, densityCapper, false); -+ } -+ -+ public static NaturalSpawner.SpawnState createState(int spawningChunkCount, Iterable entities, NaturalSpawner.ChunkGetter chunkSource, LocalMobCapCalculator densityCapper, boolean countMobs) { -+ // Paper end -+ PotentialCalculator spawnercreatureprobabilities = new PotentialCalculator(); -+ Object2IntOpenHashMap object2intopenhashmap = new Object2IntOpenHashMap<>(); -+ -+ for (Entity entity : entities) { -+ if (entity instanceof Mob entityinsentient) { -+ if (entityinsentient.isPersistenceRequired() || entityinsentient.requiresCustomPersistence()) { -+ continue; -+ } -+ } -+ -+ MobCategory enumcreaturetype = entity.getType().getCategory(); -+ -+ if (enumcreaturetype != MobCategory.MISC) { -+ // Paper start - Only count natural spawns -+ if (!entity.level().paperConfig().entities.spawning.countAllMobsForSpawning && -+ !(entity.spawnReason == SpawnReason.NATURAL || -+ entity.spawnReason == SpawnReason.CHUNK_GEN)) { -+ continue; -+ } -+ // Paper end -+ BlockPos blockposition = entity.blockPosition(); -+ -+ chunkSource.query(ChunkPos.asLong(blockposition), (chunk) -> { -+ MobSpawnSettings.MobSpawnCost biomesettingsmobs_b = NewNaturalSpawner.getRoughBiome(blockposition, chunk).getMobSettings().getMobSpawnCost(entity.getType()); -+ -+ if (biomesettingsmobs_b != null) { -+ spawnercreatureprobabilities.addCharge(entity.blockPosition(), biomesettingsmobs_b.charge()); -+ } -+ -+ if (densityCapper != null && entity instanceof Mob) { // Paper -+ densityCapper.addMob(chunk.getPos(), enumcreaturetype); -+ } -+ -+ object2intopenhashmap.addTo(enumcreaturetype, 1); -+ // Paper start -+ if (countMobs) { -+ if (!TickThread.isTickThread()) { -+ RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(chunk.level, chunk.locX, chunk.locZ, () -> { -+ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity); -+ }); -+ return; -+ } -+ chunk.level.getChunkSource().chunkMap.updatePlayerMobTypeMap(entity); -+ } -+ // Paper end -+ }); -+ } -+ } -+ -+ return new NaturalSpawner.SpawnState(spawningChunkCount, object2intopenhashmap, spawnercreatureprobabilities, densityCapper); -+ } -+ -+ static Biome getRoughBiome(BlockPos pos, ChunkAccess chunk) { -+ return chunk.getNoiseBiome(QuartPos.fromBlock(pos.getX()), QuartPos.fromBlock(pos.getY()), QuartPos.fromBlock(pos.getZ())).value(); -+ } -+ -+ public static void spawnForChunk(ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnState info, boolean spawnAnimals, boolean spawnMonsters, boolean rareSpawn) { -+ for (MobCategory enumcreaturetype : NewNaturalSpawner.SPAWNING_CATEGORIES) { -+ // CraftBukkit start - Use per-world spawn limits -+ boolean spawnThisTick = true; -+ int limit = enumcreaturetype.getMaxInstancesPerChunk(); -+ SpawnCategory spawnCategory = CraftSpawnCategory.toBukkit(enumcreaturetype); -+ if (CraftSpawnCategory.isValidForLimits(spawnCategory)) { -+ spawnThisTick = world.ticksPerSpawnCategory.getLong(spawnCategory) != 0 && world.getRedstoneGameTime() % world.ticksPerSpawnCategory.getLong(spawnCategory) == 0; // Folia - region threading -+ limit = world.getWorld().getSpawnLimit(spawnCategory); -+ } -+ -+ if (!spawnThisTick || limit == 0) { -+ continue; -+ } -+ -+ // Paper start - only allow spawns upto the limit per chunk and update count afterwards -+ int currEntityCount = info.getModifiableMobCategoryCounts().getInt(enumcreaturetype); -+ int k1 = limit * info.getSpawnableChunkCount() / NewNaturalSpawner.MAGIC_NUMBER; -+ int difference = k1 - currEntityCount; -+ -+ if ((spawnAnimals || !enumcreaturetype.isFriendly()) && (spawnMonsters || enumcreaturetype.isFriendly()) && (rareSpawn || !enumcreaturetype.isPersistent()) && difference > 0) { -+ // Paper end -+ // CraftBukkit end -+ Objects.requireNonNull(info); -+ NaturalSpawner.SpawnPredicate spawnercreature_c = info::canSpawn; -+ -+ Objects.requireNonNull(info); -+ // Paper start -+ int spawnCount = NewNaturalSpawner.spawnCategoryForChunk(enumcreaturetype, world, chunk, spawnercreature_c, info::afterSpawn, -+ difference); -+ info.getModifiableMobCategoryCounts().mergeInt(enumcreaturetype, spawnCount, Integer::sum); -+ // Paper end -+ } -+ } -+ } -+ -+ // Paper start -+ public static int globalLimitForCategory(final ServerLevel level, final MobCategory category, final int spawnableChunkCount) { -+ final int categoryLimit = level.getWorld().getSpawnLimitUnsafe(CraftSpawnCategory.toBukkit(category)); -+ if (categoryLimit < 1) { -+ return categoryLimit; -+ } -+ return categoryLimit * spawnableChunkCount / NewNaturalSpawner.MAGIC_NUMBER; -+ } -+ // Paper end -+ -+ public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) { -+ // Paper start - add parameters and int ret type -+ spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE); -+ } -+ public static int spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns) { -+ // Paper end - add parameters and int ret type -+ BlockPos blockposition = NewNaturalSpawner.getRandomPosWithin(world, chunk); -+ -+ if (blockposition.getY() >= world.getMinBuildHeight() + 1) { -+ return NewNaturalSpawner.spawnCategoryForPosition(group, world, chunk, blockposition, checker, runner, maxSpawns); // Paper -+ } -+ return 0; // Paper -+ } -+ -+ @VisibleForDebug -+ public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, BlockPos pos) { -+ NewNaturalSpawner.spawnCategoryForPosition(group, world, world.getChunk(pos), pos, (entitytypes, blockposition1, ichunkaccess) -> true, (entityinsentient, ichunkaccess) -> {}); -+ } -+ -+ // Paper start - add maxSpawns parameter and return spawned mobs -+ public static void spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) { -+ spawnCategoryForPosition(group, world,chunk, pos, checker, runner, Integer.MAX_VALUE); -+ } -+ public static int spawnCategoryForPosition(MobCategory group, ServerLevel world, ChunkAccess chunk, BlockPos pos, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner, int maxSpawns) { -+ // Paper end - add maxSpawns parameter and return spawned mobs -+ StructureManager structuremanager = world.structureManager(); -+ ChunkGenerator chunkgenerator = world.getChunkSource().getGenerator(); -+ int i = pos.getY(); -+ BlockState iblockdata = world.getBlockStateIfLoadedAndInBounds(pos); // Paper - don't load chunks for mob spawn -+ int j = 0; // Paper - moved up -+ -+ if (iblockdata != null && !iblockdata.isRedstoneConductor(chunk, pos)) { // Paper - don't load chunks for mob spawn -+ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(); -+ //int j = 0; // Paper - moved up -+ int k = 0; -+ -+ while (k < 3) { -+ int l = pos.getX(); -+ int i1 = pos.getZ(); -+ MobSpawnSettings.SpawnerData biomesettingsmobs_c = null; -+ SpawnGroupData groupdataentity = null; -+ int j1 = Mth.ceil(world.random.nextFloat() * 4.0F); -+ int k1 = 0; -+ int l1 = 0; -+ -+ while (true) { -+ if (l1 < j1) { -+ label53: -+ { -+ l += world.random.nextInt(6) - world.random.nextInt(6); -+ i1 += world.random.nextInt(6) - world.random.nextInt(6); -+ blockposition_mutableblockposition.set(l, i, i1); -+ double d0 = (double) l + 0.5D; -+ double d1 = (double) i1 + 0.5D; -+ Player entityhuman = world.getNearestPlayer(d0, i, d1, -1.0D, false); -+ -+ if (entityhuman != null) { -+ double d2 = entityhuman.distanceToSqr(d0, i, d1); -+ -+ if (world.isLoadedAndInBounds(blockposition_mutableblockposition) && NewNaturalSpawner.isRightDistanceToPlayerAndSpawnPoint(world, chunk, blockposition_mutableblockposition, d2)) { // Paper - don't load chunks for mob spawn -+ if (biomesettingsmobs_c == null) { -+ Optional optional = NewNaturalSpawner.getRandomSpawnMobAt(world, structuremanager, chunkgenerator, group, world.random, blockposition_mutableblockposition); -+ -+ if (optional.isEmpty()) { -+ break label53; -+ } -+ -+ biomesettingsmobs_c = optional.get(); -+ j1 = biomesettingsmobs_c.minCount + world.random.nextInt(1 + biomesettingsmobs_c.maxCount - biomesettingsmobs_c.minCount); -+ } -+ -+ // Paper start -+ PreSpawnStatus doSpawning = isValidSpawnPostitionForType(world, group, structuremanager, chunkgenerator, biomesettingsmobs_c, blockposition_mutableblockposition, d2); -+ // Paper start - mob count backoff -+ if (doSpawning == PreSpawnStatus.ABORT || doSpawning == PreSpawnStatus.CANCELLED) { -+ world.getChunkSource().chunkMap.updateFailurePlayerMobTypeMap(blockposition_mutableblockposition.getX() >> 4, blockposition_mutableblockposition.getZ() >> 4, group); -+ } -+ // Paper end - mob count backoff -+ if (doSpawning == PreSpawnStatus.ABORT) { -+ return j; // Paper -+ } -+ if (doSpawning == PreSpawnStatus.SUCCESS && checker.test(biomesettingsmobs_c.type, blockposition_mutableblockposition, chunk)) { -+ // Paper end -+ Mob entityinsentient = NewNaturalSpawner.getMobForSpawn(world, biomesettingsmobs_c.type); -+ -+ if (entityinsentient == null) { -+ return j; // Paper -+ } -+ -+ entityinsentient.moveTo(d0, i, d1, world.random.nextFloat() * 360.0F, 0.0F); -+ if (NewNaturalSpawner.isValidPositionForMob(world, entityinsentient, d2)) { -+ groupdataentity = entityinsentient.finalizeSpawn(world, world.getCurrentDifficultyAt(entityinsentient.blockPosition()), MobSpawnType.NATURAL, groupdataentity, null); -+ // CraftBukkit start -+ // SPIGOT-7045: Give ocelot babies back their special spawn reason. Note: This is the only modification required as ocelots count as monsters which means they only spawn during normal chunk ticking and do not spawn during chunk generation as starter mobs. -+ world.addFreshEntityWithPassengers(entityinsentient, (entityinsentient instanceof net.minecraft.world.entity.animal.Ocelot && !((org.bukkit.entity.Ageable) entityinsentient.getBukkitEntity()).isAdult()) ? SpawnReason.OCELOT_BABY : SpawnReason.NATURAL); -+ if (!entityinsentient.isRemoved()) { -+ ++j; -+ ++k1; -+ runner.run(entityinsentient, chunk); -+ } -+ // CraftBukkit end -+ if (j >= entityinsentient.getMaxSpawnClusterSize() || j >= maxSpawns) { // Paper -+ return j; // Paper -+ } -+ -+ if (entityinsentient.isMaxGroupSizeReached(k1)) { -+ break label53; -+ } -+ } -+ } -+ } -+ } -+ -+ ++l1; -+ continue; -+ } -+ } -+ -+ ++k; -+ break; -+ } -+ } -+ -+ } -+ return j; // Paper -+ } -+ -+ public static boolean isRightDistanceToPlayerAndSpawnPoint(ServerLevel world, ChunkAccess chunk, BlockPos.MutableBlockPos pos, double squaredDistance) { -+ return !(squaredDistance <= 576.0D) && (!world.getSharedSpawnPos().closerToCenterThan(new Vec3((double) pos.getX() + 0.5D, pos.getY(), (double) pos.getZ() + 0.5D), 24.0D) && (Objects.equals(new ChunkPos(pos), chunk.getPos()) || world.isNaturalSpawningAllowed(pos))); -+ } -+ -+ public static PreSpawnStatus isValidSpawnPostitionForType(ServerLevel world, MobCategory group, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobSpawnSettings.SpawnerData spawnEntry, BlockPos.MutableBlockPos pos, double squaredDistance) { -+ // Paper end -+ EntityType entitytypes = spawnEntry.type; -+ -+ // Paper start -+ com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event; -+ org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(EntityType.getKey(entitytypes).getPath()); -+ if (type != null) { -+ event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent( -+ io.papermc.paper.util.MCUtil.toLocation(world, pos), -+ type, SpawnReason.NATURAL -+ ); -+ if (!event.callEvent()) { -+ if (event.shouldAbortSpawn()) { -+ return PreSpawnStatus.ABORT; // Paper -+ } -+ return PreSpawnStatus.CANCELLED; // Paper -+ } -+ } -+ // Paper end -+ if (entitytypes.getCategory() == MobCategory.MISC) { -+ return PreSpawnStatus.FAIL; // Paper -+ } else if (!entitytypes.canSpawnFarFromPlayer() && squaredDistance > (double) (entitytypes.getCategory().getDespawnDistance() * entitytypes.getCategory().getDespawnDistance())) { -+ return PreSpawnStatus.FAIL; // Paper -+ } else if (entitytypes.canSummon() && NewNaturalSpawner.canSpawnMobAt(world, structureAccessor, chunkGenerator, group, spawnEntry, pos)) { -+ SpawnPlacements.Type entitypositiontypes_surface = SpawnPlacements.getPlacementType(entitytypes); -+ -+ boolean isValid = NewNaturalSpawner.isSpawnPositionOk(entitypositiontypes_surface, world, pos, entitytypes) && (SpawnPlacements.checkSpawnRules(entitytypes, world, MobSpawnType.NATURAL, pos, world.random) && world.noCollision(entitytypes.getAABB((double) pos.getX() + 0.5D, pos.getY(), (double) pos.getZ() + 0.5D))); // Paper -+ return isValid ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL; // Paper -+ } else { -+ return PreSpawnStatus.FAIL; // Paper -+ } -+ } -+ -+ @Nullable -+ public static Mob getMobForSpawn(ServerLevel world, EntityType type) { -+ try { -+ Entity entity = type.create(world); -+ -+ if (entity instanceof Mob) { -+ return (Mob) entity; -+ } -+ -+ NewNaturalSpawner.LOGGER.warn("Can't spawn entity of type: {}", BuiltInRegistries.ENTITY_TYPE.getKey(type)); -+ } catch (Exception exception) { -+ NewNaturalSpawner.LOGGER.warn("Failed to create mob", exception); -+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper -+ } -+ -+ return null; -+ } -+ -+ public static boolean isValidPositionForMob(ServerLevel world, Mob entity, double squaredDistance) { -+ return (!(squaredDistance > (double) (entity.getType().getCategory().getDespawnDistance() * entity.getType().getCategory().getDespawnDistance())) || !entity.removeWhenFarAway(squaredDistance)) && entity.checkSpawnRules(world, MobSpawnType.NATURAL) && entity.checkSpawnObstruction(world); -+ } -+ -+ public static Optional getRandomSpawnMobAt(ServerLevel world, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobCategory spawnGroup, RandomSource random, BlockPos pos) { -+ Holder holder = world.getBiome(pos); -+ -+ return spawnGroup == MobCategory.WATER_AMBIENT && holder.is(BiomeTags.REDUCED_WATER_AMBIENT_SPAWNS) && random.nextFloat() < 0.98F ? Optional.empty() : NewNaturalSpawner.mobsAt(world, structureAccessor, chunkGenerator, spawnGroup, pos, holder).getRandom(random); -+ } -+ -+ public static boolean canSpawnMobAt(ServerLevel world, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobCategory spawnGroup, MobSpawnSettings.SpawnerData spawnEntry, BlockPos pos) { -+ return NewNaturalSpawner.mobsAt(world, structureAccessor, chunkGenerator, spawnGroup, pos, null).unwrap().contains(spawnEntry); -+ } -+ -+ public static WeightedRandomList mobsAt(ServerLevel world, StructureManager structureAccessor, ChunkGenerator chunkGenerator, MobCategory spawnGroup, BlockPos pos, @Nullable Holder biomeEntry) { -+ return NewNaturalSpawner.isInNetherFortressBounds(pos, world, spawnGroup, structureAccessor) ? NetherFortressStructure.FORTRESS_ENEMIES : chunkGenerator.getMobsAt(biomeEntry != null ? biomeEntry : world.getBiome(pos), structureAccessor, spawnGroup, pos); -+ } -+ -+ public static boolean isInNetherFortressBounds(BlockPos pos, ServerLevel world, MobCategory spawnGroup, StructureManager structureAccessor) { -+ if (spawnGroup == MobCategory.MONSTER && world.getBlockState(pos.below()).is(Blocks.NETHER_BRICKS)) { -+ Structure structure = structureAccessor.registryAccess().registryOrThrow(Registries.STRUCTURE).get(BuiltinStructures.FORTRESS); -+ -+ return structure != null && structureAccessor.getStructureAt(pos, structure).isValid(); -+ } else { -+ return false; -+ } -+ } -+ -+ public static BlockPos getRandomPosWithin(ServerLevel world, LevelChunk chunk) { -+ ChunkPos chunkcoordintpair = chunk.getPos(); -+ int i = chunkcoordintpair.getMinBlockX() + world.getThreadUnsafeRandom().nextInt(16); -+ int j = chunkcoordintpair.getMinBlockZ() + world.getThreadUnsafeRandom().nextInt(16); -+ int k = chunk.getHeight(Heightmap.Types.WORLD_SURFACE, i, j) + 1; -+ int l = Mth.randomBetweenInclusive(world.getThreadUnsafeRandom(), world.getMinBuildHeight(), k); -+ -+ return new BlockPos(i, l, j); -+ } -+ -+ public static boolean isValidEmptySpawnBlock(BlockGetter blockView, BlockPos pos, BlockState state, FluidState fluidState, EntityType entityType) { -+ return !state.isCollisionShapeFullBlock(blockView, pos) && (!state.isSignalSource() && (fluidState.isEmpty() && (!state.is(BlockTags.PREVENT_MOB_SPAWNING_INSIDE) && !entityType.isBlockDangerous(state)))); -+ } -+ -+ public static boolean isSpawnPositionOk(SpawnPlacements.Type location, LevelReader world, BlockPos pos, @Nullable EntityType entityType) { -+ if (location == SpawnPlacements.Type.NO_RESTRICTIONS) { -+ return true; -+ } else if (entityType != null && world.getWorldBorder().isWithinBounds(pos)) { -+ BlockState iblockdata = world.getBlockState(pos); -+ FluidState fluid = world.getFluidState(pos); -+ BlockPos blockposition1 = pos.above(); -+ BlockPos blockposition2 = pos.below(); -+ -+ switch (location) { -+ case IN_WATER -> { -+ return fluid.is(FluidTags.WATER) && !world.getBlockState(blockposition1).isRedstoneConductor(world, blockposition1); -+ } -+ case IN_LAVA -> { -+ return fluid.is(FluidTags.LAVA); -+ } -+ default -> { -+ BlockState iblockdata1 = world.getBlockState(blockposition2); -+ return iblockdata1.isValidSpawn(world, blockposition2, entityType) && NewNaturalSpawner.isValidEmptySpawnBlock(world, pos, iblockdata, fluid, entityType) && NewNaturalSpawner.isValidEmptySpawnBlock(world, blockposition1, world.getBlockState(blockposition1), world.getFluidState(blockposition1), entityType); -+ } -+ } -+ } else { -+ return false; -+ } -+ } -+ -+ public static void spawnMobsForChunkGeneration(ServerLevelAccessor world, Holder biomeEntry, ChunkPos chunkPos, RandomSource random) { -+ MobSpawnSettings biomesettingsmobs = biomeEntry.value().getMobSettings(); -+ WeightedRandomList weightedrandomlist = biomesettingsmobs.getMobs(MobCategory.CREATURE); -+ -+ if (!weightedrandomlist.isEmpty()) { -+ int i = chunkPos.getMinBlockX(); -+ int j = chunkPos.getMinBlockZ(); -+ -+ while (random.nextFloat() < biomesettingsmobs.getCreatureProbability()) { -+ Optional optional = weightedrandomlist.getRandom(random); -+ -+ if (optional.isPresent()) { -+ MobSpawnSettings.SpawnerData biomesettingsmobs_c = optional.get(); -+ int k = biomesettingsmobs_c.minCount + random.nextInt(1 + biomesettingsmobs_c.maxCount - biomesettingsmobs_c.minCount); -+ SpawnGroupData groupdataentity = null; -+ int l = i + random.nextInt(16); -+ int i1 = j + random.nextInt(16); -+ int j1 = l; -+ int k1 = i1; -+ -+ for (int l1 = 0; l1 < k; ++l1) { -+ boolean flag = false; -+ -+ for (int i2 = 0; !flag && i2 < 4; ++i2) { -+ BlockPos blockposition = NewNaturalSpawner.getTopNonCollidingPos(world, biomesettingsmobs_c.type, l, i1); -+ -+ if (biomesettingsmobs_c.type.canSummon() && NewNaturalSpawner.isSpawnPositionOk(SpawnPlacements.getPlacementType(biomesettingsmobs_c.type), world, blockposition, biomesettingsmobs_c.type)) { -+ float f = biomesettingsmobs_c.type.getWidth(); -+ double d0 = Mth.clamp(l, (double) i + (double) f, (double) i + 16.0D - (double) f); -+ double d1 = Mth.clamp(i1, (double) j + (double) f, (double) j + 16.0D - (double) f); -+ -+ if (!world.noCollision(biomesettingsmobs_c.type.getAABB(d0, blockposition.getY(), d1)) || !SpawnPlacements.checkSpawnRules(biomesettingsmobs_c.type, world, MobSpawnType.CHUNK_GENERATION, BlockPos.containing(d0, blockposition.getY(), d1), world.getRandom())) { -+ continue; -+ } -+ -+ Entity entity; -+ -+ try { -+ entity = biomesettingsmobs_c.type.create(world.getLevel()); -+ } catch (Exception exception) { -+ NewNaturalSpawner.LOGGER.warn("Failed to create mob", exception); -+ com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper -+ continue; -+ } -+ -+ if (entity == null) { -+ continue; -+ } -+ -+ entity.moveTo(d0, blockposition.getY(), d1, random.nextFloat() * 360.0F, 0.0F); -+ if (entity instanceof Mob entityinsentient) { -+ -+ if (entityinsentient.checkSpawnRules(world, MobSpawnType.CHUNK_GENERATION) && entityinsentient.checkSpawnObstruction(world)) { -+ groupdataentity = entityinsentient.finalizeSpawn(world, world.getCurrentDifficultyAt(entityinsentient.blockPosition()), MobSpawnType.CHUNK_GENERATION, groupdataentity, null); -+ world.addFreshEntityWithPassengers(entityinsentient, SpawnReason.CHUNK_GEN); // CraftBukkit -+ flag = true; -+ } -+ } -+ } -+ -+ l += random.nextInt(5) - random.nextInt(5); -+ -+ for (i1 += random.nextInt(5) - random.nextInt(5); l < i || l >= i + 16 || i1 < j || i1 >= j + 16; i1 = k1 + random.nextInt(5) - random.nextInt(5)) { -+ l = j1 + random.nextInt(5) - random.nextInt(5); -+ } -+ } -+ } -+ } -+ } -+ -+ } -+ } -+ -+ public static BlockPos getTopNonCollidingPos(LevelReader world, EntityType entityType, int x, int z) { -+ int k = world.getHeight(SpawnPlacements.getHeightmapType(entityType), x, z); -+ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos(x, k, z); -+ -+ if (world.dimensionType().hasCeiling()) { -+ do { -+ blockposition_mutableblockposition.move(Direction.DOWN); -+ } while (!world.getBlockState(blockposition_mutableblockposition).isAir()); -+ -+ do { -+ blockposition_mutableblockposition.move(Direction.DOWN); -+ } while (world.getBlockState(blockposition_mutableblockposition).isAir() && blockposition_mutableblockposition.getY() > world.getMinBuildHeight()); -+ } -+ -+ if (SpawnPlacements.getPlacementType(entityType) == SpawnPlacements.Type.ON_GROUND) { -+ BlockPos blockposition = blockposition_mutableblockposition.below(); -+ -+ if (world.getBlockState(blockposition).isPathfindable(world, blockposition, PathComputationType.LAND)) { -+ return blockposition; -+ } -+ } -+ -+ return blockposition_mutableblockposition.immutable(); -+ } -+}