diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f3b86f1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,44 @@ +[*] +charset=utf-8 +end_of_line=lf +insert_final_newline=true +indent_style=space +indent_size=4 +ij_any_block_comment_add_space = false +ij_any_block_comment_at_first_column = false +ij_any_line_comment_at_first_column = false +ij_any_line_comment_add_space = true + +[*.tiny] +indent_style=tab + +[*.bat] +end_of_line=crlf + +[*.yml] +indent_size=2 + +[*.patch] +trim_trailing_whitespace=false + +[*.java] +ij_continuation_indent_size = 4 +ij_java_class_count_to_use_import_on_demand = 999999 +ij_java_insert_inner_class_imports = false +ij_java_names_count_to_use_import_on_demand = 999999 +ij_java_imports_layout = *,|,$* +ij_java_generate_final_locals = true +ij_java_generate_final_parameters = true +ij_java_method_parameters_new_line_after_left_paren = true +ij_java_method_parameters_right_paren_on_new_line = true +ij_java_use_fq_class_names = false +ij_java_class_names_in_javadoc = 1 + +[divinemc-server/src/minecraft/java/**/*.java] +ij_java_use_fq_class_names = true + +[divinemc-server/src/minecraft/resources/data/**/*.json] +indent_size = 2 + +[paper-api/src/generated/**/*.java] +ij_java_imports_layout = $*,|,* diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c850393 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +* text=auto eol=lf + +*.sh text eol=lf +gradlew text eol=lf +*.bat text eol=crlf + +*.jar binary \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f4379c..1fe83df 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,12 +37,9 @@ jobs: chmod +x gradlew - name: Patch - run: ./gradlew applyPatches --stacktrace + run: ./gradlew applyAllPatches --stacktrace - - name: Create Reobf Jar - run: ./gradlew createReobfPaperclipJar --stacktrace - - - name: Create Mojmap Jar + - name: Build Paperclip Jar run: ./gradlew createMojmapPaperclipJar --stacktrace - name: Publish API @@ -53,8 +50,8 @@ jobs: if: env.debug == 'true' || github.ref_name != env.branch uses: actions/upload-artifact@main with: - name: Artifacts - path: build/libs + name: DivineMC + path: divinemc-server/build/libs/divinemc-paperclip-*-mojmap.jar - name: Release Artifacts if: github.ref_name == env.branch @@ -63,7 +60,7 @@ jobs: title: "Release #${{ env.workflow }}" automatic_release_tag: release-${{ env.workflow }} repo_token: "${{ secrets.GITHUB_TOKEN }}" - files: build/libs/*.jar + files: divinemc-server/build/libs/divinemc-paperclip-*-mojmap.jar prerelease: false - name: Release Artifacts (Latest) @@ -73,5 +70,5 @@ jobs: title: "Release #${{ env.workflow }}" automatic_release_tag: latest-${{ env.version }} repo_token: "${{ secrets.GITHUB_TOKEN }}" - files: build/libs/*.jar + files: divinemc-server/build/libs/divinemc-paperclip-*-mojmap.jar prerelease: false diff --git a/.gitignore b/.gitignore index 8cdd4cd..76a44b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,15 @@ -# DivineMC-API and DivineMC-Server changes + paper-api-generator -DivineMC-API -DivineMC-Server -paper-api-generator/* - # IDE and Gradle things build .gradle run .idea -# Other files and folders -build-data \ No newline at end of file +# Server things +/divinemc-server/build.gradle.kts +/divinemc-server/src/minecraft +/paper-server +/purpur-server +/divinemc-api/build.gradle.kts +/paper-api +/purpur-api +*.jar diff --git a/README.md b/README.md index 2c955fd..9f1ab74 100644 --- a/README.md +++ b/README.md @@ -72,18 +72,16 @@ dependencies { ### Initial setup First, clone this repository (do not download it) and the run the following command in the root directory: ```bash -./gradlew applyPatches +./gradlew applyAllPatches ``` After that, project is ready to use and editing it. ### Creating a patch -Patches are effectively just commits in either `DivineMC-API` or `DivineMC-Server`. To create one, just add a commit to either repo and run `./gradlew rebuildPatches`, and a patch will be placed in the patches folder. Modifying commits will also modify its corresponding patch file. - -Read our [documentation](https://docs.bx-team.space/divinemc/dev/contributing) to learn more about patches +Patches are effectively just commits in either `paper-api`, `paper-server`, `purpur-api`, `purpur-server` or `divinemc-server`. If you want to learn how to work with patch system, you can read our [contributing documentation](https://docs.bx-team.space/documentation/divinemc/development/contributing). ### Compiling -Use the command `./gradlew build` to build the API and server. Compiled JARs will be placed under `DivineMC-API/build/libs` and `DivineMC-Server/build/libs`. **These JARs are not used to start a server**. +Use the command `./gradlew build` to build the API and server. Compiled JARs will be placed under `divinemc-api/build/libs` and `divinemc-server/build/libs`. **These JARs are not used to start a server**. -To compile a server-ready paperclip jar, run `./gradlew createMojmapPaperclipJar`. The compiled paperclip jar will be in `build/libs/` in the main root. +To compile a server-ready paperclip jar, run `./gradlew createMojmapPaperclipJar`. The compiled paperclip jar will be put in `divinemc-server/build/libs`. ###### We don't steal logo from YatopiaMC! [List of all forks](https://gist.github.com/NONPLAYT/48742353af8ae36bcef5d1c36de9730a) diff --git a/build-data/dev-imports.txt b/build-data/dev-imports.txt index b818b96..1d9862a 100644 --- a/build-data/dev-imports.txt +++ b/build-data/dev-imports.txt @@ -8,3 +8,7 @@ # To import classes from the vanilla Minecraft jar use `minecraft` as the artifactId: # minecraft net.minecraft.world.level.entity.LevelEntityGetterAdapter # minecraft net/minecraft/world/level/entity/LevelEntityGetter.java +# To import minecraft data files, like the default chat type, use `mc_data` as the prefix: +# mc_data chat_type/chat.json +# mc_data dimension_type/overworld.json +# diff --git a/build-data/divinemc.at b/build-data/divinemc.at new file mode 100644 index 0000000..7239a5d --- /dev/null +++ b/build-data/divinemc.at @@ -0,0 +1,9 @@ +# This file is auto generated, any changes may be overridden! +# See CONTRIBUTING.md on how to add access transformers. +public net.minecraft.world.level.block.state.pattern.BlockPattern matches(Lnet/minecraft/core/BlockPos;Lnet/minecraft/core/Direction;Lnet/minecraft/core/Direction;Lcom/google/common/cache/LoadingCache;)Lnet/minecraft/world/level/block/state/pattern/BlockPattern$BlockPatternMatch; +public net.minecraft.world.level.chunk.storage.RegionFile getOversizedData(II)Lnet/minecraft/nbt/CompoundTag; +public net.minecraft.world.level.chunk.storage.RegionFile isOversized(II)Z +public net.minecraft.world.level.chunk.storage.RegionFile recalculateHeader()Z +public net.minecraft.world.level.chunk.storage.RegionFile setOversized(IIZ)V +public net.minecraft.world.level.chunk.storage.RegionFile write(Lnet/minecraft/world/level/ChunkPos;Ljava/nio/ByteBuffer;)V +public net.minecraft.world.level.pathfinder.SwimNodeEvaluator allowBreaching diff --git a/build.gradle.kts b/build.gradle.kts index 5905e3e..4947d32 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,43 +1,56 @@ -import io.papermc.paperweight.util.constants.PAPERCLIP_CONFIG +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { java - `maven-publish` - id("io.papermc.paperweight.patcher") version "1.7.7" + id("io.papermc.paperweight.patcher") version "2.0.0-beta.14" } val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/" -repositories { - mavenCentral() - maven("https://papermc.io/repo/repository/maven-public/") { - content { - onlyForConfigurations(PAPERCLIP_CONFIG) +paperweight { + upstreams.register("purpur") { + repo = github("PurpurMC", "Purpur") + ref = providers.gradleProperty("purpurRef") + + patchFile { + path = "purpur-server/build.gradle.kts" + outputFile = file("divinemc-server/build.gradle.kts") + patchFile = file("divinemc-server/build.gradle.kts.patch") } - } -} - -dependencies { - remapper("net.fabricmc:tiny-remapper:0.10.3:fat") - decompiler("org.vineflower:vineflower:1.10.1") - paperclip("io.papermc:paperclip:3.0.3") -} - -allprojects { - apply(plugin = "java") - apply(plugin = "maven-publish") - - java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) + patchFile { + path = "purpur-api/build.gradle.kts" + outputFile = file("divinemc-api/build.gradle.kts") + patchFile = file("divinemc-api/build.gradle.kts.patch") + } + patchRepo("paperApi") { + upstreamPath = "paper-api" + patchesDir = file("divinemc-api/paper-patches") + outputDir = file("paper-api") + } + patchDir("purpurApi") { + upstreamPath = "purpur-api" + excludes = listOf("build.gradle.kts", "build.gradle.kts.patch", "paper-patches") + patchesDir = file("divinemc-api/purpur-patches") + outputDir = file("purpur-api") } } } subprojects { + apply(plugin = "java-library") + apply(plugin = "maven-publish") + + extensions.configure { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + tasks.withType { options.encoding = Charsets.UTF_8.name() - options.release.set(21) + options.release = 21 + options.isFork = true } tasks.withType { options.encoding = Charsets.UTF_8.name() @@ -45,64 +58,29 @@ subprojects { tasks.withType { filteringCharset = Charsets.UTF_8.name() } + tasks.withType { + testLogging { + showStackTraces = true + exceptionFormat = TestExceptionFormat.FULL + events(TestLogEvent.STANDARD_OUT) + } + } + tasks.withType().configureEach { + isPreserveFileTimestamps = false + isReproducibleFileOrder = true + } repositories { mavenCentral() maven(paperMavenPublicUrl) - maven("https://oss.sonatype.org/content/groups/public/") - maven("https://ci.emc.gs/nexus/content/groups/aikar/") - maven("https://repo.aikar.co/content/groups/aikar") - maven("https://repo.md-5.net/content/repositories/releases/") - maven("https://hub.spigotmc.org/nexus/content/groups/public/") maven("https://jitpack.io") } -} -paperweight { - serverProject.set(project(":divinemc-server")) - - remapRepo.set("https://maven.fabricmc.net/") - decompileRepo.set(paperMavenPublicUrl) - - useStandardUpstream("purpur") { - url.set(github("PurpurMC", "Purpur")) - ref.set(providers.gradleProperty("purpurRef")) - - withStandardPatcher { - apiSourceDirPath.set("Purpur-API") - serverSourceDirPath.set("Purpur-Server") - - apiPatchDir.set(layout.projectDirectory.dir("patches/api")) - apiOutputDir.set(layout.projectDirectory.dir("DivineMC-API")) - - serverPatchDir.set(layout.projectDirectory.dir("patches/server")) - serverOutputDir.set(layout.projectDirectory.dir("DivineMC-Server")) - } - - patchTasks.register("generatedApi") { - isBareDirectory = true - upstreamDirPath = "paper-api-generator/generated" - patchDir = layout.projectDirectory.dir("patches/generated-api") - outputDir = layout.projectDirectory.dir("paper-api-generator/generated") - } - } -} - -tasks.generateDevelopmentBundle { - apiCoordinates = "space.bxteam.divinemc:divinemc-api" - libraryRepositories.set( - listOf( - "https://repo.maven.apache.org/maven2/", - paperMavenPublicUrl - ) - ) -} - -publishing { - if (project.providers.gradleProperty("publishDevBundle").isPresent) { - publications.create("devBundle") { - artifact(tasks.generateDevelopmentBundle) { - artifactId = "dev-bundle" + extensions.configure { + repositories { + maven("https://repo.bx-team.space/snapshots") { + name = "divinemc" + credentials(PasswordCredentials::class) } } } diff --git a/divinemc-api/build.gradle.kts.patch b/divinemc-api/build.gradle.kts.patch new file mode 100644 index 0000000..25b5d9a --- /dev/null +++ b/divinemc-api/build.gradle.kts.patch @@ -0,0 +1,40 @@ +--- a/purpur-api/build.gradle.kts ++++ b/purpur-api/build.gradle.kts +@@ -104,17 +_,21 @@ + java { + srcDir(generatedApiPath) + srcDir(file("../paper-api/src/main/java")) ++ srcDir(file("../purpur-api/src/main/java")) + } + resources { + srcDir(file("../paper-api/src/main/resources")) ++ srcDir(file("../purpur-api/src/main/resources")) + } + } + test { + java { + srcDir(file("../paper-api/src/test/java")) ++ srcDir(file("../purpur-api/src/test/java")) + } + resources { + srcDir(file("../paper-api/src/test/resources")) ++ srcDir(file("../purpur-api/src/test/resources")) + } + } + } +@@ -161,6 +_,15 @@ + pomProps.get().asFile.writeText("version=$projectVersion") + } + } ++ ++// DivineMC start - Hide unnecessary compilation warnings ++tasks.withType { ++ val compilerArgs = options.compilerArgs ++ compilerArgs.add("-Xlint:-module") ++ compilerArgs.add("-Xlint:-removal") ++ compilerArgs.add("-Xlint:-dep-ann") ++} ++// DivineMC end - Hide unnecessary compilation warnings + + tasks.jar { + from(generateApiVersioningFile.map { it.outputs.files.singleFile }) { diff --git a/patches/api/0003-Disable-reload-command-by-default.patch b/divinemc-api/paper-patches/features/0001-Disable-reload-command-by-default.patch similarity index 95% rename from patches/api/0003-Disable-reload-command-by-default.patch rename to divinemc-api/paper-patches/features/0001-Disable-reload-command-by-default.patch index 7b6048f..ccac9db 100644 --- a/patches/api/0003-Disable-reload-command-by-default.patch +++ b/divinemc-api/paper-patches/features/0001-Disable-reload-command-by-default.patch @@ -1,26 +1,24 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 12 Jun 2023 00:23:38 +0300 +Date: Sat, 11 Jan 2025 23:50:00 +0300 Subject: [PATCH] Disable reload command by default This is fully rewritten reload command, you can enable or disable it with -DDivineMC.EnableReloadCommand flag Read this article why reload is VERY UNSAFE in Bukkit: https://madelinemiller.dev/blog/problem-with-reload/ diff --git a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java -index bdfe68b386b5ca2878475e548d3c9a3808fce848..0a21f2138969f75425b720487e626aac796821d1 100644 +index bdfe68b386b5ca2878475e548d3c9a3808fce848..55387ddbf5734b3a860f129789b9ce95f0a69618 100644 --- a/src/main/java/org/bukkit/command/defaults/ReloadCommand.java +++ b/src/main/java/org/bukkit/command/defaults/ReloadCommand.java -@@ -1,8 +1,7 @@ +@@ -1,7 +1,5 @@ package org.bukkit.command.defaults; -import java.util.Arrays; -import java.util.Collections; import java.util.List; -+ import org.bukkit.Bukkit; import org.bukkit.ChatColor; - import org.bukkit.command.Command; -@@ -15,7 +14,7 @@ public class ReloadCommand extends BukkitCommand { +@@ -15,7 +13,7 @@ public class ReloadCommand extends BukkitCommand { this.description = "Reloads the server configuration and plugins"; this.usageMessage = "/reload [permissions|commands|confirm]"; // Paper this.setPermission("bukkit.command.reload"); @@ -29,7 +27,7 @@ index bdfe68b386b5ca2878475e548d3c9a3808fce848..0a21f2138969f75425b720487e626aac } @org.jetbrains.annotations.ApiStatus.Internal // Paper -@@ -25,48 +24,53 @@ public class ReloadCommand extends BukkitCommand { +@@ -25,47 +23,50 @@ public class ReloadCommand extends BukkitCommand { public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) { // Paper if (!testPermission(sender)) return true; @@ -45,7 +43,7 @@ index bdfe68b386b5ca2878475e548d3c9a3808fce848..0a21f2138969f75425b720487e626aac - Command.broadcastCommandMessage(sender, net.kyori.adventure.text.Component.text("Command aliases successfully reloaded.", net.kyori.adventure.text.format.NamedTextColor.GREEN)); + // DivineMC start - disable reload command by default & add startup flag + if (System.getProperty("DivineMC.EnableReloadCommand") == null || false) { -+ sender.sendMessage(net.kyori.adventure.text.Component.text("Operation denied, command disabled.", net.kyori.adventure.text.format.NamedTextColor.RED )); ++ sender.sendMessage(net.kyori.adventure.text.Component.text("Operation denied, command disabled.", net.kyori.adventure.text.format.NamedTextColor.RED)); + sender.sendMessage(net.kyori.adventure.text.Component.text("Reload command SHOULD NEVER be used in whatever circumstances.", net.kyori.adventure.text.format.NamedTextColor.YELLOW)); + sender.sendMessage(net.kyori.adventure.text.Component.text("DivineMC has intentionally disabled it in order to stop you using it, instead of restarting your server.", net.kyori.adventure.text.format.NamedTextColor.YELLOW)); + sender.sendMessage(net.kyori.adventure.text.Component.text("If you still want to enable reload command, add -DDivineMC.EnableReloadCommand=true flag to your startup arguments.", net.kyori.adventure.text.format.NamedTextColor.YELLOW)); @@ -83,7 +81,7 @@ index bdfe68b386b5ca2878475e548d3c9a3808fce848..0a21f2138969f75425b720487e626aac - return true; - } - // Paper end - +- - Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues when using some plugins."); - Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server."); - // Paper start - lifecycle events @@ -104,16 +102,15 @@ index bdfe68b386b5ca2878475e548d3c9a3808fce848..0a21f2138969f75425b720487e626aac + return true; + } } -+ // Paper end - lifecycle events -+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Reload complete."); -+ -+ return true; - } +- } - // Paper end - lifecycle events - Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Reload complete."); ++ // Paper end - lifecycle events ++ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Reload complete."); - return true; ++ return true; ++ } + // DivineMC end + return true; } - @NotNull diff --git a/patches/api/0004-Delete-Timings.patch b/divinemc-api/paper-patches/features/0002-Delete-Timings.patch similarity index 89% rename from patches/api/0004-Delete-Timings.patch rename to divinemc-api/paper-patches/features/0002-Delete-Timings.patch index fda194c..7ff3eeb 100644 --- a/patches/api/0004-Delete-Timings.patch +++ b/divinemc-api/paper-patches/features/0002-Delete-Timings.patch @@ -1,6 +1,6 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sun, 12 May 2024 20:24:24 +0300 +Date: Sat, 11 Jan 2025 23:57:34 +0300 Subject: [PATCH] Delete Timings @@ -179,7 +179,7 @@ index 42e7e712403676171d34d5f2be27e48e7a071ebd..00000000000000000000000000000000 -} diff --git a/src/main/java/co/aikar/timings/TimedEventExecutor.java b/src/main/java/co/aikar/timings/TimedEventExecutor.java deleted file mode 100644 -index 438a9c76381ea3f5b774e2232ff56c5dc6f82586..0000000000000000000000000000000000000000 +index c178d77bd23a484043f50e46ccd603734f7b6059..0000000000000000000000000000000000000000 --- a/src/main/java/co/aikar/timings/TimedEventExecutor.java +++ /dev/null @@ -1,93 +0,0 @@ @@ -265,9 +265,9 @@ index 438a9c76381ea3f5b774e2232ff56c5dc6f82586..00000000000000000000000000000000 - executor.execute(listener, event); - return; - } -- //try (Timing ignored = timings.startTiming()){ // Purpur +- //try (Timing ignored = timings.startTiming()){ // Purpur - Remove Timings - executor.execute(listener, event); -- //} // Purpur +- //} // Purpur - Remove Timings - } - - @Override @@ -278,7 +278,7 @@ index 438a9c76381ea3f5b774e2232ff56c5dc6f82586..00000000000000000000000000000000 -} diff --git a/src/main/java/co/aikar/timings/Timing.java b/src/main/java/co/aikar/timings/Timing.java deleted file mode 100644 -index 8709c955bac34bc546a8e022cfac808bc61ee793..0000000000000000000000000000000000000000 +index acd06662bf1da1f12f73e2e85c0fd1b10f3b8b93..0000000000000000000000000000000000000000 --- a/src/main/java/co/aikar/timings/Timing.java +++ /dev/null @@ -1,92 +0,0 @@ @@ -323,7 +323,7 @@ index 8709c955bac34bc546a8e022cfac808bc61ee793..00000000000000000000000000000000 - * @return Timing - */ - @NotNull -- @io.papermc.paper.annotation.DoNotUse // Purpur +- @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings - Timing startTiming(); - - /** @@ -331,7 +331,7 @@ index 8709c955bac34bc546a8e022cfac808bc61ee793..00000000000000000000000000000000 - * - * Will automatically be called when this Timing is used with try-with-resources - */ -- @io.papermc.paper.annotation.DoNotUse // Purpur +- @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings - void stopTiming(); - - /** @@ -342,7 +342,7 @@ index 8709c955bac34bc546a8e022cfac808bc61ee793..00000000000000000000000000000000 - * @return Timing - */ - @NotNull -- @io.papermc.paper.annotation.DoNotUse // Purpur +- @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings - Timing startTimingIfSync(); - - /** @@ -352,14 +352,14 @@ index 8709c955bac34bc546a8e022cfac808bc61ee793..00000000000000000000000000000000 - * - * But only if we are on the primary thread. - */ -- @io.papermc.paper.annotation.DoNotUse // Purpur +- @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings - void stopTimingIfSync(); - - /** - * @deprecated Doesn't do anything - Removed - */ - @Deprecated -- @io.papermc.paper.annotation.DoNotUse // Purpur +- @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings - void abort(); - - /** @@ -371,7 +371,7 @@ index 8709c955bac34bc546a8e022cfac808bc61ee793..00000000000000000000000000000000 - TimingHandler getTimingHandler(); - - @Override -- @io.papermc.paper.annotation.DoNotUse // Purpur +- @io.papermc.paper.annotation.DoNotUse // Purpur - Remove Timings - void close(); -} diff --git a/src/main/java/co/aikar/timings/TimingData.java b/src/main/java/co/aikar/timings/TimingData.java @@ -1285,10 +1285,10 @@ index df142a89b8c43acb81eb383eac0ef048a1f49a6e..00000000000000000000000000000000 -} diff --git a/src/main/java/co/aikar/timings/Timings.java b/src/main/java/co/aikar/timings/Timings.java deleted file mode 100644 -index 27a02f0c3261067d8e4ee6169c62cecbbfe50d42..0000000000000000000000000000000000000000 +index 627922fa5c65e9b77e79cd896c8c52889f5a0cc5..0000000000000000000000000000000000000000 --- a/src/main/java/co/aikar/timings/Timings.java +++ /dev/null -@@ -1,325 +0,0 @@ +@@ -1,324 +0,0 @@ -/* - * This file is licensed under the MIT License (MIT). - * @@ -1415,7 +1415,7 @@ index 27a02f0c3261067d8e4ee6169c62cecbbfe50d42..00000000000000000000000000000000 - @NotNull - public static Timing ofStart(@NotNull Plugin plugin, @NotNull String name, @Nullable Timing groupHandler) { - Timing timing = of(plugin, name, groupHandler); -- //timing.startTiming(); // Purpur +- //timing.startTiming(); // Purpur - Remove Timings - return timing; - } - @@ -1437,7 +1437,7 @@ index 27a02f0c3261067d8e4ee6169c62cecbbfe50d42..00000000000000000000000000000000 - */ - public static void setTimingsEnabled(boolean enabled) { - if (enabled && !warnedAboutDeprecationOnEnable) { -- //Bukkit.getLogger().severe(PlainTextComponentSerializer.plainText().serialize(deprecationMessage())); +- //Bukkit.getLogger().severe(PlainTextComponentSerializer.plainText().serialize(deprecationMessage())); // Purpur - Remove Timings - warnedAboutDeprecationOnEnable = true; - } - } @@ -1613,13 +1613,12 @@ index 27a02f0c3261067d8e4ee6169c62cecbbfe50d42..00000000000000000000000000000000 - return TimingsManager.getHandler(groupName, name, groupHandler); - } -} -- diff --git a/src/main/java/co/aikar/timings/TimingsCommand.java b/src/main/java/co/aikar/timings/TimingsCommand.java deleted file mode 100644 -index f28eec202237461cb489a2b13289d813381a25bc..0000000000000000000000000000000000000000 +index 04d0dc27406e9f96224f88edb1c535176e84d395..0000000000000000000000000000000000000000 --- a/src/main/java/co/aikar/timings/TimingsCommand.java +++ /dev/null -@@ -1,130 +0,0 @@ +@@ -1,132 +0,0 @@ -/* - * This file is licensed under the MIT License (MIT). - * @@ -1669,7 +1668,7 @@ index f28eec202237461cb489a2b13289d813381a25bc..00000000000000000000000000000000 - public TimingsCommand(@NotNull String name) { - super(name); - this.description = "Manages Spigot Timings data to see performance of the server."; -- this.usageMessage = "/timings";// "; // Purpur +- this.usageMessage = "/timings";// "; // Purpur - Remove Timings - this.setPermission("bukkit.command.timings"); - } - @@ -1679,10 +1678,12 @@ index f28eec202237461cb489a2b13289d813381a25bc..00000000000000000000000000000000 - return true; - } - if (true) { +- // Purpur start - Remove Timings - net.kyori.adventure.text.minimessage.MiniMessage mm = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage(); - sender.sendMessage(mm.deserialize("Purpur has removed timings to save your performance. Please use /spark instead")); - sender.sendMessage(mm.deserialize("For more information, view its documentation at")); -- sender.sendMessage(mm.deserialize("https://spark.lucko.me/docs/Command-Usage")); // Purpur +- sender.sendMessage(mm.deserialize("https://spark.lucko.me/docs/Command-Usage")); +- // Purpur end - Remove Timings - return true; - } - if (args.length < 1) { @@ -1743,7 +1744,7 @@ index f28eec202237461cb489a2b13289d813381a25bc..00000000000000000000000000000000 - Preconditions.checkNotNull(args, "Arguments cannot be null"); - Preconditions.checkNotNull(alias, "Alias cannot be null"); - -- if (false && args.length == 1) { // Purpur +- if (false && args.length == 1) { // Purpur - Remove Timings - return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS, - new ArrayList(TIMINGS_SUBCOMMANDS.size())); - } @@ -2103,158 +2104,9 @@ index 632c4961515f5052551f841cfa840e60bba7a257..00000000000000000000000000000000 - super.stopTiming(); - } -} -diff --git a/src/main/java/org/bukkit/command/Command.java b/src/main/java/org/bukkit/command/Command.java -index 74384a56eebbce41d431db2507c55eddbcf50a41..7d1ac11cfffbaf7d799f2a0032221fb23ee6daf5 100644 ---- a/src/main/java/org/bukkit/command/Command.java -+++ b/src/main/java/org/bukkit/command/Command.java -@@ -33,16 +33,6 @@ public abstract class Command { - protected String usageMessage; - private String permission; - private net.kyori.adventure.text.Component permissionMessage; // Paper -- /** -- * @deprecated Timings will be removed in the future -- */ -- @Deprecated(forRemoval = true) -- public co.aikar.timings.Timing timings; // Paper -- /** -- * @deprecated Timings will be removed in the future -- */ -- @Deprecated(forRemoval = true) -- @NotNull public String getTimingName() {return getName();} // Paper - - protected Command(@NotNull String name) { - this(name, "", "/" + name, new ArrayList()); -diff --git a/src/main/java/org/bukkit/command/FormattedCommandAlias.java b/src/main/java/org/bukkit/command/FormattedCommandAlias.java -index abe256e1e45ce28036da4aa1586715bc8a1a3414..9eab8024e0675865f17669847759a26d28f74f3a 100644 ---- a/src/main/java/org/bukkit/command/FormattedCommandAlias.java -+++ b/src/main/java/org/bukkit/command/FormattedCommandAlias.java -@@ -12,7 +12,6 @@ public class FormattedCommandAlias extends Command { - - public FormattedCommandAlias(@NotNull String alias, @NotNull String[] formatStrings) { - super(alias); -- timings = co.aikar.timings.TimingsManager.getCommandTiming("minecraft", this); // Spigot - this.formatStrings = formatStrings; - } - -@@ -120,10 +119,6 @@ public class FormattedCommandAlias extends Command { - return formatString.trim(); // Paper - Causes an extra space at the end, breaks with brig commands - } - -- @NotNull -- @Override // Paper -- public String getTimingName() {return "Command Forwarder - " + super.getTimingName();} // Paper -- - private static boolean inRange(int i, int j, int k) { - return i >= j && i <= k; - } -diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java -index 7740ad53796d08584bb0110f99af5639993e4d71..f0da5a8ae7e989e120f9d5ef2a417a9840aed7d0 100644 ---- a/src/main/java/org/bukkit/command/SimpleCommandMap.java -+++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java -@@ -39,7 +39,6 @@ public class SimpleCommandMap implements CommandMap { - register("bukkit", new VersionCommand("version")); - register("bukkit", new ReloadCommand("reload")); - //register("bukkit", new PluginsCommand("plugins")); // Paper -- register("bukkit", new co.aikar.timings.TimingsCommand("timings")); // Paper - } - - public void setFallbackCommands() { -@@ -71,7 +70,6 @@ public class SimpleCommandMap implements CommandMap { - */ - @Override - public boolean register(@NotNull String label, @NotNull String fallbackPrefix, @NotNull Command command) { -- command.timings = co.aikar.timings.TimingsManager.getCommandTiming(fallbackPrefix, command); // Paper - label = label.toLowerCase(Locale.ROOT).trim(); - fallbackPrefix = fallbackPrefix.toLowerCase(Locale.ROOT).trim(); - boolean registered = register(label, command, false, fallbackPrefix); -@@ -166,17 +164,9 @@ public class SimpleCommandMap implements CommandMap { - parsedArgs = event.getArgs(); - // Purpur end - -- // Paper start - Plugins do weird things to workaround normal registration -- if (target.timings == null) { -- target.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, target); -- } -- // Paper end -- - try { -- //try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources // Purpur - // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) - target.execute(sender, sentCommandLabel, parsedArgs); // Purpur -- //} // target.timings.stopTiming(); // Spigot // Paper // Purpur - } catch (CommandException ex) { - server.getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper - //target.timings.stopTiming(); // Spigot // Paper -diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java -index 2e6d62c4f3687e299c34e876c503b400e13be05a..0f5b442ea8e18d21c310205c217d22d18be3c863 100644 ---- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java -+++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java -@@ -724,12 +724,7 @@ public final class SimplePluginManager implements PluginManager { - throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled"); - } - -- executor = new co.aikar.timings.TimedEventExecutor(executor, plugin, null, event); // Paper -- if (false) { // Spigot - RL handles useTimings check now // Paper -- getEventListeners(event).register(new TimedRegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); -- } else { -- getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); -- } -+ getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); - } - - @NotNull -@@ -959,8 +954,7 @@ public final class SimplePluginManager implements PluginManager { - - @Override - public boolean useTimings() { -- if (true) {return this.paperPluginManager.useTimings();} // Paper -- return co.aikar.timings.Timings.isTimingsEnabled(); // Spigot -+ return false; // DivineMC - Delete Timings - } - - /** -@@ -970,7 +964,7 @@ public final class SimplePluginManager implements PluginManager { - */ - @Deprecated(forRemoval = true) - public void useTimings(boolean use) { -- co.aikar.timings.Timings.setTimingsEnabled(use); // Paper -+ - } - - // Paper start -diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java -index e2b631fc160f13ea6e27b69f835bbdf83d6d3dec..b1b15caa19137e92ab08df7affcd37d1c249570d 100644 ---- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java -+++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java -@@ -43,7 +43,6 @@ import org.bukkit.plugin.TimedRegisteredListener; - import org.bukkit.plugin.UnknownDependencyException; - import org.jetbrains.annotations.NotNull; - import org.jetbrains.annotations.Nullable; --import org.spigotmc.CustomTimingsHandler; // Spigot - import org.yaml.snakeyaml.error.YAMLException; - - /** -@@ -294,7 +293,7 @@ public final class JavaPluginLoader implements PluginLoader { - } - } - -- EventExecutor executor = new co.aikar.timings.TimedEventExecutor(new EventExecutor() { // Paper -+ EventExecutor executor = new EventExecutor() { // Paper - @Override - public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException { // Paper - try { -@@ -308,7 +307,7 @@ public final class JavaPluginLoader implements PluginLoader { - throw new EventException(t); - } - } -- }, plugin, method, eventClass); // Paper -+ }; // DivineMC - Delete Timings - if (false) { // Spigot - RL handles useTimings check now - eventSet.add(new TimedRegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); - } else { diff --git a/src/main/java/org/spigotmc/CustomTimingsHandler.java b/src/main/java/org/spigotmc/CustomTimingsHandler.java deleted file mode 100644 -index 9c2d605c50cbf9aefa56ec209df9f6cea1392e89..0000000000000000000000000000000000000000 +index b4249da3eb26eae26ec000cc4d56cd21ac2fc6d5..0000000000000000000000000000000000000000 --- a/src/main/java/org/spigotmc/CustomTimingsHandler.java +++ /dev/null @@ -1,67 +0,0 @@ @@ -2308,7 +2160,7 @@ index 9c2d605c50cbf9aefa56ec209df9f6cea1392e89..00000000000000000000000000000000 - public CustomTimingsHandler(@NotNull String name) { - Timing timing; - -- new AuthorNagException("Deprecated use of CustomTimingsHandler. Please Switch to Timings.of ASAP").printStackTrace(); +- new AuthorNagException("Deprecated use of CustomTimingsHandler. Timings has been removed.").printStackTrace(); - try { - final Method ofSafe = TimingsManager.class.getDeclaredMethod("getHandler", String.class, String.class, Timing.class); - ofSafe.setAccessible(true); @@ -2321,7 +2173,7 @@ index 9c2d605c50cbf9aefa56ec209df9f6cea1392e89..00000000000000000000000000000000 - handler = timing; - } - -- public void startTiming() { /*handler.startTiming();*/ } // Purpur -- public void stopTiming() { /*handler.stopTiming();*/ } // Purpur +- public void startTiming() { /*handler.startTiming();*/ } // Purpur - Remove Timings +- public void stopTiming() { /*handler.stopTiming();*/ } // Purpur - Remove Timings - -} diff --git a/divinemc-api/paper-patches/features/0003-Expanded-Adventure-support.patch b/divinemc-api/paper-patches/features/0003-Expanded-Adventure-support.patch new file mode 100644 index 0000000..3641c37 --- /dev/null +++ b/divinemc-api/paper-patches/features/0003-Expanded-Adventure-support.patch @@ -0,0 +1,77 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Tue, 14 Jan 2025 19:49:49 +0300 +Subject: [PATCH] Expanded Adventure support + +Adds support for Adventure in a few places where it was previously missing. +Original patch was taken from Parchment: https://github.com/ProjectEdenGG/Parchment + +diff --git a/src/main/java/org/bukkit/Color.java b/src/main/java/org/bukkit/Color.java +index f8edb964c4af597b03a2de06c464cc06a96b791c..596e2e09c6a64fa5861221789185d2fd7b004248 100644 +--- a/src/main/java/org/bukkit/Color.java ++++ b/src/main/java/org/bukkit/Color.java +@@ -17,7 +17,7 @@ import org.jetbrains.annotations.Nullable; + * but subject to change. + */ + @SerializableAs("Color") +-public final class Color implements ConfigurationSerializable { ++public final class Color implements ConfigurationSerializable, net.kyori.adventure.text.format.TextColor { // DivineMC - Expanded Adventure support + private static final int BIT_MASK = 0xff; + private static final int DEFAULT_ALPHA = 255; + +@@ -310,6 +310,13 @@ public final class Color implements ConfigurationSerializable { + return getAlpha() << 24 | getRed() << 16 | getGreen() << 8 | getBlue(); + } + ++ // DivineMC start - Expanded Adventure support ++ @Override ++ public int value() { ++ return asRGB(); ++ } ++ // DivineMC end - Expanded Adventure support ++ + /** + * Gets the color as an BGR integer. + * +diff --git a/src/main/java/org/bukkit/DyeColor.java b/src/main/java/org/bukkit/DyeColor.java +index 2f038f233afd4210687586800070d5f61e40562a..24068f23f45a0d3b837b04565d143a5cd8cd8869 100644 +--- a/src/main/java/org/bukkit/DyeColor.java ++++ b/src/main/java/org/bukkit/DyeColor.java +@@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable; + /** + * All supported color values for dyes and cloth + */ +-public enum DyeColor { ++public enum DyeColor implements net.kyori.adventure.util.RGBLike, net.kyori.adventure.text.format.StyleBuilderApplicable { // DivineMC - Expanded Adventure support + + /** + * Represents white dye. +@@ -135,6 +135,28 @@ public enum DyeColor { + return firework; + } + ++ // DivineMC start - Expanded Adventure support ++ @Override ++ public @org.jetbrains.annotations.Range(from = 0L, to = 255L) int red() { ++ return color.getRed(); ++ } ++ ++ @Override ++ public @org.jetbrains.annotations.Range(from = 0L, to = 255L) int green() { ++ return color.getGreen(); ++ } ++ ++ @Override ++ public @org.jetbrains.annotations.Range(from = 0L, to = 255L) int blue() { ++ return color.getBlue(); ++ } ++ ++ @Override ++ public void styleApply(net.kyori.adventure.text.format.Style.@org.jetbrains.annotations.NotNull Builder style) { ++ style.color(net.kyori.adventure.text.format.TextColor.color(color)); ++ } ++ // DivineMC end - Expanded Adventure support ++ + /** + * Gets the DyeColor with the given wool data value. + * diff --git a/divinemc-api/paper-patches/features/0004-Extend-Location-API.patch b/divinemc-api/paper-patches/features/0004-Extend-Location-API.patch new file mode 100644 index 0000000..5af0ef6 --- /dev/null +++ b/divinemc-api/paper-patches/features/0004-Extend-Location-API.patch @@ -0,0 +1,181 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Tue, 14 Jan 2025 20:36:30 +0300 +Subject: [PATCH] Extend Location API + + +diff --git a/src/main/java/org/bukkit/Location.java b/src/main/java/org/bukkit/Location.java +index 8bc340c9d4d8d1b113d877e25af769ef9251dc94..1311f7c82e412aec415a7f2a581400b84f86df9b 100644 +--- a/src/main/java/org/bukkit/Location.java ++++ b/src/main/java/org/bukkit/Location.java +@@ -1212,4 +1212,170 @@ public class Location implements Cloneable, ConfigurationSerializable, io.paperm + public @NotNull Location toLocation(@NotNull World world) { + return new Location(world, this.x(), this.y(), this.z(), this.getYaw(), this.getPitch()); + } ++ ++ // DivineMC start - Extend Location API ++ /** ++ * Sets the x-coordinate of this location. ++ * ++ * @param x The x-coordinate ++ * @return this location ++ */ ++ public @NotNull Location x(double x) { ++ this.x = x; ++ return this; ++ } ++ ++ /** ++ * Sets the y-coordinate of this location. ++ * ++ * @param y The y-coordinate ++ * @return this location ++ */ ++ public @NotNull Location y(double y) { ++ this.y = y; ++ return this; ++ } ++ ++ /** ++ * Sets the z-coordinate of this location. ++ * ++ * @param z The z-coordinate ++ * @return this location ++ */ ++ public @NotNull Location z(double z) { ++ this.z = z; ++ return this; ++ } ++ ++ /** ++ * Sets the yaw of this location, measured in degrees. ++ *
    ++ *
  • A yaw of 0 or 360 represents the positive z direction. ++ *
  • A yaw of 180 represents the negative z direction. ++ *
  • A yaw of 90 represents the negative x direction. ++ *
  • A yaw of 270 represents the positive x direction. ++ *
++ * Increasing yaw values are the equivalent of turning to your ++ * right-facing, increasing the scale of the next respective axis, and ++ * decreasing the scale of the previous axis. ++ * ++ * @param yaw new rotation's yaw ++ * @return this location ++ */ ++ public @NotNull Location yaw(float yaw) { ++ this.yaw = yaw; ++ return this; ++ } ++ ++ /** ++ * Sets the pitch of this location, measured in degrees. ++ *
    ++ *
  • A pitch of 0 represents level forward facing. ++ *
  • A pitch of 90 represents downward facing, or negative y ++ * direction. ++ *
  • A pitch of -90 represents upward facing, or positive y direction. ++ *
++ * Increasing pitch values the equivalent of looking down. ++ * ++ * @param pitch new incline's pitch ++ * @return this location ++ */ ++ public @NotNull Location pitch(float pitch) { ++ this.pitch = pitch; ++ return this; ++ } ++ ++ /** ++ * Sets the world that this location resides in ++ * ++ * @param world New world that this location resides in ++ * @return this location ++ */ ++ public @NotNull Location world(@Nullable World world) { ++ this.world = (world == null) ? null : new WeakReference<>(world); ++ return this; ++ } ++ ++ /** ++ * Increments the x-coordinate by the given value. ++ * ++ * @param x Amount to increment the x-coordinate by ++ * @return this location ++ */ ++ public @NotNull Location addX(double x) { ++ this.x += x; ++ return this; ++ } ++ ++ /** ++ * Increments the y-coordinate by the given value. ++ * ++ * @param y Amount to increment the y-coordinate by ++ * @return this location ++ */ ++ public @NotNull Location addY(double y) { ++ this.y += y; ++ return this; ++ } ++ ++ /** ++ * Increments the z-coordinate by the given value. ++ * ++ * @param z Amount to increment the z-coordinate by ++ * @return this location ++ */ ++ public @NotNull Location addZ(double z) { ++ this.z += z; ++ return this; ++ } ++ ++ /** ++ * Returns location with centered X, Y and Z values. ++ * ++ * @return this location ++ */ ++ public @NotNull Location center() { ++ return center(0.5); ++ } ++ ++ /** ++ * Returns location with centered X and Z values. ++ * Y will be set to provided. ++ * ++ * @param y Y adding value ++ * @return this location ++ */ ++ public @NotNull Location center(double y) { ++ return set(getBlockX() + 0.5, getBlockY() + y, getBlockZ() + 0.5); ++ } ++ ++ /** ++ * Checks if locations have the same X, Y and Z values. ++ * ++ * @param loc Location to check ++ * @return true if locations have same coordinates ++ * @apiNote Ignores world ++ */ ++ public boolean isSame(@NotNull Location loc) { ++ return getY() == loc.getY() && getX() == loc.getX() && getZ() == loc.getZ(); ++ } ++ ++ /** ++ * Shifts this location by one block up ++ * ++ * @return this location ++ */ ++ public @NotNull Location above() { ++ return addY(1); ++ } ++ ++ /** ++ * Shifts this location by one block down ++ * ++ * @return this location ++ */ ++ public @NotNull Location below() { ++ return addY(-1); ++ } ++ // DivineMC end - Extend Location API + } diff --git a/divinemc-api/paper-patches/features/0005-Extend-Sound-API.patch b/divinemc-api/paper-patches/features/0005-Extend-Sound-API.patch new file mode 100644 index 0000000..3275a03 --- /dev/null +++ b/divinemc-api/paper-patches/features/0005-Extend-Sound-API.patch @@ -0,0 +1,80 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Tue, 14 Jan 2025 20:42:04 +0300 +Subject: [PATCH] Extend Sound API + + +diff --git a/src/main/java/org/bukkit/block/Block.java b/src/main/java/org/bukkit/block/Block.java +index b703ad820ff873097dadff9e55b53fcc6b1b8698..b35c3852a3b8e62c7d2f67fc3ff651c8e0a4d5f2 100644 +--- a/src/main/java/org/bukkit/block/Block.java ++++ b/src/main/java/org/bukkit/block/Block.java +@@ -817,4 +817,29 @@ public interface Block extends Metadatable, Translatable, net.kyori.adventure.tr + return this.getBlockData().getDestroySpeed(itemStack, considerEnchants); + } + // Paper end - destroy speed API ++ ++ // DivineMC start - Extend Sound API ++ /** ++ * Plays a sound at the location of the block ++ * ++ * @param sound sound to play ++ * @param volume volume of the sound ++ * @param pitch pitch of the sound ++ */ ++ default void emitSound(@NotNull org.bukkit.Sound sound, float volume, float pitch) { ++ emitSound(sound, org.bukkit.SoundCategory.BLOCKS, volume, pitch); ++ } ++ ++ /** ++ * Plays a sound at the location of the block ++ * ++ * @param sound sound to play ++ * @param category category of the sound ++ * @param volume volume of the sound ++ * @param pitch pitch of the sound ++ */ ++ default void emitSound(@NotNull org.bukkit.Sound sound, @NotNull org.bukkit.SoundCategory category, float volume, float pitch) { ++ getWorld().playSound(getLocation().toCenterLocation(), sound, category, volume, pitch); ++ } ++ // DivineMC end - Extend Sound API + } +diff --git a/src/main/java/org/bukkit/entity/Entity.java b/src/main/java/org/bukkit/entity/Entity.java +index 49d3ca54a761e08cfe1bc770cb879223bf0e21e8..942f7497a56baa8e717980795e605a8907039fe6 100644 +--- a/src/main/java/org/bukkit/entity/Entity.java ++++ b/src/main/java/org/bukkit/entity/Entity.java +@@ -1251,4 +1251,35 @@ public interface Entity extends Metadatable, CommandSender, Nameable, Persistent + */ + void setImmuneToFire(@Nullable Boolean fireImmune); + // Purpur end - Fire Immunity API ++ ++ // DivineMC start - Extend Sound API ++ /** ++ * Plays a sound at the location of the entity ++ * ++ * @param sound sound to play ++ * @param volume volume of the sound ++ * @param pitch pitch of the sound ++ */ ++ default void emitSound(@NotNull org.bukkit.Sound sound, float volume, float pitch) { ++ org.bukkit.SoundCategory soundGroup = switch (this) { ++ case HumanEntity humanEntity -> org.bukkit.SoundCategory.PLAYERS; ++ case Ambient ambient -> org.bukkit.SoundCategory.AMBIENT; ++ case Monster monster -> org.bukkit.SoundCategory.HOSTILE; ++ default -> org.bukkit.SoundCategory.NEUTRAL; ++ }; ++ emitSound(sound, soundGroup, volume, pitch); ++ } ++ ++ /** ++ * Plays a sound at the location of the block ++ * ++ * @param sound sound to play ++ * @param category category of the sound ++ * @param volume volume of the sound ++ * @param pitch pitch of the sound ++ */ ++ default void emitSound(@NotNull org.bukkit.Sound sound, @NotNull org.bukkit.SoundCategory category, float volume, float pitch) { ++ getWorld().playSound(this, sound, category, volume, pitch); ++ } ++ // DivineMC end - Extend Sound API + } diff --git a/divinemc-api/paper-patches/files/src/main/java/io/papermc/paper/ServerBuildInfo.java.patch b/divinemc-api/paper-patches/files/src/main/java/io/papermc/paper/ServerBuildInfo.java.patch new file mode 100644 index 0000000..739b397 --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/io/papermc/paper/ServerBuildInfo.java.patch @@ -0,0 +1,17 @@ +--- a/src/main/java/io/papermc/paper/ServerBuildInfo.java ++++ b/src/main/java/io/papermc/paper/ServerBuildInfo.java +@@ -25,6 +_,14 @@ + */ + Key BRAND_PURPUR_ID = Key.key("purpurmc", "purpur"); + // Purpur end ++ ++ // DivineMC start ++ /** ++ * The brand id for DivineMC. ++ */ ++ Key BRAND_DIVINEMC_ID = Key.key("divinemc", "divinemc"); ++ // DivineMC end ++ + /** + * Gets the {@code ServerBuildInfo}. + * diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/Note.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/Note.java.patch new file mode 100644 index 0000000..45db7c3 --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/Note.java.patch @@ -0,0 +1,73 @@ +--- a/src/main/java/org/bukkit/Note.java ++++ b/src/main/java/org/bukkit/Note.java +@@ -127,6 +_,7 @@ + } + + private final byte note; ++ private final net.kyori.adventure.text.format.TextColor color; // DivineMC - Note Color API + + /** + * Creates a new note. +@@ -138,6 +_,7 @@ + Preconditions.checkArgument(note >= 0 && note <= 24, "The note value has to be between 0 and 24."); + + this.note = (byte) note; ++ this.color = getColor(note); // DivineMC - Note Color API + } + + /** +@@ -158,6 +_,7 @@ + } + + this.note = (byte) (octave * Tone.TONES_COUNT + tone.getId(sharped)); ++ this.color = getColor(note); // DivineMC - Note Color API + } + + /** +@@ -298,4 +_,46 @@ + public String toString() { + return "Note{" + getTone().toString() + (isSharped() ? "#" : "") + "}"; + } ++ ++ // DivineMC start - Note Color API ++ /** ++ * Get color of the played note. ++ * ++ * @return the color of the note ++ */ ++ @NotNull ++ public net.kyori.adventure.text.format.TextColor getColor() { ++ return color; ++ } ++ ++ private static @NotNull net.kyori.adventure.text.format.TextColor getColor(int note) { ++ return switch (note) { ++ case 0 -> net.kyori.adventure.text.format.TextColor.fromHexString("#77D700"); ++ case 1 -> net.kyori.adventure.text.format.TextColor.fromHexString("#95C000"); ++ case 2 -> net.kyori.adventure.text.format.TextColor.fromHexString("#B2A500"); ++ case 3 -> net.kyori.adventure.text.format.TextColor.fromHexString("#CC8600"); ++ case 4 -> net.kyori.adventure.text.format.TextColor.fromHexString("#E26500"); ++ case 5 -> net.kyori.adventure.text.format.TextColor.fromHexString("#F34100"); ++ case 6 -> net.kyori.adventure.text.format.TextColor.fromHexString("#FC1E00"); ++ case 7 -> net.kyori.adventure.text.format.TextColor.fromHexString("#FE000F"); ++ case 8 -> net.kyori.adventure.text.format.TextColor.fromHexString("#F70033"); ++ case 9 -> net.kyori.adventure.text.format.TextColor.fromHexString("#E8005A"); ++ case 10 -> net.kyori.adventure.text.format.TextColor.fromHexString("#CF0083"); ++ case 11 -> net.kyori.adventure.text.format.TextColor.fromHexString("#AE00A9"); ++ case 12 -> net.kyori.adventure.text.format.TextColor.fromHexString("#8600CC"); ++ case 13 -> net.kyori.adventure.text.format.TextColor.fromHexString("#5B00E7"); ++ case 14 -> net.kyori.adventure.text.format.TextColor.fromHexString("#2D00F9"); ++ case 15 -> net.kyori.adventure.text.format.TextColor.fromHexString("#020AFE"); ++ case 16 -> net.kyori.adventure.text.format.TextColor.fromHexString("#0037F6"); ++ case 17 -> net.kyori.adventure.text.format.TextColor.fromHexString("#0068E0"); ++ case 18 -> net.kyori.adventure.text.format.TextColor.fromHexString("#009ABC"); ++ case 19 -> net.kyori.adventure.text.format.TextColor.fromHexString("#00C68D"); ++ case 20 -> net.kyori.adventure.text.format.TextColor.fromHexString("#00E958"); ++ case 21 -> net.kyori.adventure.text.format.TextColor.fromHexString("#00FC21"); ++ case 22 -> net.kyori.adventure.text.format.TextColor.fromHexString("#1FFC00"); ++ case 23 -> net.kyori.adventure.text.format.TextColor.fromHexString("#59E800"); ++ default -> net.kyori.adventure.text.format.TextColor.fromHexString("#94C100"); ++ }; ++ } ++ // DivineMC end - Note Color API + } diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/Server.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/Server.java.patch new file mode 100644 index 0000000..587acd0 --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/Server.java.patch @@ -0,0 +1,16 @@ +--- a/src/main/java/org/bukkit/Server.java ++++ b/src/main/java/org/bukkit/Server.java +@@ -2342,6 +_,13 @@ + } + // Purpur end + ++ // DivineMC start ++ @NotNull ++ public org.bukkit.configuration.file.YamlConfiguration getDivineConfig() { ++ throw new UnsupportedOperationException("Not supported yet"); ++ } ++ // DivineMC end ++ + /** + * Sends the component to the player + * diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/Command.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/Command.java.patch new file mode 100644 index 0000000..f95e9f7 --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/Command.java.patch @@ -0,0 +1,19 @@ +--- a/src/main/java/org/bukkit/command/Command.java ++++ b/src/main/java/org/bukkit/command/Command.java +@@ -33,16 +_,6 @@ + protected String usageMessage; + private String permission; + private net.kyori.adventure.text.Component permissionMessage; // Paper +- /** +- * @deprecated Timings will be removed in the future +- */ +- @Deprecated(forRemoval = true) +- public co.aikar.timings.Timing timings; // Paper +- /** +- * @deprecated Timings will be removed in the future +- */ +- @Deprecated(forRemoval = true) +- @NotNull public String getTimingName() {return getName();} // Paper + + protected Command(@NotNull String name) { + this(name, "", "/" + name, new ArrayList()); diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/FormattedCommandAlias.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/FormattedCommandAlias.java.patch new file mode 100644 index 0000000..bc49ff4 --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/FormattedCommandAlias.java.patch @@ -0,0 +1,21 @@ +--- a/src/main/java/org/bukkit/command/FormattedCommandAlias.java ++++ b/src/main/java/org/bukkit/command/FormattedCommandAlias.java +@@ -12,7 +_,6 @@ + + public FormattedCommandAlias(@NotNull String alias, @NotNull String[] formatStrings) { + super(alias); +- timings = co.aikar.timings.TimingsManager.getCommandTiming("minecraft", this); // Spigot + this.formatStrings = formatStrings; + } + +@@ -119,10 +_,6 @@ + + return formatString.trim(); // Paper - Causes an extra space at the end, breaks with brig commands + } +- +- @NotNull +- @Override // Paper +- public String getTimingName() {return "Command Forwarder - " + super.getTimingName();} // Paper + + private static boolean inRange(int i, int j, int k) { + return i >= j && i <= k; diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/SimpleCommandMap.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/SimpleCommandMap.java.patch new file mode 100644 index 0000000..ca83d8c --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/SimpleCommandMap.java.patch @@ -0,0 +1,31 @@ +--- a/src/main/java/org/bukkit/command/SimpleCommandMap.java ++++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java +@@ -39,7 +_,6 @@ + register("bukkit", new VersionCommand("version")); + register("bukkit", new ReloadCommand("reload")); + //register("bukkit", new PluginsCommand("plugins")); // Paper +- register("bukkit", new co.aikar.timings.TimingsCommand("timings")); // Paper + } + + public void setFallbackCommands() { +@@ -71,7 +_,6 @@ + */ + @Override + public boolean register(@NotNull String label, @NotNull String fallbackPrefix, @NotNull Command command) { +- command.timings = co.aikar.timings.TimingsManager.getCommandTiming(fallbackPrefix, command); // Paper + label = label.toLowerCase(Locale.ROOT).trim(); + fallbackPrefix = fallbackPrefix.toLowerCase(Locale.ROOT).trim(); + boolean registered = register(label, command, false, fallbackPrefix); +@@ -165,12 +_,6 @@ + sentCommandLabel = event.getLabel(); + parsedArgs = event.getArgs(); + // Purpur end - ExecuteCommandEvent +- +- // Paper start - Plugins do weird things to workaround normal registration +- if (target.timings == null) { +- target.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, target); +- } +- // Paper end + + try { + //try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources // Purpur - Remove Timings diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/defaults/VersionCommand.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/defaults/VersionCommand.java.patch new file mode 100644 index 0000000..33e6aa3 --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/command/defaults/VersionCommand.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java ++++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java +@@ -259,7 +_,7 @@ + // Purpur start + int distance = getVersionFetcher().distance(); + final Component message = Component.join(net.kyori.adventure.text.JoinConfiguration.separator(Component.newline()), +- ChatColor.parseMM("Current Purpur Version: %s%s*", distance == 0 ? "" : distance > 0 ? "" : "", Bukkit.getVersion()), ++ ChatColor.parseMM("Current DivineMC Version: %s%s*", distance == 0 ? "" : distance > 0 ? "" : "", Bukkit.getVersion()), // DivineMC - Rebrand + // Purpur end + msg + ); diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/AbstractArrow.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/AbstractArrow.java.patch new file mode 100644 index 0000000..22a6821 --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/AbstractArrow.java.patch @@ -0,0 +1,14 @@ +--- a/src/main/java/org/bukkit/entity/AbstractArrow.java ++++ b/src/main/java/org/bukkit/entity/AbstractArrow.java +@@ -282,4 +_,11 @@ + */ + void setShooter(@Nullable org.bukkit.projectiles.ProjectileSource source, boolean resetPickupStatus); + // Paper end - Fix PickupStatus getting reset ++ ++ // DivineMC start - Add startFalling method to AbstractArrow ++ /** ++ * Asks projectile to start falling if possible ++ */ ++ void startFalling(); ++ // DivineMC end - Add startFalling method to AbstractArrow + } diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/Player.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/Player.java.patch new file mode 100644 index 0000000..27bfd2a --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/entity/Player.java.patch @@ -0,0 +1,17 @@ +--- a/src/main/java/org/bukkit/entity/Player.java ++++ b/src/main/java/org/bukkit/entity/Player.java +@@ -4011,4 +_,14 @@ + sendDeathScreen(message); + } + // Purpur end ++ ++ // DivineMC start - Open Ender Chest API ++ /** ++ * Opens ender chest for the player ++ * ++ * @param enderChest ender chest ++ * @return whether the chest was opened ++ */ ++ boolean openEnderChest(@NotNull org.bukkit.block.EnderChest enderChest); ++ // DivineMC end - Open Ender Chest API + } diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/event/block/NotePlayEvent.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/event/block/NotePlayEvent.java.patch new file mode 100644 index 0000000..0caf824 --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/event/block/NotePlayEvent.java.patch @@ -0,0 +1,43 @@ +--- a/src/main/java/org/bukkit/event/block/NotePlayEvent.java ++++ b/src/main/java/org/bukkit/event/block/NotePlayEvent.java +@@ -16,13 +_,21 @@ + private static HandlerList handlers = new HandlerList(); + private Instrument instrument; + private Note note; ++ private final @org.jetbrains.annotations.Nullable org.bukkit.entity.Player player; // DivineMC - Add player to NotePlayEvent + private boolean cancelled = false; + ++ // DivineMC start - Add player to NotePlayEvent + public NotePlayEvent(@NotNull Block block, @NotNull Instrument instrument, @NotNull Note note) { ++ this(block, instrument, note, null); ++ } ++ ++ public NotePlayEvent(@NotNull Block block, @NotNull Instrument instrument, @NotNull Note note, @org.jetbrains.annotations.Nullable org.bukkit.entity.Player player) { + super(block); + this.instrument = instrument; + this.note = note; ++ this.player = player; + } ++ // DivineMC end - Add player to NotePlayEvent + + @Override + public boolean isCancelled() { +@@ -53,6 +_,18 @@ + public Note getNote() { + return note; + } ++ ++ // DivineMC start - Add player to NotePlayEvent ++ /** ++ * Gets the {@link org.bukkit.entity.Player} who played the note ++ * ++ * @return player who played the note, if present ++ */ ++ @org.jetbrains.annotations.Nullable ++ public org.bukkit.entity.Player getPlayer() { ++ return this.player; ++ } ++ // DivineMC end - Add player to NotePlayEvent + + /** + * Overrides the {@link Instrument} to be used. diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/plugin/SimplePluginManager.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/plugin/SimplePluginManager.java.patch new file mode 100644 index 0000000..e1e854a --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/plugin/SimplePluginManager.java.patch @@ -0,0 +1,35 @@ +--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java ++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java +@@ -720,12 +_,7 @@ + throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled"); + } + +- executor = new co.aikar.timings.TimedEventExecutor(executor, plugin, null, event); // Paper +- if (false) { // Spigot - RL handles useTimings check now // Paper +- getEventListeners(event).register(new TimedRegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); +- } else { +- getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); +- } ++ getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); + } + + @NotNull +@@ -955,8 +_,7 @@ + + @Override + public boolean useTimings() { +- if (true) {return this.paperPluginManager.useTimings();} // Paper +- return co.aikar.timings.Timings.isTimingsEnabled(); // Spigot ++ return false; + } + + /** +@@ -966,7 +_,7 @@ + */ + @Deprecated(forRemoval = true) + public void useTimings(boolean use) { +- co.aikar.timings.Timings.setTimingsEnabled(use); // Paper ++ // DivineMC - Delete timings + } + + // Paper start diff --git a/divinemc-api/paper-patches/files/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java.patch b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java.patch new file mode 100644 index 0000000..ded8bdb --- /dev/null +++ b/divinemc-api/paper-patches/files/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java.patch @@ -0,0 +1,28 @@ +--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java ++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +@@ -43,7 +_,6 @@ + import org.bukkit.plugin.UnknownDependencyException; + import org.jetbrains.annotations.NotNull; + import org.jetbrains.annotations.Nullable; +-import org.spigotmc.CustomTimingsHandler; // Spigot + import org.yaml.snakeyaml.error.YAMLException; + + /** +@@ -294,7 +_,7 @@ + } + } + +- EventExecutor executor = new co.aikar.timings.TimedEventExecutor(new EventExecutor() { // Paper ++ EventExecutor executor = new EventExecutor() { // Paper + @Override + public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException { // Paper + try { +@@ -308,7 +_,7 @@ + throw new EventException(t); + } + } +- }, plugin, method, eventClass); // Paper ++ }; // DivineMC - Delete Timings + if (false) { // Spigot - RL handles useTimings check now + eventSet.add(new TimedRegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); + } else { diff --git a/patches/removed/1.19.2/server/0002-Global-Eula-file.patch b/divinemc-archived-patches/removed/1.19.2/server/0002-Global-Eula-file.patch similarity index 100% rename from patches/removed/1.19.2/server/0002-Global-Eula-file.patch rename to divinemc-archived-patches/removed/1.19.2/server/0002-Global-Eula-file.patch diff --git a/patches/removed/1.19.2/server/0004-Reduce-constants-allocations.patch b/divinemc-archived-patches/removed/1.19.2/server/0004-Reduce-constants-allocations.patch similarity index 100% rename from patches/removed/1.19.2/server/0004-Reduce-constants-allocations.patch rename to divinemc-archived-patches/removed/1.19.2/server/0004-Reduce-constants-allocations.patch diff --git a/patches/removed/1.19.2/server/0007-lithium-CompactSineLUT.patch b/divinemc-archived-patches/removed/1.19.2/server/0007-lithium-CompactSineLUT.patch similarity index 100% rename from patches/removed/1.19.2/server/0007-lithium-CompactSineLUT.patch rename to divinemc-archived-patches/removed/1.19.2/server/0007-lithium-CompactSineLUT.patch diff --git a/patches/removed/1.19.2/server/0008-lithium-fast-retrieval.patch b/divinemc-archived-patches/removed/1.19.2/server/0008-lithium-fast-retrieval.patch similarity index 100% rename from patches/removed/1.19.2/server/0008-lithium-fast-retrieval.patch rename to divinemc-archived-patches/removed/1.19.2/server/0008-lithium-fast-retrieval.patch diff --git a/patches/removed/1.19.2/server/0042-lithium-replace-AI-goal-set-with-optimized-collectio.patch b/divinemc-archived-patches/removed/1.19.2/server/0042-lithium-replace-AI-goal-set-with-optimized-collectio.patch similarity index 100% rename from patches/removed/1.19.2/server/0042-lithium-replace-AI-goal-set-with-optimized-collectio.patch rename to divinemc-archived-patches/removed/1.19.2/server/0042-lithium-replace-AI-goal-set-with-optimized-collectio.patch diff --git a/patches/removed/1.19.2/server/0060-lithium-gen.patch b/divinemc-archived-patches/removed/1.19.2/server/0060-lithium-gen.patch similarity index 100% rename from patches/removed/1.19.2/server/0060-lithium-gen.patch rename to divinemc-archived-patches/removed/1.19.2/server/0060-lithium-gen.patch diff --git a/patches/removed/1.19.3/api/0003-Update-Bungeecord-Chat-API.patch b/divinemc-archived-patches/removed/1.19.3/api/0003-Update-Bungeecord-Chat-API.patch similarity index 100% rename from patches/removed/1.19.3/api/0003-Update-Bungeecord-Chat-API.patch rename to divinemc-archived-patches/removed/1.19.3/api/0003-Update-Bungeecord-Chat-API.patch diff --git a/patches/removed/1.19.3/server/0011-Fix-outdated-server-showing-in-ping-before-server-fu.patch b/divinemc-archived-patches/removed/1.19.3/server/0011-Fix-outdated-server-showing-in-ping-before-server-fu.patch similarity index 100% rename from patches/removed/1.19.3/server/0011-Fix-outdated-server-showing-in-ping-before-server-fu.patch rename to divinemc-archived-patches/removed/1.19.3/server/0011-Fix-outdated-server-showing-in-ping-before-server-fu.patch diff --git a/patches/removed/1.19.3/server/0012-Remove-TickTask.patch b/divinemc-archived-patches/removed/1.19.3/server/0012-Remove-TickTask.patch similarity index 100% rename from patches/removed/1.19.3/server/0012-Remove-TickTask.patch rename to divinemc-archived-patches/removed/1.19.3/server/0012-Remove-TickTask.patch diff --git a/patches/removed/1.19.3/server/0014-Completely-remove-bootstrapExecutor.patch b/divinemc-archived-patches/removed/1.19.3/server/0014-Completely-remove-bootstrapExecutor.patch similarity index 100% rename from patches/removed/1.19.3/server/0014-Completely-remove-bootstrapExecutor.patch rename to divinemc-archived-patches/removed/1.19.3/server/0014-Completely-remove-bootstrapExecutor.patch diff --git a/patches/removed/1.19.3/server/0017-Remove-sync-chunk-writes-in-server.properties.patch b/divinemc-archived-patches/removed/1.19.3/server/0017-Remove-sync-chunk-writes-in-server.properties.patch similarity index 100% rename from patches/removed/1.19.3/server/0017-Remove-sync-chunk-writes-in-server.properties.patch rename to divinemc-archived-patches/removed/1.19.3/server/0017-Remove-sync-chunk-writes-in-server.properties.patch diff --git a/patches/removed/1.20/api/0002-Bump-Bungeecord-Chat-API-to-1.19-R0.1-SNAPSHOT.patch b/divinemc-archived-patches/removed/1.20/api/0002-Bump-Bungeecord-Chat-API-to-1.19-R0.1-SNAPSHOT.patch similarity index 100% rename from patches/removed/1.20/api/0002-Bump-Bungeecord-Chat-API-to-1.19-R0.1-SNAPSHOT.patch rename to divinemc-archived-patches/removed/1.20/api/0002-Bump-Bungeecord-Chat-API-to-1.19-R0.1-SNAPSHOT.patch diff --git a/patches/removed/1.20/api/0002-Rework-Spigot-Deprecations.patch b/divinemc-archived-patches/removed/1.20/api/0002-Rework-Spigot-Deprecations.patch similarity index 100% rename from patches/removed/1.20/api/0002-Rework-Spigot-Deprecations.patch rename to divinemc-archived-patches/removed/1.20/api/0002-Rework-Spigot-Deprecations.patch diff --git a/patches/removed/1.20/api/0003-Paper-PR-Add-Movement-Direction-API.patch b/divinemc-archived-patches/removed/1.20/api/0003-Paper-PR-Add-Movement-Direction-API.patch similarity index 100% rename from patches/removed/1.20/api/0003-Paper-PR-Add-Movement-Direction-API.patch rename to divinemc-archived-patches/removed/1.20/api/0003-Paper-PR-Add-Movement-Direction-API.patch diff --git a/patches/removed/1.20/api/0004-Paper-PR-BoneMeal-API.patch b/divinemc-archived-patches/removed/1.20/api/0004-Paper-PR-BoneMeal-API.patch similarity index 100% rename from patches/removed/1.20/api/0004-Paper-PR-BoneMeal-API.patch rename to divinemc-archived-patches/removed/1.20/api/0004-Paper-PR-BoneMeal-API.patch diff --git a/patches/removed/1.20/api/0005-Additional-pathfinding-API.patch b/divinemc-archived-patches/removed/1.20/api/0005-Additional-pathfinding-API.patch similarity index 100% rename from patches/removed/1.20/api/0005-Additional-pathfinding-API.patch rename to divinemc-archived-patches/removed/1.20/api/0005-Additional-pathfinding-API.patch diff --git a/patches/removed/1.20/api/0006-Optimize-spigot-event-bus.patch b/divinemc-archived-patches/removed/1.20/api/0006-Optimize-spigot-event-bus.patch similarity index 100% rename from patches/removed/1.20/api/0006-Optimize-spigot-event-bus.patch rename to divinemc-archived-patches/removed/1.20/api/0006-Optimize-spigot-event-bus.patch diff --git a/patches/removed/1.20/server/0003-Accept-EULA.patch b/divinemc-archived-patches/removed/1.20/server/0003-Accept-EULA.patch similarity index 100% rename from patches/removed/1.20/server/0003-Accept-EULA.patch rename to divinemc-archived-patches/removed/1.20/server/0003-Accept-EULA.patch diff --git a/patches/removed/1.20/server/0007-Fix-entity-serialization.patch b/divinemc-archived-patches/removed/1.20/server/0007-Fix-entity-serialization.patch similarity index 100% rename from patches/removed/1.20/server/0007-Fix-entity-serialization.patch rename to divinemc-archived-patches/removed/1.20/server/0007-Fix-entity-serialization.patch diff --git a/patches/removed/1.20/server/0009-EMC-Allow-negative-Amplifier-values.patch b/divinemc-archived-patches/removed/1.20/server/0009-EMC-Allow-negative-Amplifier-values.patch similarity index 100% rename from patches/removed/1.20/server/0009-EMC-Allow-negative-Amplifier-values.patch rename to divinemc-archived-patches/removed/1.20/server/0009-EMC-Allow-negative-Amplifier-values.patch diff --git a/patches/removed/1.20/server/0010-Use-unused-ghast-sound.patch b/divinemc-archived-patches/removed/1.20/server/0010-Use-unused-ghast-sound.patch similarity index 100% rename from patches/removed/1.20/server/0010-Use-unused-ghast-sound.patch rename to divinemc-archived-patches/removed/1.20/server/0010-Use-unused-ghast-sound.patch diff --git a/patches/removed/1.20/server/0012-Fallback-to-Dimension-if-World-UUID-is-unknown.patch b/divinemc-archived-patches/removed/1.20/server/0012-Fallback-to-Dimension-if-World-UUID-is-unknown.patch similarity index 100% rename from patches/removed/1.20/server/0012-Fallback-to-Dimension-if-World-UUID-is-unknown.patch rename to divinemc-archived-patches/removed/1.20/server/0012-Fallback-to-Dimension-if-World-UUID-is-unknown.patch diff --git a/patches/removed/1.20/server/0013-Paper-PR-Add-Movement-Direction-API.patch b/divinemc-archived-patches/removed/1.20/server/0013-Paper-PR-Add-Movement-Direction-API.patch similarity index 100% rename from patches/removed/1.20/server/0013-Paper-PR-Add-Movement-Direction-API.patch rename to divinemc-archived-patches/removed/1.20/server/0013-Paper-PR-Add-Movement-Direction-API.patch diff --git a/patches/removed/1.20/server/0014-Paper-PR-BoneMeal-API.patch b/divinemc-archived-patches/removed/1.20/server/0014-Paper-PR-BoneMeal-API.patch similarity index 100% rename from patches/removed/1.20/server/0014-Paper-PR-BoneMeal-API.patch rename to divinemc-archived-patches/removed/1.20/server/0014-Paper-PR-BoneMeal-API.patch diff --git a/patches/removed/1.20/server/0015-Paper-PR-Fire-ServerListPingEvent-for-secondary-motd.patch b/divinemc-archived-patches/removed/1.20/server/0015-Paper-PR-Fire-ServerListPingEvent-for-secondary-motd.patch similarity index 100% rename from patches/removed/1.20/server/0015-Paper-PR-Fire-ServerListPingEvent-for-secondary-motd.patch rename to divinemc-archived-patches/removed/1.20/server/0015-Paper-PR-Fire-ServerListPingEvent-for-secondary-motd.patch diff --git a/patches/removed/1.20/server/0016-Additional-pathfinding-API.patch b/divinemc-archived-patches/removed/1.20/server/0016-Additional-pathfinding-API.patch similarity index 100% rename from patches/removed/1.20/server/0016-Additional-pathfinding-API.patch rename to divinemc-archived-patches/removed/1.20/server/0016-Additional-pathfinding-API.patch diff --git a/patches/removed/1.20/server/0019-Better-Impaling.patch b/divinemc-archived-patches/removed/1.20/server/0019-Better-Impaling.patch similarity index 100% rename from patches/removed/1.20/server/0019-Better-Impaling.patch rename to divinemc-archived-patches/removed/1.20/server/0019-Better-Impaling.patch diff --git a/patches/removed/1.20/server/0022-Do-not-process-chat-commands-before-player-has-joine.patch b/divinemc-archived-patches/removed/1.20/server/0022-Do-not-process-chat-commands-before-player-has-joine.patch similarity index 100% rename from patches/removed/1.20/server/0022-Do-not-process-chat-commands-before-player-has-joine.patch rename to divinemc-archived-patches/removed/1.20/server/0022-Do-not-process-chat-commands-before-player-has-joine.patch diff --git a/patches/removed/1.20/server/0022-Paper-PR-Optimize-Varints.patch b/divinemc-archived-patches/removed/1.20/server/0022-Paper-PR-Optimize-Varints.patch similarity index 100% rename from patches/removed/1.20/server/0022-Paper-PR-Optimize-Varints.patch rename to divinemc-archived-patches/removed/1.20/server/0022-Paper-PR-Optimize-Varints.patch diff --git a/patches/removed/1.20/server/0023-Bump-Dependencies.patch b/divinemc-archived-patches/removed/1.20/server/0023-Bump-Dependencies.patch similarity index 100% rename from patches/removed/1.20/server/0023-Bump-Dependencies.patch rename to divinemc-archived-patches/removed/1.20/server/0023-Bump-Dependencies.patch diff --git a/patches/removed/1.20/server/0028-lithium-collections.attributes.patch b/divinemc-archived-patches/removed/1.20/server/0028-lithium-collections.attributes.patch similarity index 100% rename from patches/removed/1.20/server/0028-lithium-collections.attributes.patch rename to divinemc-archived-patches/removed/1.20/server/0028-lithium-collections.attributes.patch diff --git a/patches/removed/1.20/server/0028-lithium-collections.goals.patch b/divinemc-archived-patches/removed/1.20/server/0028-lithium-collections.goals.patch similarity index 100% rename from patches/removed/1.20/server/0028-lithium-collections.goals.patch rename to divinemc-archived-patches/removed/1.20/server/0028-lithium-collections.goals.patch diff --git a/patches/removed/1.20/server/0030-lithium-collections.attributes.patch b/divinemc-archived-patches/removed/1.20/server/0030-lithium-collections.attributes.patch similarity index 100% rename from patches/removed/1.20/server/0030-lithium-collections.attributes.patch rename to divinemc-archived-patches/removed/1.20/server/0030-lithium-collections.attributes.patch diff --git a/patches/removed/1.20/server/0031-lithium-gen.cached_generator_settings.patch b/divinemc-archived-patches/removed/1.20/server/0031-lithium-gen.cached_generator_settings.patch similarity index 100% rename from patches/removed/1.20/server/0031-lithium-gen.cached_generator_settings.patch rename to divinemc-archived-patches/removed/1.20/server/0031-lithium-gen.cached_generator_settings.patch diff --git a/patches/removed/1.20/server/0032-Replace-shape-full-block-cache-with-hashtable.patch b/divinemc-archived-patches/removed/1.20/server/0032-Replace-shape-full-block-cache-with-hashtable.patch similarity index 100% rename from patches/removed/1.20/server/0032-Replace-shape-full-block-cache-with-hashtable.patch rename to divinemc-archived-patches/removed/1.20/server/0032-Replace-shape-full-block-cache-with-hashtable.patch diff --git a/patches/server/0024-Boat-Settings.patch b/divinemc-archived-patches/removed/1.21.4/server/0024-Boat-Settings.patch similarity index 100% rename from patches/server/0024-Boat-Settings.patch rename to divinemc-archived-patches/removed/1.21.4/server/0024-Boat-Settings.patch diff --git a/patches/server/0026-Reduce-sensor-work.patch b/divinemc-archived-patches/removed/1.21.4/server/0026-Reduce-sensor-work.patch similarity index 100% rename from patches/server/0026-Reduce-sensor-work.patch rename to divinemc-archived-patches/removed/1.21.4/server/0026-Reduce-sensor-work.patch diff --git a/patches/server/0036-C2ME-opts-math.patch b/divinemc-archived-patches/removed/1.21.4/server/0036-C2ME-opts-math.patch similarity index 100% rename from patches/server/0036-C2ME-opts-math.patch rename to divinemc-archived-patches/removed/1.21.4/server/0036-C2ME-opts-math.patch diff --git a/patches/removed/1.21/api/0008-Add-Higher-Java-Version-for-Pufferfish.patch b/divinemc-archived-patches/removed/1.21/api/0008-Add-Higher-Java-Version-for-Pufferfish.patch similarity index 100% rename from patches/removed/1.21/api/0008-Add-Higher-Java-Version-for-Pufferfish.patch rename to divinemc-archived-patches/removed/1.21/api/0008-Add-Higher-Java-Version-for-Pufferfish.patch diff --git a/patches/removed/1.21/server/0005-Fix-MC-98160-and-MC-105103.patch b/divinemc-archived-patches/removed/1.21/server/0005-Fix-MC-98160-and-MC-105103.patch similarity index 100% rename from patches/removed/1.21/server/0005-Fix-MC-98160-and-MC-105103.patch rename to divinemc-archived-patches/removed/1.21/server/0005-Fix-MC-98160-and-MC-105103.patch diff --git a/patches/server/0009-Fix-MC-2025.patch b/divinemc-archived-patches/removed/1.21/server/0009-Fix-MC-2025.patch similarity index 100% rename from patches/server/0009-Fix-MC-2025.patch rename to divinemc-archived-patches/removed/1.21/server/0009-Fix-MC-2025.patch diff --git a/patches/server/0011-Fix-MC-93018.patch b/divinemc-archived-patches/removed/1.21/server/0011-Fix-MC-93018.patch similarity index 100% rename from patches/server/0011-Fix-MC-93018.patch rename to divinemc-archived-patches/removed/1.21/server/0011-Fix-MC-93018.patch diff --git a/patches/removed/1.21/server/0021-lithium-precompute-shape-arrays.patch b/divinemc-archived-patches/removed/1.21/server/0021-lithium-precompute-shape-arrays.patch similarity index 100% rename from patches/removed/1.21/server/0021-lithium-precompute-shape-arrays.patch rename to divinemc-archived-patches/removed/1.21/server/0021-lithium-precompute-shape-arrays.patch diff --git a/patches/removed/1.21/server/0022-lithium-cached_hashcode.patch b/divinemc-archived-patches/removed/1.21/server/0022-lithium-cached_hashcode.patch similarity index 100% rename from patches/removed/1.21/server/0022-lithium-cached_hashcode.patch rename to divinemc-archived-patches/removed/1.21/server/0022-lithium-cached_hashcode.patch diff --git a/patches/removed/1.21/server/0040-Carpet-Fixes-Sheep-Optimization.patch b/divinemc-archived-patches/removed/1.21/server/0040-Carpet-Fixes-Sheep-Optimization.patch similarity index 100% rename from patches/removed/1.21/server/0040-Carpet-Fixes-Sheep-Optimization.patch rename to divinemc-archived-patches/removed/1.21/server/0040-Carpet-Fixes-Sheep-Optimization.patch diff --git a/patches/removed/1.21/server/0049-Improve-biome-temperature-cache.patch b/divinemc-archived-patches/removed/1.21/server/0049-Improve-biome-temperature-cache.patch similarity index 100% rename from patches/removed/1.21/server/0049-Improve-biome-temperature-cache.patch rename to divinemc-archived-patches/removed/1.21/server/0049-Improve-biome-temperature-cache.patch diff --git a/patches/removed/1.21/server/0052-Add-Higher-Java-Version-for-Pufferfish-SIMD.patch b/divinemc-archived-patches/removed/1.21/server/0052-Add-Higher-Java-Version-for-Pufferfish-SIMD.patch similarity index 100% rename from patches/removed/1.21/server/0052-Add-Higher-Java-Version-for-Pufferfish-SIMD.patch rename to divinemc-archived-patches/removed/1.21/server/0052-Add-Higher-Java-Version-for-Pufferfish-SIMD.patch diff --git a/patches/removed/1.21/server/0057-Fix-MC-167242.patch b/divinemc-archived-patches/removed/1.21/server/0057-Fix-MC-167242.patch similarity index 100% rename from patches/removed/1.21/server/0057-Fix-MC-167242.patch rename to divinemc-archived-patches/removed/1.21/server/0057-Fix-MC-167242.patch diff --git a/divinemc-server/build.gradle.kts.patch b/divinemc-server/build.gradle.kts.patch new file mode 100644 index 0000000..a1103cb --- /dev/null +++ b/divinemc-server/build.gradle.kts.patch @@ -0,0 +1,111 @@ +--- a/purpur-server/build.gradle.kts ++++ b/purpur-server/build.gradle.kts +@@ -22,6 +_,7 @@ + // gitFilePatches = true + + val purpur = forks.register("purpur") { ++ rootDirectory = upstreamsDirectory().map { it.dir("purpur") } + upstream.patchDir("paperServer") { + upstreamPath = "paper-server" + excludes = setOf("src/minecraft", "patches", "build.gradle.kts") +@@ -29,7 +_,23 @@ + outputDir = rootDirectory.dir("paper-server") + } + } +- activeFork = purpur ++ ++ val divinemc = forks.register("divinemc") { ++ forks = purpur ++ upstream.patchRepo("paperServer") { ++ upstreamRepo = purpur.patchedRepo("paperServer") ++ patchesDir = rootDirectory.dir("divinemc-server/paper-patches") ++ outputDir = rootDirectory.dir("paper-server") ++ } ++ upstream.patchDir("purpurServer") { ++ upstreamPath = "purpur-server" ++ excludes = setOf("src/minecraft", "paper-patches", "minecraft-patches", "build.gradle.kts", "build.gradle.kts.patch") ++ patchesDir = rootDirectory.dir("divinemc-server/purpur-patches") ++ outputDir = rootDirectory.dir("purpur-server") ++ } ++ } ++ ++ activeFork = divinemc + + spigot { + buildDataRef = "3edaf46ec1eed4115ce1b18d2846cded42577e42" +@@ -115,10 +_,14 @@ + main { + java { srcDir("../paper-server/src/main/java") } + resources { srcDir("../paper-server/src/main/resources") } ++ java { srcDir("../purpur-server/src/main/java") } ++ resources { srcDir("../purpur-server/src/main/resources") } + } + test { + java { srcDir("../paper-server/src/test/java") } + resources { srcDir("../paper-server/src/test/resources") } ++ java { srcDir("../purpur-server/src/test/java") } ++ resources { srcDir("../purpur-server/src/test/resources") } + } + } + +@@ -142,7 +_,7 @@ + } + + dependencies { +- implementation(project(":purpur-api")) ++ implementation(project(":divinemc-api")) // DivineMC + implementation("ca.spottedleaf:concurrentutil:0.0.3") + implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ + implementation("org.jline:jline-terminal-jni:3.27.1") // fall back to jni on java 21 +@@ -176,6 +_,9 @@ + implementation("org.mozilla:rhino-engine:1.7.14") // Purpur + implementation("dev.omega24:upnp4j:1.0") // Purpur + ++ implementation("com.github.luben:zstd-jni:1.5.6-9") // DivineMC ++ implementation("org.lz4:lz4-java:1.8.0") // DivineMC ++ + runtimeOnly("org.apache.maven:maven-resolver-provider:3.9.6") + runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18") + runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18") +@@ -203,26 +_,35 @@ + implementation("me.lucko:spark-paper:1.10.119-SNAPSHOT") + } + ++// DivineMC start - hide irrelevant compilation warnings ++tasks.withType { ++ val compilerArgs = options.compilerArgs ++ compilerArgs.add("-Xlint:-module") ++ compilerArgs.add("-Xlint:-removal") ++ compilerArgs.add("-Xlint:-dep-ann") ++} ++// DivineMC end - hide irrelevant compilation warnings ++ + tasks.jar { + manifest { + val git = Git(rootProject.layout.projectDirectory.path) + val mcVersion = rootProject.providers.gradleProperty("mcVersion").get() + val build = System.getenv("BUILD_NUMBER") ?: null +- val buildTime = if (build != null) Instant.now() else Instant.EPOCH ++ val buildTime = Instant.now() // DivineMC - Build time to current, we dont have a build server rn + val gitHash = git.exec(providers, "rev-parse", "--short=7", "HEAD").get().trim() + val implementationVersion = "$mcVersion-${build ?: "DEV"}-$gitHash" + val date = git.exec(providers, "show", "-s", "--format=%ci", gitHash).get().trim() + val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim() + attributes( + "Main-Class" to "org.bukkit.craftbukkit.Main", +- "Implementation-Title" to "Purpur", // Purpur ++ "Implementation-Title" to "DivineMC", // DivineMC + "Implementation-Version" to implementationVersion, + "Implementation-Vendor" to date, +- "Specification-Title" to "Purpur", // Purpur ++ "Specification-Title" to "DivineMC", // DivineMC + "Specification-Version" to project.version, +- "Specification-Vendor" to "Purpur Team", // Purpur +- "Brand-Id" to "purpurmc:purpur", // Purpur +- "Brand-Name" to "Purpur", // Purpur ++ "Specification-Vendor" to "BX Team", // DivineMC ++ "Brand-Id" to "divinemc:divinemc", // DivineMC ++ "Brand-Name" to "DivineMC", // DivineMC + "Build-Number" to (build ?: ""), + "Build-Time" to buildTime.toString(), + "Git-Branch" to gitBranch, diff --git a/divinemc-server/minecraft-patches/features/0001-Rebrand.patch b/divinemc-server/minecraft-patches/features/0001-Rebrand.patch new file mode 100644 index 0000000..f4e6ae1 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0001-Rebrand.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sat, 11 Jan 2025 22:22:01 +0300 +Subject: [PATCH] Rebrand + + +diff --git a/net/minecraft/CrashReport.java b/net/minecraft/CrashReport.java +index 394443d00e661715439be1e56dddc129947699a4..480ad57a6b7b74e6b83e9c6ceb69ea1feacca106 100644 +--- a/net/minecraft/CrashReport.java ++++ b/net/minecraft/CrashReport.java +@@ -30,7 +30,7 @@ public class CrashReport { + private boolean trackingStackTrace = true; + private StackTraceElement[] uncategorizedStackTrace = new StackTraceElement[0]; + private final SystemReport systemReport = new SystemReport(); +- private List extraInfo = List.of("", "DO NOT REPORT THIS TO PAPER! REPORT TO PURPUR INSTEAD!", ""); // Purpur - Rebrand ++ private List extraInfo = List.of("", "DO NOT REPORT THIS TO PAPER! REPORT TO DIVINEMC INSTEAD!", ""); // DivineMC - Rebrand + + public CrashReport(String title, Throwable exception) { + io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(exception); // Paper +diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +index 80ed0e4b8c867d031413b4140e52af1342fdcb54..6ebd1300c2561116b83cb2472ac7939ead36d576 100644 +--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java ++++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +@@ -283,7 +283,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + + // Paper start + private static void printOversizedLog(String msg, Path file, int x, int z) { +- 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 PURPUR - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); // Purpur - Rebrand ++ 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 DIVINEMC - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); // DivineMC - Rebrand + } + + private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { diff --git a/divinemc-server/minecraft-patches/features/0002-DivineMC-Configuration.patch b/divinemc-server/minecraft-patches/features/0002-DivineMC-Configuration.patch new file mode 100644 index 0000000..54bd755 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0002-DivineMC-Configuration.patch @@ -0,0 +1,48 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sun, 12 Jan 2025 16:19:14 +0300 +Subject: [PATCH] DivineMC Configuration + + +diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java +index 2d7493122db9067ecbc9ca66cc62e7df4ae78a90..c85023893fbb227894f8f888244f1f53e25dff3f 100644 +--- a/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/net/minecraft/server/dedicated/DedicatedServer.java +@@ -243,6 +243,17 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + } + org.purpurmc.purpur.PurpurConfig.registerCommands(); + // Purpur end - Purpur config files ++ ++ // DivineMC start - DivineMC configuration ++ try { ++ space.bxteam.divinemc.configuration.DivineConfig.init((java.io.File) options.valueOf("divinemc-settings")); ++ } catch (Exception e) { ++ DedicatedServer.LOGGER.error("Unable to load server configuration", e); ++ return false; ++ } ++ space.bxteam.divinemc.command.DivineCommands.registerCommands(this); // DivineMC - register commands ++ // DivineMC end - DivineMC configuration ++ + com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // Paper - load version history now + + this.setPvpAllowed(properties.pvp); +diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java +index 1b2d152649bc12b37db1cd7a4f54517f417d46e8..71c4c79d94473a95479bc73cfb7415e1813970de 100644 +--- a/net/minecraft/world/level/Level.java ++++ b/net/minecraft/world/level/Level.java +@@ -171,6 +171,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + + public final io.papermc.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray + public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur - Purpur config files ++ public final space.bxteam.divinemc.configuration.DivineWorldConfig divinemcConfig; // DivineMC - DivineMC config files + public static BlockPos lastPhysicsProblem; // Spigot + private int tileTickPosition; + public final Map explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions +@@ -896,6 +897,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName()); // Spigot + this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config + this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName(), env); // Purpur - Purpur config files ++ this.divinemcConfig = new space.bxteam.divinemc.configuration.DivineWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) levelData).getLevelName(), env); // DivineMC - DivineMC configuration + this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); // Purpur - Add adjustable breeding cooldown to config + this.generator = gen; + this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); diff --git a/divinemc-server/minecraft-patches/features/0003-Add-missing-purpur-config-options.patch b/divinemc-server/minecraft-patches/features/0003-Add-missing-purpur-config-options.patch new file mode 100644 index 0000000..01012c1 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0003-Add-missing-purpur-config-options.patch @@ -0,0 +1,183 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sun, 12 Jan 2025 00:14:45 +0300 +Subject: [PATCH] Add missing purpur config options + + +diff --git a/net/minecraft/world/entity/animal/allay/Allay.java b/net/minecraft/world/entity/animal/allay/Allay.java +index 22e0fad86da2e7b932863ef30182355aa41424a1..d4eee24b5ab89f82e4e90f551d6651330d4508cb 100644 +--- a/net/minecraft/world/entity/animal/allay/Allay.java ++++ b/net/minecraft/world/entity/animal/allay/Allay.java +@@ -181,6 +181,18 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS + } + // Purpur end - Configurable entity base attributes + ++ // DivineMC start - Add missing purpur config options ++ @Override ++ public boolean isSensitiveToWater() { ++ return level().purpurConfig.allayTakeDamageFromWater; ++ } ++ ++ @Override ++ public boolean isAlwaysExperienceDropper() { ++ return level().purpurConfig.allayAlwaysDropExp; ++ } ++ // DivineMC end - Add missing purpur config options ++ + @Override + protected Brain.Provider brainProvider() { + return Brain.provider(MEMORY_TYPES, SENSOR_TYPES); +diff --git a/net/minecraft/world/entity/animal/camel/Camel.java b/net/minecraft/world/entity/animal/camel/Camel.java +index 1d7ae2a08968860636918e7c66b60139a9d761b4..126af60a694600d40e3ae6bd39e94e55dd9d0b6e 100644 +--- a/net/minecraft/world/entity/animal/camel/Camel.java ++++ b/net/minecraft/world/entity/animal/camel/Camel.java +@@ -97,6 +97,18 @@ public class Camel extends AbstractHorse { + } + // Purpur end - Make entity breeding times configurable + ++ // DivineMC start - Add missing purpur config options ++ @Override ++ public boolean isSensitiveToWater() { ++ return level().purpurConfig.camelTakeDamageFromWater; ++ } ++ ++ @Override ++ public boolean isAlwaysExperienceDropper() { ++ return level().purpurConfig.camelAlwaysDropExp; ++ } ++ // DivineMC end - Add missing purpur config options ++ + @Override + public void addAdditionalSaveData(CompoundTag compound) { + super.addAdditionalSaveData(compound); +diff --git a/net/minecraft/world/entity/animal/frog/Frog.java b/net/minecraft/world/entity/animal/frog/Frog.java +index c4ea9485294b7dec2582c638802f003ad70659b6..d286d4a45b6c8d5c684ad11500d2ad1a10a70c18 100644 +--- a/net/minecraft/world/entity/animal/frog/Frog.java ++++ b/net/minecraft/world/entity/animal/frog/Frog.java +@@ -165,6 +165,23 @@ public class Frog extends Animal implements VariantHolder> { + } + // Purpur end - Ridables + ++ // DivineMC start - Add missing purpur config options ++ @Override ++ public boolean isSensitiveToWater() { ++ return level().purpurConfig.frogTakeDamageFromWater; ++ } ++ ++ @Override ++ public boolean isAlwaysExperienceDropper() { ++ return level().purpurConfig.frogAlwaysDropExp; ++ } ++ ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(level().purpurConfig.frogMaxHealth); ++ } ++ // DivineMC end ++ + // Purpur start - Make entity breeding times configurable + @Override + public int getPurpurBreedTime() { +diff --git a/net/minecraft/world/entity/animal/frog/Tadpole.java b/net/minecraft/world/entity/animal/frog/Tadpole.java +index e888e606b4b14fa6485de7426bc146b6005962af..a4383a7d41c91f9e478c7e221828ba92437af06c 100644 +--- a/net/minecraft/world/entity/animal/frog/Tadpole.java ++++ b/net/minecraft/world/entity/animal/frog/Tadpole.java +@@ -107,6 +107,23 @@ public class Tadpole extends AbstractFish { + } + // Purpur end - Ridables + ++ // DivineMC start - Add missing purpur config options ++ @Override ++ public boolean isSensitiveToWater() { ++ return level().purpurConfig.tadpoleTakeDamageFromWater; ++ } ++ ++ @Override ++ public boolean isAlwaysExperienceDropper() { ++ return level().purpurConfig.tadpoleAlwaysDropExp; ++ } ++ ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(level().purpurConfig.tadpoleMaxHealth); ++ } ++ // DivineMC end - Add missing purpur config options ++ + @Override + protected PathNavigation createNavigation(Level level) { + return new WaterBoundPathNavigation(this, level); +diff --git a/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/net/minecraft/world/entity/animal/sniffer/Sniffer.java +index 11a5da22149a61ca48bbb0a8ed10b71e91a5cc98..ed1ffc1578e50fa6fedc6124fe016a1535c0e968 100644 +--- a/net/minecraft/world/entity/animal/sniffer/Sniffer.java ++++ b/net/minecraft/world/entity/animal/sniffer/Sniffer.java +@@ -120,6 +120,18 @@ public class Sniffer extends Animal { + } + // Purpur end - Make entity breeding times configurable + ++ // DivineMC start - Add missing purpur config options ++ @Override ++ public boolean isSensitiveToWater() { ++ return level().purpurConfig.snifferTakeDamageFromWater; ++ } ++ ++ @Override ++ public boolean isAlwaysExperienceDropper() { ++ return level().purpurConfig.snifferAlwaysDropExp; ++ } ++ // DivineMC end ++ + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + super.defineSynchedData(builder); +diff --git a/net/minecraft/world/entity/monster/warden/Warden.java b/net/minecraft/world/entity/monster/warden/Warden.java +index f968e5c99bdb23b268bc34ea1ba5d54ae9ad0ff9..f74c784906208034f51b31bd9aba45733c3ebebe 100644 +--- a/net/minecraft/world/entity/monster/warden/Warden.java ++++ b/net/minecraft/world/entity/monster/warden/Warden.java +@@ -155,6 +155,23 @@ public class Warden extends Monster implements VibrationSystem { + } + // Purpur end - Ridables + ++ // DivineMC start - Add missing purpur config options ++ @Override ++ public boolean isSensitiveToWater() { ++ return level().purpurConfig.wardenTakeDamageFromWater; ++ } ++ ++ @Override ++ public boolean isAlwaysExperienceDropper() { ++ return level().purpurConfig.wardenAlwaysDropExp; ++ } ++ ++ @Override ++ public void initAttributes() { ++ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(level().purpurConfig.wardenMaxHealth); ++ } ++ // DivineMC end ++ + @Override + public Packet getAddEntityPacket(ServerEntity entity) { + return new ClientboundAddEntityPacket(this, entity, this.hasPose(Pose.EMERGING) ? 1 : 0); +diff --git a/net/minecraft/world/entity/vehicle/AbstractChestBoat.java b/net/minecraft/world/entity/vehicle/AbstractChestBoat.java +index b230955ae880d84fde40b4feffa5caf3c4449eb7..5b88c69427d5915ff547e4caf7b5656e96912e93 100644 +--- a/net/minecraft/world/entity/vehicle/AbstractChestBoat.java ++++ b/net/minecraft/world/entity/vehicle/AbstractChestBoat.java +@@ -26,8 +26,8 @@ import net.minecraft.world.level.gameevent.GameEvent; + import net.minecraft.world.level.storage.loot.LootTable; + + public abstract class AbstractChestBoat extends AbstractBoat implements HasCustomInventoryScreen, ContainerEntity { +- private static final int CONTAINER_SIZE = 27; +- private NonNullList itemStacks = NonNullList.withSize(27, ItemStack.EMPTY); ++ private static final int CONTAINER_SIZE = org.purpurmc.purpur.PurpurConfig.chestBoatRows * 9; // DivineMC - Add missing purpur config options ++ private NonNullList itemStacks = NonNullList.withSize(org.purpurmc.purpur.PurpurConfig.chestBoatRows * 9, ItemStack.EMPTY); // DivineMC - Add missing purpur config options + @Nullable + private ResourceKey lootTable; + private long lootTableSeed; +@@ -118,7 +118,7 @@ public abstract class AbstractChestBoat extends AbstractBoat implements HasCusto + + @Override + public int getContainerSize() { +- return 27; ++ return org.purpurmc.purpur.PurpurConfig.chestBoatRows * 9; // DivineMC - Add missing purpur config options + } + + @Override diff --git a/patches/server/0057-lithium-fast_util.patch b/divinemc-server/minecraft-patches/features/0004-lithium-fast_util.patch similarity index 71% rename from patches/server/0057-lithium-fast_util.patch rename to divinemc-server/minecraft-patches/features/0004-lithium-fast_util.patch index 125f1f9..7abdd99 100644 --- a/patches/server/0057-lithium-fast_util.patch +++ b/divinemc-server/minecraft-patches/features/0004-lithium-fast_util.patch @@ -1,15 +1,13 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 16 Dec 2024 20:52:48 +0300 +Date: Sun, 12 Jan 2025 15:27:57 +0300 Subject: [PATCH] lithium: fast_util -Original code by CaffeineMC, licensed under GNU Lesser General Public License v3.0 -You can find the original code on https://github.com/CaffeineMC/lithium-fabric (Yarn mappings) -diff --git a/src/main/java/net/minecraft/core/Direction.java b/src/main/java/net/minecraft/core/Direction.java -index 928f38fd6beb00753c92ae9f4678f7507519a39b..4c53995d6d173ff6f544d52f11b675015efde26e 100644 ---- a/src/main/java/net/minecraft/core/Direction.java -+++ b/src/main/java/net/minecraft/core/Direction.java +diff --git a/net/minecraft/core/Direction.java b/net/minecraft/core/Direction.java +index 216f97207dac88cc1dc3df59c6ee8a62c7614b4a..05c7de5729466786a0196fa5f91eccc3cfffc675 100644 +--- a/net/minecraft/core/Direction.java ++++ b/net/minecraft/core/Direction.java @@ -217,7 +217,7 @@ public enum Direction implements StringRepresentable, ca.spottedleaf.moonrise.pa } @@ -28,10 +26,10 @@ index 928f38fd6beb00753c92ae9f4678f7507519a39b..4c53995d6d173ff6f544d52f11b67501 } public static Direction getApproximateNearest(double x, double y, double z) { -diff --git a/src/main/java/net/minecraft/world/phys/AABB.java b/src/main/java/net/minecraft/world/phys/AABB.java -index e74866e5195a5eeae7666ad7be750edac5947094..25d7e7116282ec4b2cf85fe0b8e920af77fe4206 100644 ---- a/src/main/java/net/minecraft/world/phys/AABB.java -+++ b/src/main/java/net/minecraft/world/phys/AABB.java +diff --git a/net/minecraft/world/phys/AABB.java b/net/minecraft/world/phys/AABB.java +index c9c6e4e460ad8435f12761704bb9b0284d6aa708..9581e7d51edaa6c51538a5267f421b034f4bfff0 100644 +--- a/net/minecraft/world/phys/AABB.java ++++ b/net/minecraft/world/phys/AABB.java @@ -18,6 +18,15 @@ public class AABB { public final double maxY; public final double maxZ; @@ -43,12 +41,12 @@ index e74866e5195a5eeae7666ad7be750edac5947094..25d7e7116282ec4b2cf85fe0b8e920af + assert Direction.Axis.Z.ordinal() == 2; + assert Direction.Axis.values().length == 3; + } -+ // DivineMC end ++ // DivineMC end - lithium: fast_util + public AABB(double x1, double y1, double z1, double x2, double y2, double z2) { this.minX = Math.min(x1, x2); this.minY = Math.min(y1, y2); -@@ -86,11 +95,33 @@ public class AABB { +@@ -79,11 +88,33 @@ public class AABB { } public double min(Direction.Axis axis) { @@ -64,7 +62,7 @@ index e74866e5195a5eeae7666ad7be750edac5947094..25d7e7116282ec4b2cf85fe0b8e920af + } + + throw new IllegalArgumentException(); -+ // DivineMC end ++ // DivineMC end - lithium: fast_util } public double max(Direction.Axis axis) { @@ -80,7 +78,7 @@ index e74866e5195a5eeae7666ad7be750edac5947094..25d7e7116282ec4b2cf85fe0b8e920af + } + + throw new IllegalArgumentException(); -+ // DivineMC end ++ // DivineMC end - lithium: fast_util } @Override diff --git a/patches/server/0038-Petal-Reduce-work-done-by-game-event-system.patch b/divinemc-server/minecraft-patches/features/0005-Petal-Reduce-work-done-by-game-event-system.patch similarity index 52% rename from patches/server/0038-Petal-Reduce-work-done-by-game-event-system.patch rename to divinemc-server/minecraft-patches/features/0005-Petal-Reduce-work-done-by-game-event-system.patch index 9f833a0..08ce160 100644 --- a/patches/server/0038-Petal-Reduce-work-done-by-game-event-system.patch +++ b/divinemc-server/minecraft-patches/features/0005-Petal-Reduce-work-done-by-game-event-system.patch @@ -1,26 +1,26 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Thu, 11 Jul 2024 19:28:20 +0300 +Date: Sun, 12 Jan 2025 20:57:25 +0300 Subject: [PATCH] Petal: Reduce work done by game event system Original code by Bloom-host, licensed under GPL v3 You can find the original code on https://github.com/Bloom-host/Petal -diff --git a/src/main/java/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java -index 4729befa12732a9fd65cce243b33b3b479026c41..4f63d2d9e37be27724eddea2c61f681ceb318a97 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java -@@ -68,7 +68,7 @@ public class SculkCatalystBlockEntity extends BlockEntity implements GameEventLi +diff --git a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java +index 1638eccef431fb68775af624110f1968f0c6dabd..2799db1223c9f279322869bdd19605e6d835af2e 100644 +--- a/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/SculkCatalystBlockEntity.java +@@ -65,7 +65,7 @@ public class SculkCatalystBlockEntity extends BlockEntity implements GameEventLi return this.catalystListener; } - public static class CatalystListener implements GameEventListener { + public class CatalystListener implements GameEventListener { // DivineMC - static -> non-static - public static final int PULSE_TICKS = 8; final SculkSpreader sculkSpreader; -@@ -139,6 +139,13 @@ public class SculkCatalystBlockEntity extends BlockEntity implements GameEventLi - world.playSound((Player) null, pos, SoundEvents.SCULK_CATALYST_BLOOM, SoundSource.BLOCKS, 2.0F, 0.6F + random.nextFloat() * 0.4F); + private final BlockState blockState; +@@ -127,6 +127,13 @@ public class SculkCatalystBlockEntity extends BlockEntity implements GameEventLi + level.playSound(null, pos, SoundEvents.SCULK_CATALYST_BLOOM, SoundSource.BLOCKS, 2.0F, 0.6F + random.nextFloat() * 0.4F); } + // DivineMC start - Petal: Reduce work done by game event system @@ -28,16 +28,16 @@ index 4729befa12732a9fd65cce243b33b3b479026c41..4f63d2d9e37be27724eddea2c61f681c + public boolean listensToEvent(GameEvent gameEvent, GameEvent.Context context) { + return !SculkCatalystBlockEntity.this.isRemoved() && gameEvent == GameEvent.ENTITY_DIE.value() && context.sourceEntity() instanceof LivingEntity; + } -+ // DivineMC end ++ // DivineMC end - Petal: Reduce work done by game event system + - private void tryAwardItSpreadsAdvancement(Level world, LivingEntity deadEntity) { - LivingEntity entityliving1 = deadEntity.getLastHurtByMob(); - -diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -index 00ea1c2037c7c7780764bfcc3e07b6554e910db2..32d64bdb475fdaa5aba0e9082c6ee6655f38fa1e 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java -@@ -84,7 +84,18 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + private void tryAwardItSpreadsAdvancement(Level level, LivingEntity entity) { + if (entity.getLastHurtByMob() instanceof ServerPlayer serverPlayer) { + DamageSource damageSource = entity.getLastDamageSource() == null +diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java +index 761fdcd4a4e18f45547afd8edff44f61c6eeacb4..03264d2b170002c640cec34530909ebb34c7e54e 100644 +--- a/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/net/minecraft/world/level/chunk/LevelChunk.java +@@ -81,7 +81,18 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p private Supplier fullStatus; @Nullable private LevelChunk.PostLoadProcessor postLoad; @@ -53,28 +53,32 @@ index 00ea1c2037c7c7780764bfcc3e07b6554e910db2..32d64bdb475fdaa5aba0e9082c6ee665 + private static int getGameEventSectionLength(int sectionCount) { + return sectionCount + (GAME_EVENT_DISPATCHER_RADIUS * 2); + } -+ // DivineMC end ++ // DivineMC end - Petal: Reduce work done by game event system private final LevelChunkTicks blockTicks; private final LevelChunkTicks fluidTicks; - private LevelChunk.UnsavedListener unsavedListener; -@@ -111,7 +122,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p - this.unsavedListener = (chunkcoordintpair1) -> { - }; - this.level = (ServerLevel) world; // CraftBukkit - type -- this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap(); + private LevelChunk.UnsavedListener unsavedListener = chunkPos -> {}; +@@ -144,7 +155,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + ) { + super(pos, data, level, net.minecraft.server.MinecraftServer.getServer().registryAccess().lookupOrThrow(Registries.BIOME), inhabitedTime, sections, blendingData); // Paper - Anti-Xray - The world isn't ready yet, use server singleton for registry + this.level = (ServerLevel) level; // CraftBukkit - type +- this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap<>(); + this.gameEventListenerRegistrySections = new GameEventListenerRegistry[getGameEventSectionLength(this.getSectionsCount())]; // DivineMC - Petal: Reduce work done by game event system - Heightmap.Types[] aheightmap_type = Heightmap.Types.values(); - int j = aheightmap_type.length; -@@ -273,9 +284,23 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p - Level world = this.level; + for (Heightmap.Types types : Heightmap.Types.values()) { + if (ChunkStatus.FULL.heightmapsAfter().contains(types)) { +@@ -254,10 +265,29 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p - if (world instanceof ServerLevel worldserver) { -- return (GameEventListenerRegistry) this.gameEventListenerRegistrySections.computeIfAbsent(ySectionCoord, (j) -> { -- return new EuclideanGameEventListenerRegistry(worldserver, ySectionCoord, this::removeGameEventListenerRegistry); -- }); + @Override + public GameEventListenerRegistry getListenerRegistry(int sectionY) { +- return this.level instanceof ServerLevel serverLevel +- ? this.gameEventListenerRegistrySections +- .computeIfAbsent(sectionY, i -> new EuclideanGameEventListenerRegistry(serverLevel, sectionY, this::removeGameEventListenerRegistry)) +- : super.getListenerRegistry(sectionY); ++ Level world = this.level; ++ ++ if (world instanceof ServerLevel worldserver) { + // DivineMC start - Petal: Reduce work done by game event system -+ int sectionIndex = getGameEventSectionIndex(this.getSectionIndexFromSectionY(ySectionCoord)); ++ int sectionIndex = getGameEventSectionIndex(this.getSectionIndexFromSectionY(sectionY)); + + // drop game events that are too far away (32 blocks) from loaded sections + // this matches the highest radius of game events in the game @@ -85,35 +89,38 @@ index 00ea1c2037c7c7780764bfcc3e07b6554e910db2..32d64bdb475fdaa5aba0e9082c6ee665 + var dispatcher = this.gameEventListenerRegistrySections[sectionIndex]; + + if (dispatcher == null) { -+ dispatcher = this.gameEventListenerRegistrySections[sectionIndex] = new EuclideanGameEventListenerRegistry(worldserver, ySectionCoord, this::removeGameEventListenerRegistry); ++ dispatcher = this.gameEventListenerRegistrySections[sectionIndex] = new EuclideanGameEventListenerRegistry(worldserver, sectionY, this::removeGameEventListenerRegistry); + } + + return dispatcher; -+ // DivineMC end - } else { - return super.getListenerRegistry(ySectionCoord); - } -@@ -671,7 +696,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p ++ // DivineMC end - Petal: Reduce work done by game event system ++ } else { ++ return super.getListenerRegistry(sectionY); ++ } } - private void removeGameEventListenerRegistry(int ySectionCoord) { -- this.gameEventListenerRegistrySections.remove(ySectionCoord); -+ this.gameEventListenerRegistrySections[getGameEventSectionIndex(this.getSectionIndexFromSectionY(ySectionCoord))] = null; // DivineMC - Petal: Reduce work done by game event system + // Paper start - Perf: Reduce instructions and provide final method +@@ -601,7 +631,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + } + + private void removeGameEventListenerRegistry(int sectionY) { +- this.gameEventListenerRegistrySections.remove(sectionY); ++ this.gameEventListenerRegistrySections[getGameEventSectionIndex(this.getSectionIndexFromSectionY(sectionY))] = null; // DivineMC - Petal: Reduce work done by game event system } private void removeBlockEntityTicker(BlockPos pos) { -diff --git a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java -index 1e93f3a1b11196a431a1f5a0957036fe0c9191a4..a28b48676b459e8cfa0c3ef2d4ad6de55fc91d66 100644 ---- a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java -+++ b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java +diff --git a/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java b/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java +index 5175fc90a1fc61c832c6697997a97ae199b195ac..f40511b5be6cf72298d30188fd550d1d768d875a 100644 +--- a/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java ++++ b/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java @@ -14,8 +14,8 @@ import net.minecraft.world.phys.Vec3; public class EuclideanGameEventListenerRegistry implements GameEventListenerRegistry { private final List listeners = Lists.newArrayList(); - private final Set listenersToRemove = Sets.newHashSet(); - private final List listenersToAdd = Lists.newArrayList(); -+ //private final Set listenersToRemove = Sets.newHashSet(); // DivineMC - unused -+ //private final List listenersToAdd = Lists.newArrayList(); // DivineMC - unused ++ //private final Set listenersToRemove = Sets.newHashSet(); // DivineMC - Commented out ++ //private final List listenersToAdd = Lists.newArrayList(); // DivineMC - Commented out private boolean processing; private final ServerLevel level; private final int sectionY; @@ -143,7 +150,7 @@ index 1e93f3a1b11196a431a1f5a0957036fe0c9191a4..a28b48676b459e8cfa0c3ef2d4ad6de5 + if (false) { // DivineMC - Disallow concurrent modification iterator.remove(); } else { - Optional optional = getPostableListenerPosition(this.level, pos, gameEventListener); + Optional postableListenerPosition = getPostableListenerPosition(this.level, pos, gameEventListener); @@ -80,6 +80,7 @@ public class EuclideanGameEventListenerRegistry implements GameEventListenerRegi this.processing = false; } @@ -156,26 +163,26 @@ index 1e93f3a1b11196a431a1f5a0957036fe0c9191a4..a28b48676b459e8cfa0c3ef2d4ad6de5 this.listeners.removeAll(this.listenersToRemove); this.listenersToRemove.clear(); } -+ */ // DivineMC end ++ */ // DivineMC end - Petal: Reduce work done by game event system - return bl; + return flag; } -diff --git a/src/main/java/net/minecraft/world/level/gameevent/GameEventDispatcher.java b/src/main/java/net/minecraft/world/level/gameevent/GameEventDispatcher.java -index df6c97be1b278c97a20390be5d3e60f429383702..a99253227677f6d68cbb2d79ea847d36e5879175 100644 ---- a/src/main/java/net/minecraft/world/level/gameevent/GameEventDispatcher.java -+++ b/src/main/java/net/minecraft/world/level/gameevent/GameEventDispatcher.java -@@ -45,6 +45,7 @@ public class GameEventDispatcher { - int k1 = SectionPos.blockToSectionCoord(blockposition.getZ() + i); - List list = new ArrayList(); - GameEventListenerRegistry.ListenerVisitor gameeventlistenerregistry_a = (gameeventlistener, vec3d1) -> { -+ if (!gameeventlistener.listensToEvent(event.value(), emitter)) return; // DivineMC - If they don't listen, ignore - if (gameeventlistener.getDeliveryMode() == GameEventListener.DeliveryMode.BY_DISTANCE) { - list.add(new GameEvent.ListenerInfo(event, emitterPos, emitter, gameeventlistener, vec3d1)); +diff --git a/net/minecraft/world/level/gameevent/GameEventDispatcher.java b/net/minecraft/world/level/gameevent/GameEventDispatcher.java +index 1e9b066ef468ae840eda3c1f6c4b68111a5e862c..cf31dadc73c66e4d4fcca9d81d587480d1c530c5 100644 +--- a/net/minecraft/world/level/gameevent/GameEventDispatcher.java ++++ b/net/minecraft/world/level/gameevent/GameEventDispatcher.java +@@ -44,6 +44,7 @@ public class GameEventDispatcher { + int sectionPosCoord5 = SectionPos.blockToSectionCoord(blockPos.getZ() + notificationRadius); + List list = new ArrayList<>(); + GameEventListenerRegistry.ListenerVisitor listenerVisitor = (listener, pos1) -> { ++ if (!listener.listensToEvent(gameEvent.value(), context)) return; // DivineMC - If they don't listen, ignore + if (listener.getDeliveryMode() == GameEventListener.DeliveryMode.BY_DISTANCE) { + list.add(new GameEvent.ListenerInfo(gameEvent, pos, context, listener, pos1)); } else { -diff --git a/src/main/java/net/minecraft/world/level/gameevent/GameEventListener.java b/src/main/java/net/minecraft/world/level/gameevent/GameEventListener.java -index 0f3a79cc644a5b89fa35fdd413ff5781987c4ef3..57e6902990e163ff80b83df30d64e5959ac9d585 100644 ---- a/src/main/java/net/minecraft/world/level/gameevent/GameEventListener.java -+++ b/src/main/java/net/minecraft/world/level/gameevent/GameEventListener.java +diff --git a/net/minecraft/world/level/gameevent/GameEventListener.java b/net/minecraft/world/level/gameevent/GameEventListener.java +index 5a31b5f1e75dd7b412ab577ea6621b7e87fc0590..32a79d5a247ddf953d18594897c5e9565353593a 100644 +--- a/net/minecraft/world/level/gameevent/GameEventListener.java ++++ b/net/minecraft/world/level/gameevent/GameEventListener.java @@ -23,4 +23,10 @@ public interface GameEventListener { public interface Provider { T getListener(); @@ -185,5 +192,5 @@ index 0f3a79cc644a5b89fa35fdd413ff5781987c4ef3..57e6902990e163ff80b83df30d64e595 + default boolean listensToEvent(GameEvent gameEvent, GameEvent.Context context) { + return true; + } -+ // DivineMC end ++ // DivineMC end - Add check for seeing if this listener cares about an event } diff --git a/divinemc-server/minecraft-patches/features/0006-Fix-MC-65198.patch b/divinemc-server/minecraft-patches/features/0006-Fix-MC-65198.patch new file mode 100644 index 0000000..3ef08f2 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0006-Fix-MC-65198.patch @@ -0,0 +1,65 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sun, 12 Jan 2025 21:03:27 +0300 +Subject: [PATCH] Fix MC-65198 + +Original post on Mojira: https://bugs.mojang.com/browse/MC-65198 + +diff --git a/net/minecraft/world/inventory/ItemCombinerMenu.java b/net/minecraft/world/inventory/ItemCombinerMenu.java +index c605bd700fd9f5a6596a2bf9648492786306b025..ae594513799defffa3d3c29ef753fa01337e0df2 100644 +--- a/net/minecraft/world/inventory/ItemCombinerMenu.java ++++ b/net/minecraft/world/inventory/ItemCombinerMenu.java +@@ -120,6 +120,7 @@ public abstract class ItemCombinerMenu extends AbstractContainerMenu { + if (slot != null && slot.hasItem()) { + ItemStack item = slot.getItem(); + itemStack = item.copy(); ++ ItemStack itemStack2 = itemStack.copy(); // DivineMC - Fix MC-65198 + int inventorySlotStart = this.getInventorySlotStart(); + int useRowEnd = this.getUseRowEnd(); + if (index == this.getResultSlot()) { +@@ -157,7 +158,7 @@ public abstract class ItemCombinerMenu extends AbstractContainerMenu { + } + + this.activeQuickItem = itemStack; // Purpur - Anvil API +- slot.onTake(player, item); ++ slot.onTake(player, itemStack2); // DivineMC - Fix MC-65198 + this.activeQuickItem = null; // Purpur - Anvil API + } + +diff --git a/net/minecraft/world/inventory/ResultSlot.java b/net/minecraft/world/inventory/ResultSlot.java +index 01b8d73b1be9b41d6f51d11a0bead37a7bd9023f..d3865b375293e29162f08aa447bd91f90ef27513 100644 +--- a/net/minecraft/world/inventory/ResultSlot.java ++++ b/net/minecraft/world/inventory/ResultSlot.java +@@ -49,7 +49,7 @@ public class ResultSlot extends Slot { + @Override + protected void checkTakeAchievements(ItemStack stack) { + if (this.removeCount > 0) { +- stack.onCraftedBy(this.player.level(), this.player, this.removeCount); ++ stack.onCraftedBy(this.player.level(), this.player, stack.getCount()); // DivineMC - Fix MC-65198 + } + + if (this.container instanceof RecipeCraftingHolder recipeCraftingHolder) { +diff --git a/net/minecraft/world/inventory/StonecutterMenu.java b/net/minecraft/world/inventory/StonecutterMenu.java +index d6854d0ebe5cb4205963e879d71eb3940d54de1f..dbc4ca21120daf3a63750274db8cff671d12fcd2 100644 +--- a/net/minecraft/world/inventory/StonecutterMenu.java ++++ b/net/minecraft/world/inventory/StonecutterMenu.java +@@ -238,6 +238,7 @@ public class StonecutterMenu extends AbstractContainerMenu { + ItemStack item = slot.getItem(); + Item item1 = item.getItem(); + itemStack = item.copy(); ++ ItemStack itemStack2 = itemStack.copy(); // DivineMC - Fix MC-65198 + if (index == 1) { + item1.onCraftedBy(item, player.level(), player); + if (!this.moveItemStackTo(item, 2, 38, true)) { +@@ -270,9 +271,9 @@ public class StonecutterMenu extends AbstractContainerMenu { + return ItemStack.EMPTY; + } + +- slot.onTake(player, item); ++ slot.onTake(player, itemStack2); // DivineMC - Fix MC-65198 + if (index == 1) { +- player.drop(item, false); ++ player.drop(itemStack2, false); // DivineMC - Fix MC-65198 + } + + this.broadcastChanges(); diff --git a/divinemc-server/minecraft-patches/features/0007-C2ME-Optimize-world-gen-math.patch b/divinemc-server/minecraft-patches/features/0007-C2ME-Optimize-world-gen-math.patch new file mode 100644 index 0000000..ee7030c --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0007-C2ME-Optimize-world-gen-math.patch @@ -0,0 +1,75 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sun, 12 Jan 2025 21:07:52 +0300 +Subject: [PATCH] C2ME: Optimize world gen math + + +diff --git a/net/minecraft/world/level/ChunkPos.java b/net/minecraft/world/level/ChunkPos.java +index 55ce935a2fab7e32904d9ff599867269035d703f..4fa84743ba7f570f11a4979b7e5381478c844aef 100644 +--- a/net/minecraft/world/level/ChunkPos.java ++++ b/net/minecraft/world/level/ChunkPos.java +@@ -110,7 +110,12 @@ public class ChunkPos { + + @Override + public boolean equals(Object other) { +- return this == other || other instanceof ChunkPos chunkPos && this.x == chunkPos.x && this.z == chunkPos.z; ++ // DivineMC start - Use standard equals ++ if (other == this) return true; ++ if (other == null || other.getClass() != this.getClass()) return false; ++ ChunkPos thatPos = (ChunkPos) other; ++ return this.x == thatPos.x && this.z == thatPos.z; ++ // DivineMC end - Use standard equals + } + + public int getMiddleBlockX() { +diff --git a/net/minecraft/world/level/levelgen/Beardifier.java b/net/minecraft/world/level/levelgen/Beardifier.java +index 131923282c9ecbcb1d7f45a826da907c02bd2716..e7c0de38ef689510860dc0b626f744617a694956 100644 +--- a/net/minecraft/world/level/levelgen/Beardifier.java ++++ b/net/minecraft/world/level/levelgen/Beardifier.java +@@ -132,8 +132,14 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker { + } + + private static double getBuryContribution(double x, double y, double z) { +- double len = Mth.length(x, y, z); +- return Mth.clampedMap(len, 0.0, 6.0, 1.0, 0.0); ++ // DivineMC start - Optimize method for beardifier ++ double d = Math.sqrt(x * x + y * y + z * z); ++ if (d > 6.0) { ++ return 0.0; ++ } else { ++ return 1.0 - d / 6.0; ++ } ++ // DivineMC end - Optimize method for beardifier + } + + private static double getBeardContribution(int x, int y, int z, int height) { +diff --git a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java +index 65728ef17e63d71833677fdcbd5bb90794b4822b..832a26760a777fe2d62f2f5f7a885bae63f67517 100644 +--- a/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java ++++ b/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java +@@ -68,8 +68,10 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { + Aquifer.FluidStatus fluidStatus = new Aquifer.FluidStatus(-54, Blocks.LAVA.defaultBlockState()); + int seaLevel = settings.seaLevel(); + Aquifer.FluidStatus fluidStatus1 = new Aquifer.FluidStatus(seaLevel, settings.defaultFluid()); +- Aquifer.FluidStatus fluidStatus2 = new Aquifer.FluidStatus(DimensionType.MIN_Y * 2, Blocks.AIR.defaultBlockState()); +- return (x, y, z) -> y < Math.min(-54, seaLevel) ? fluidStatus : fluidStatus1; ++ // DivineMC start - Optimize world gen ++ final int min = Math.min(-54, seaLevel); ++ return (j, k, l) -> k < min ? fluidStatus : fluidStatus1; ++ // DivineMC end - Optimize world gen + } + + @Override +diff --git a/net/minecraft/world/level/levelgen/synth/PerlinNoise.java b/net/minecraft/world/level/levelgen/synth/PerlinNoise.java +index da3c26fbad32d75d71f7e59c8c3341316a754756..6e1ebd6110da183a8f97abd81ced16cd89df725f 100644 +--- a/net/minecraft/world/level/levelgen/synth/PerlinNoise.java ++++ b/net/minecraft/world/level/levelgen/synth/PerlinNoise.java +@@ -187,7 +187,7 @@ public class PerlinNoise { + } + + public static double wrap(double value) { +- return value - Mth.lfloor(value / 3.3554432E7 + 0.5) * 3.3554432E7; ++ return value - Math.floor(value / 3.3554432E7 + 0.5) * 3.3554432E7; // DivineMC - Avoid casting + } + + protected int firstOctave() { diff --git a/patches/server/0050-Carpet-Fixes-RecipeManager-Optimize.patch b/divinemc-server/minecraft-patches/features/0008-Carpet-Fixes-RecipeManager-Optimize.patch similarity index 57% rename from patches/server/0050-Carpet-Fixes-RecipeManager-Optimize.patch rename to divinemc-server/minecraft-patches/features/0008-Carpet-Fixes-RecipeManager-Optimize.patch index 5c326d4..78ff764 100644 --- a/patches/server/0050-Carpet-Fixes-RecipeManager-Optimize.patch +++ b/divinemc-server/minecraft-patches/features/0008-Carpet-Fixes-RecipeManager-Optimize.patch @@ -1,6 +1,6 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sun, 15 Dec 2024 23:32:36 +0300 +Date: Sun, 12 Jan 2025 21:10:42 +0300 Subject: [PATCH] Carpet-Fixes: RecipeManager Optimize Original project: https://github.com/fxmorin/carpet-fixes @@ -8,26 +8,26 @@ Optimized the RecipeManager getFirstMatch call to be up to 3x faster This is a fully vanilla optimization. Improves: [Blast]Furnace/Campfire/Smoker/Stonecutter/Crafting/Sheep Color Choosing This was mostly made for the auto crafting table, since the performance boost is much more visible while using that mod -diff --git a/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java b/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java -index 2483627f807d7a3907f6848a8bc45d7a798e746d..01b63bf331a39b5fb23734f72a4f9880a98094b2 100644 ---- a/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java -+++ b/src/main/java/net/minecraft/world/item/crafting/RecipeManager.java -@@ -197,7 +197,7 @@ public class RecipeManager extends SimplePreparableReloadListener imp +diff --git a/net/minecraft/world/item/crafting/RecipeManager.java b/net/minecraft/world/item/crafting/RecipeManager.java +index aefaac550b58be479cc282f52dea91d4b1e530f6..2877a3229e03285e9ba5ec2bb68e17c9da202816 100644 +--- a/net/minecraft/world/item/crafting/RecipeManager.java ++++ b/net/minecraft/world/item/crafting/RecipeManager.java +@@ -167,7 +167,7 @@ public class RecipeManager extends SimplePreparableReloadListener imp - public > Optional> getRecipeFor(RecipeType type, I input, Level world) { + public > Optional> getRecipeFor(RecipeType recipeType, I input, Level level) { // CraftBukkit start -- List> list = this.recipes.getRecipesFor(type, input, world).toList(); -+ List> list = this.recipes.getRecipesForList(type, input, world); // DivineMC - Carpet-Fixes - Remove streams to be faster +- List> list = this.recipes.getRecipesFor(recipeType, input, level).toList(); ++ List> list = this.recipes.getRecipesForList(recipeType, input, level); // DivineMC - Carpet-Fixes - Remove streams to be faster return (list.isEmpty()) ? Optional.empty() : Optional.of(list.getLast()); // CraftBukkit - SPIGOT-4638: last recipe gets priority // CraftBukkit end } -diff --git a/src/main/java/net/minecraft/world/item/crafting/RecipeMap.java b/src/main/java/net/minecraft/world/item/crafting/RecipeMap.java -index c4067fbf827fed882772962a0e4b3ead0d642e62..2d289139f80855f07121861b1f840058f0bc9ec1 100644 ---- a/src/main/java/net/minecraft/world/item/crafting/RecipeMap.java -+++ b/src/main/java/net/minecraft/world/item/crafting/RecipeMap.java -@@ -105,4 +105,24 @@ public class RecipeMap { - return recipeholder.value().matches(input, world); - }); +diff --git a/net/minecraft/world/item/crafting/RecipeMap.java b/net/minecraft/world/item/crafting/RecipeMap.java +index 098753ddd215b6ef5915fac71d8c4f0b19cf4142..1778e58dca9430756d59d07bf017ebe4cc1f4ed4 100644 +--- a/net/minecraft/world/item/crafting/RecipeMap.java ++++ b/net/minecraft/world/item/crafting/RecipeMap.java +@@ -75,4 +75,24 @@ public class RecipeMap { + public > Stream> getRecipesFor(RecipeType type, I input, Level level) { + return input.isEmpty() ? Stream.empty() : this.byType(type).stream().filter(recipeHolder -> recipeHolder.value().matches(input, level)); } + + // DivineMC start - Carpet-Fixes - Remove streams to be faster diff --git a/divinemc-server/minecraft-patches/features/0009-No-chat-sign.patch b/divinemc-server/minecraft-patches/features/0009-No-chat-sign.patch new file mode 100644 index 0000000..924e7c0 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0009-No-chat-sign.patch @@ -0,0 +1,149 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Mon, 13 Jan 2025 19:35:57 +0300 +Subject: [PATCH] No chat sign + + +diff --git a/net/minecraft/commands/arguments/ArgumentSignatures.java b/net/minecraft/commands/arguments/ArgumentSignatures.java +index 47cb25aa9c37bd84d156288c397321009f1d9ae2..259fd0401a4a0547cf83ba1793f2b3d2898a58fe 100644 +--- a/net/minecraft/commands/arguments/ArgumentSignatures.java ++++ b/net/minecraft/commands/arguments/ArgumentSignatures.java +@@ -14,9 +14,16 @@ public record ArgumentSignatures(List entries) { + private static final int MAX_ARGUMENT_NAME_LENGTH = 16; + + public ArgumentSignatures(FriendlyByteBuf buffer) { +- this(buffer.readCollection(FriendlyByteBuf.>limitValue(ArrayList::new, 8), ArgumentSignatures.Entry::new)); ++ this(readSign(buffer)); // DivineMC - No chat sign + } + ++ // DivineMC start - No chat sign ++ private static List readSign(FriendlyByteBuf buf) { ++ var entries = buf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 8), Entry::new); ++ return space.bxteam.divinemc.configuration.DivineConfig.noChatSign ? List.of() : entries; ++ } ++ // DivineMC end - No chat sign ++ + public void write(FriendlyByteBuf buffer) { + buffer.writeCollection(this.entries, (buffer1, entry) -> entry.write(buffer1)); + } +diff --git a/net/minecraft/network/FriendlyByteBuf.java b/net/minecraft/network/FriendlyByteBuf.java +index e5e5d9bc095ccd9fbf1c8aaa09e5c4ebb1d1c920..d8c98e596f938f103f8155269a2096a6d1b7ffbb 100644 +--- a/net/minecraft/network/FriendlyByteBuf.java ++++ b/net/minecraft/network/FriendlyByteBuf.java +@@ -114,6 +114,17 @@ public class FriendlyByteBuf extends ByteBuf { + public void writeJsonWithCodec(Codec codec, T value, int maxLength) { + // Paper end - Adventure; add max length parameter + DataResult dataResult = codec.encodeStart(JsonOps.INSTANCE, value); ++ ++ // DivineMC start - No chat sign ++ if (codec == net.minecraft.network.protocol.status.ServerStatus.CODEC) { ++ JsonElement element = dataResult.getOrThrow(string -> new EncoderException("Failed to encode: " + string + " " + value)); ++ element.getAsJsonObject().addProperty("preventsChatReports", space.bxteam.divinemc.configuration.DivineConfig.noChatSign); ++ ++ this.writeUtf(GSON.toJson(element)); ++ return; ++ } ++ // DivineMC end - No chat sign ++ + this.writeUtf(GSON.toJson(dataResult.getOrThrow(exception -> new EncoderException("Failed to encode: " + exception + " " + value))), maxLength); // Paper - Adventure; add max length parameter + } + +diff --git a/net/minecraft/network/protocol/game/ServerboundChatPacket.java b/net/minecraft/network/protocol/game/ServerboundChatPacket.java +index b5afc05924ae899e020c303c8b86398e1d4ab8a0..468ec7f1a9ee73c7748ca36850bf810a1811e372 100644 +--- a/net/minecraft/network/protocol/game/ServerboundChatPacket.java ++++ b/net/minecraft/network/protocol/game/ServerboundChatPacket.java +@@ -16,7 +16,7 @@ public record ServerboundChatPacket(String message, Instant timeStamp, long salt + ); + + private ServerboundChatPacket(FriendlyByteBuf buffer) { +- this(buffer.readUtf(256), buffer.readInstant(), buffer.readLong(), buffer.readNullable(MessageSignature::read), new LastSeenMessages.Update(buffer)); ++ this(buffer.readUtf(256), buffer.readInstant(), buffer.readLong(), buffer.readNullable(ServerboundChatPacket::readSign), new LastSeenMessages.Update(buffer)); // DivineMC - No chat sign + } + + private void write(FriendlyByteBuf buffer) { +@@ -27,6 +27,14 @@ public record ServerboundChatPacket(String message, Instant timeStamp, long salt + this.lastSeenMessages.write(buffer); + } + ++ // DivineMC start - No chat sign ++ private static MessageSignature readSign(FriendlyByteBuf buf) { ++ byte[] bs = new byte[256]; ++ buf.readBytes(bs); ++ return space.bxteam.divinemc.configuration.DivineConfig.noChatSign ? null : new MessageSignature(bs); ++ } ++ // DivineMC end - No chat sign ++ + @Override + public PacketType type() { + return GamePacketTypes.SERVERBOUND_CHAT; +diff --git a/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java b/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java +index 1df628ac0b414511aaed6e09d78f884c4170f730..2d674c7ef305469c0483f24720e673e04a8fd963 100644 +--- a/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java ++++ b/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java +@@ -26,6 +26,11 @@ public record ServerboundChatSessionUpdatePacket(RemoteChatSession.Data chatSess + + @Override + public void handle(ServerGamePacketListener handler) { ++ // DivineMC start - No chat sign ++ if (space.bxteam.divinemc.configuration.DivineConfig.noChatSign) { ++ return; ++ } ++ // DivineMC end - No chat sign + handler.handleChatSessionUpdate(this); + } + } +diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java +index c85023893fbb227894f8f888244f1f53e25dff3f..0755bd59ab467da4a42cf5dae0f2e409d3132fea 100644 +--- a/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/net/minecraft/server/dedicated/DedicatedServer.java +@@ -668,7 +668,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + // Paper start - Add setting for proxy online mode status + return properties.enforceSecureProfile + && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() +- && this.services.canValidateProfileKeys(); ++ && this.services.canValidateProfileKeys() && !space.bxteam.divinemc.configuration.DivineConfig.noChatSign; // DivineMC - No chat sign + // Paper end - Add setting for proxy online mode status + } + +diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +index 398c1733824b689520170de0be94006731afa5cd..289d8d2420f6298a4fc10e0563e99cc13c18880a 100644 +--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +@@ -312,10 +312,24 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + } + + public void send(Packet packet) { ++ // DivineMC start - No chat sign ++ if (space.bxteam.divinemc.configuration.DivineConfig.noChatSign) { ++ if (this instanceof ServerGamePacketListenerImpl && packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket chat) { ++ packet = new net.minecraft.network.protocol.game.ClientboundSystemChatPacket(chat.chatType().decorate(chat.unsignedContent() != null ? chat.unsignedContent() : Component.literal(chat.body().content())), false); ++ } ++ } ++ // DivineMC end - No chat sign + this.send(packet, null); + } + + public void send(Packet packet, @Nullable PacketSendListener listener) { ++ // DivineMC start - No chat sign ++ if (space.bxteam.divinemc.configuration.DivineConfig.noChatSign) { ++ if (packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket chat && listener != null) { ++ listener = null; ++ } ++ } ++ // DivineMC end - No chat sign + // CraftBukkit start + if (packet == null || this.processedDisconnect) { // Spigot + return; +diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java +index 94abb9d8f6381aee000dbd0720477db8b7ca279c..cb43a189e286afd858738e2590f8e812c510c3d3 100644 +--- a/net/minecraft/server/players/PlayerList.java ++++ b/net/minecraft/server/players/PlayerList.java +@@ -1319,7 +1319,7 @@ public abstract class PlayerList { + } + + public boolean verifyChatTrusted(PlayerChatMessage message) { // Paper - private -> public +- return message.hasSignature() && !message.hasExpiredServer(Instant.now()); ++ return space.bxteam.divinemc.configuration.DivineConfig.noChatSign || (message.hasSignature() && !message.hasExpiredServer(Instant.now())); // DivineMC - No chat sign + } + + // CraftBukkit start diff --git a/divinemc-server/minecraft-patches/features/0010-Petal-Async-Pathfinding.patch b/divinemc-server/minecraft-patches/features/0010-Petal-Async-Pathfinding.patch new file mode 100644 index 0000000..70bae88 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0010-Petal-Async-Pathfinding.patch @@ -0,0 +1,810 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Wed, 15 Jan 2025 18:12:55 +0300 +Subject: [PATCH] Petal: Async Pathfinding + +Original code by Bloom-host, licensed under GPL v3 +You can find the original code on https://github.com/Bloom-host/Petal + +Makes most pathfinding-related work happen asynchronously + +diff --git a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +index 67cbf9f5760fae5db6f31e64095cd1b6be6ade8e..3f4e42c6ee1a1aa14250de4f71ae61f324d335e9 100644 +--- a/net/minecraft/world/entity/ai/behavior/AcquirePoi.java ++++ b/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +@@ -94,21 +94,54 @@ public class AcquirePoi { + } + } + // Paper end - optimise POI access +- Path path = findPathToPois(mob, set); +- if (path != null && path.canReach()) { +- BlockPos target = path.getTarget(); +- poiManager.getType(target).ifPresent(holder -> { +- poiManager.take(acquirablePois, (holder1, blockPos) -> blockPos.equals(target), target, 1); +- memoryAccessor.set(GlobalPos.of(level.dimension(), target)); +- entityEventId.ifPresent(id -> level.broadcastEntityEvent(mob, id)); +- map.clear(); +- DebugPackets.sendPoiTicketCountPacket(level, target); ++ // DivineMC start - Async path processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ // await on path async ++ Path possiblePath = findPathToPois(mob, set); ++ ++ // wait on the path to be processed ++ space.bxteam.divinemc.pathfinding.AsyncPathProcessor.awaitProcessing(possiblePath, path -> { ++ // read canReach check ++ if (path == null || !path.canReach()) { ++ for (Pair, BlockPos> pair : set) { ++ map.computeIfAbsent( ++ pair.getSecond().asLong(), ++ m -> new JitteredLinearRetry(mob.level().random, time) ++ ); ++ } ++ return; ++ } ++ BlockPos blockPos = path.getTarget(); ++ poiManager.getType(blockPos).ifPresent(poiType -> { ++ poiManager.take(acquirablePois, ++ (holder, blockPos2) -> blockPos2.equals(blockPos), ++ blockPos, ++ 1 ++ ); ++ memoryAccessor.set(GlobalPos.of(level.dimension(), blockPos)); ++ entityEventId.ifPresent(status -> level.broadcastEntityEvent(mob, status)); ++ map.clear(); ++ DebugPackets.sendPoiTicketCountPacket(level, blockPos); ++ }); + }); + } else { +- for (Pair, BlockPos> pair : set) { +- map.computeIfAbsent(pair.getSecond().asLong(), l -> new AcquirePoi.JitteredLinearRetry(level.random, time)); ++ Path path = findPathToPois(mob, set); ++ if (path != null && path.canReach()) { ++ BlockPos target = path.getTarget(); ++ poiManager.getType(target).ifPresent(holder -> { ++ poiManager.take(acquirablePois, (holder1, blockPos) -> blockPos.equals(target), target, 1); ++ memoryAccessor.set(GlobalPos.of(level.dimension(), target)); ++ entityEventId.ifPresent(id -> level.broadcastEntityEvent(mob, id)); ++ map.clear(); ++ DebugPackets.sendPoiTicketCountPacket(level, target); ++ }); ++ } else { ++ for (Pair, BlockPos> pair : set) { ++ map.computeIfAbsent(pair.getSecond().asLong(), l -> new AcquirePoi.JitteredLinearRetry(level.random, time)); ++ } + } + } ++ // DivineMC end - Async path processing + + return true; + } +diff --git a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java +index 621ba76784f2b92790eca62be4d0688834335ab6..11a124ad7df99b3ddfc92496a8950dc6e46cdf07 100644 +--- a/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java ++++ b/net/minecraft/world/entity/ai/behavior/MoveToTargetSink.java +@@ -21,6 +21,7 @@ public class MoveToTargetSink extends Behavior { + private int remainingCooldown; + @Nullable + private Path path; ++ private boolean finishedProcessing; // DivineMC - async path processing + @Nullable + private BlockPos lastTargetPos; + private float speedModifier; +@@ -53,9 +54,11 @@ public class MoveToTargetSink extends Behavior { + Brain brain = owner.getBrain(); + WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); + boolean flag = this.reachedTarget(owner, walkTarget); +- if (!flag && this.tryComputePath(owner, walkTarget, level.getGameTime())) { ++ if (!space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding && !flag && this.tryComputePath(owner, walkTarget, level.getGameTime())) { // DivineMC - async path processing + this.lastTargetPos = walkTarget.getTarget().currentBlockPosition(); + return true; ++ } else if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding && !flag) { // DivineMC - async pathfinding ++ return true; + } else { + brain.eraseMemory(MemoryModuleType.WALK_TARGET); + if (flag) { +@@ -69,6 +72,7 @@ public class MoveToTargetSink extends Behavior { + + @Override + protected boolean canStillUse(ServerLevel level, Mob entity, long gameTime) { ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding && !this.finishedProcessing) return true; // DivineMC - wait for processing + if (this.path != null && this.lastTargetPos != null) { + Optional memory = entity.getBrain().getMemory(MemoryModuleType.WALK_TARGET); + boolean flag = memory.map(MoveToTargetSink::isWalkTargetSpectator).orElse(false); +@@ -95,27 +99,98 @@ public class MoveToTargetSink extends Behavior { + + @Override + protected void start(ServerLevel level, Mob entity, long gameTime) { ++ // DivineMC start - start processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ Brain brain = entity.getBrain(); ++ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); ++ ++ this.finishedProcessing = false; ++ this.lastTargetPos = walkTarget.getTarget().currentBlockPosition(); ++ this.path = this.computePath(entity, walkTarget); ++ return; ++ } ++ // DivineMC end - start processing + entity.getBrain().setMemory(MemoryModuleType.PATH, this.path); + entity.getNavigation().moveTo(this.path, (double)this.speedModifier); + } + + @Override + protected void tick(ServerLevel level, Mob owner, long gameTime) { +- Path path = owner.getNavigation().getPath(); +- Brain brain = owner.getBrain(); +- if (this.path != path) { +- this.path = path; +- brain.setMemory(MemoryModuleType.PATH, path); +- } ++ // DivineMC start - Async path processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ if (this.path != null && !this.path.isProcessed()) return; // wait for processing + +- if (path != null && this.lastTargetPos != null) { +- WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); +- if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0 && this.tryComputePath(owner, walkTarget, level.getGameTime())) { +- this.lastTargetPos = walkTarget.getTarget().currentBlockPosition(); +- this.start(level, owner, gameTime); ++ if (!this.finishedProcessing) { ++ this.finishedProcessing = true; ++ ++ Brain brain = owner.getBrain(); ++ boolean canReach = this.path != null && this.path.canReach(); ++ if (canReach) { ++ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE); ++ } else if (!brain.hasMemoryValue(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE)) { ++ brain.setMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, gameTime); ++ } ++ ++ if (!canReach) { ++ Optional walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET); ++ ++ if (!walkTarget.isPresent()) return; ++ ++ BlockPos blockPos = walkTarget.get().getTarget().currentBlockPosition(); ++ Vec3 vec3 = DefaultRandomPos.getPosTowards((PathfinderMob) owner, 10, 7, Vec3.atBottomCenterOf(blockPos), (float) Math.PI / 2F); ++ if (vec3 != null) { ++ // try recalculating the path using a random position ++ this.path = owner.getNavigation().createPath(vec3.x, vec3.y, vec3.z, 0); ++ this.finishedProcessing = false; ++ return; ++ } ++ } ++ ++ owner.getBrain().setMemory(MemoryModuleType.PATH, this.path); ++ owner.getNavigation().moveTo(this.path, this.speedModifier); + } ++ ++ Path path = owner.getNavigation().getPath(); ++ Brain brain = owner.getBrain(); ++ ++ if (path != null && this.lastTargetPos != null && brain.hasMemoryValue(MemoryModuleType.WALK_TARGET)) { ++ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); // we know isPresent = true ++ if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0D) { ++ this.start(level, owner, gameTime); ++ } ++ } ++ } else { ++ Path path = owner.getNavigation().getPath(); ++ Brain brain = owner.getBrain(); ++ if (this.path != path) { ++ this.path = path; ++ brain.setMemory(MemoryModuleType.PATH, path); ++ } ++ ++ if (path != null && this.lastTargetPos != null) { ++ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); ++ if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0 ++ && this.tryComputePath(owner, walkTarget, level.getGameTime())) { ++ this.lastTargetPos = walkTarget.getTarget().currentBlockPosition(); ++ this.start(level, owner, gameTime); ++ } ++ } ++ } ++ // DivineMC end - Async path processing ++ } ++ ++ // DivineMC start - Async path processing ++ @Nullable ++ private Path computePath(Mob entity, WalkTarget walkTarget) { ++ BlockPos blockPos = walkTarget.getTarget().currentBlockPosition(); ++ this.speedModifier = walkTarget.getSpeedModifier(); ++ Brain brain = entity.getBrain(); ++ if (this.reachedTarget(entity, walkTarget)) { ++ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE); + } ++ return entity.getNavigation().createPath(blockPos, 0); + } ++ // DivineMC end - Async path processing + + private boolean tryComputePath(Mob mob, WalkTarget target, long time) { + BlockPos blockPos = target.getTarget().currentBlockPosition(); +diff --git a/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java b/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java +index 4f9f3367b1ca3903df03a80fa2b01a3d24e6e77d..8308934ec65d1f49ea356e19c940407bae7041f2 100644 +--- a/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java ++++ b/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java +@@ -60,17 +60,38 @@ public class SetClosestHomeAsWalkTarget { + poi -> poi.is(PoiTypes.HOME), predicate, mob.blockPosition(), 48, PoiManager.Occupancy.ANY + ) + .collect(Collectors.toSet()); +- Path path = AcquirePoi.findPathToPois(mob, set); +- if (path != null && path.canReach()) { +- BlockPos target = path.getTarget(); +- Optional> type = poiManager.getType(target); +- if (type.isPresent()) { +- walkTarget.set(new WalkTarget(target, speedModifier, 1)); +- DebugPackets.sendPoiTicketCountPacket(level, target); ++ // DivineMC start - async path processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ // await on path async ++ Path possiblePath = AcquirePoi.findPathToPois(mob, set); ++ ++ // wait on the path to be processed ++ space.bxteam.divinemc.pathfinding.AsyncPathProcessor.awaitProcessing(possiblePath, path -> { ++ if (path == null || !path.canReach() || mutableInt.getValue() < 5) { // read canReach check ++ map.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue()); ++ return; ++ } ++ BlockPos blockPos = path.getTarget(); ++ Optional> optional2 = poiManager.getType(blockPos); ++ if (optional2.isPresent()) { ++ walkTarget.set(new WalkTarget(blockPos, speedModifier, 1)); ++ DebugPackets.sendPoiTicketCountPacket(level, blockPos); ++ } ++ }); ++ } else { ++ Path path = AcquirePoi.findPathToPois(mob, set); ++ if (path != null && path.canReach()) { ++ BlockPos target = path.getTarget(); ++ Optional> type = poiManager.getType(target); ++ if (type.isPresent()) { ++ walkTarget.set(new WalkTarget(target, speedModifier, 1)); ++ DebugPackets.sendPoiTicketCountPacket(level, target); ++ } ++ } else if (mutableInt.getValue() < 5) { ++ map.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue()); + } +- } else if (mutableInt.getValue() < 5) { +- map.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue()); + } ++ // DivineMC end - async path processing + + return true; + } else { +diff --git a/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java b/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java +index d8f532c5e68ff4dff933556c4f981e9474c044e6..37f3d3888ea2a862d006cf2b201f9715bcb8ce1e 100644 +--- a/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java ++++ b/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java +@@ -56,7 +56,7 @@ public abstract class DoorInteractGoal extends Goal { + } else { + GroundPathNavigation groundPathNavigation = (GroundPathNavigation)this.mob.getNavigation(); + Path path = groundPathNavigation.getPath(); +- if (path != null && !path.isDone()) { ++ if (path != null && path.isProcessed() && !path.isDone()) { // DivineMC - async path processing + for (int i = 0; i < Math.min(path.getNextNodeIndex() + 2, path.getNodeCount()); i++) { + Node node = path.getNode(i); + this.doorPos = new BlockPos(node.x, node.y + 1, node.z); +diff --git a/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java b/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java +index 66a02fe7594522ef391d67e09856bf3f70fe597d..d560bbfb1272f8fd9b0d9b7af80f748afa61246c 100644 +--- a/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java ++++ b/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java +@@ -12,9 +12,25 @@ public class AmphibiousPathNavigation extends PathNavigation { + super(mob, level); + } + ++ // DivineMC start - async path processing ++ private static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { ++ AmphibiousNodeEvaluator nodeEvaluator = new AmphibiousNodeEvaluator(false); ++ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); ++ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); ++ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); ++ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); ++ return nodeEvaluator; ++ }; ++ // DivineMC end - async path processing ++ + @Override + protected PathFinder createPathFinder(int maxVisitedNodes) { + this.nodeEvaluator = new AmphibiousNodeEvaluator(false); ++ // DivineMC start - async path processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator); ++ } ++ // DivineMC end - async path processing + return new PathFinder(this.nodeEvaluator, maxVisitedNodes); + } + +diff --git a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java +index 71ea68b56b3069bdf8e47931156b6ef49ea8ce5d..da804d3feef49555677ab6485d7aa6b1114b9d7f 100644 +--- a/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java ++++ b/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java +@@ -16,9 +16,25 @@ public class FlyingPathNavigation extends PathNavigation { + super(mob, level); + } + ++ // DivineMC start - async path processing ++ private static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { ++ FlyNodeEvaluator nodeEvaluator = new FlyNodeEvaluator(); ++ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); ++ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); ++ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); ++ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); ++ return nodeEvaluator; ++ }; ++ // DivineMC end - async path processing ++ + @Override + protected PathFinder createPathFinder(int maxVisitedNodes) { + this.nodeEvaluator = new FlyNodeEvaluator(); ++ // DivineMC start - async path processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator); ++ } ++ // DivineMC end - async path processing + return new PathFinder(this.nodeEvaluator, maxVisitedNodes); + } + +@@ -48,6 +64,7 @@ public class FlyingPathNavigation extends PathNavigation { + if (this.hasDelayedRecomputation) { + this.recomputePath(); + } ++ if (this.path != null && !this.path.isProcessed()) return; // DivineMC - async path processing + + if (!this.isDone()) { + if (this.canUpdatePath()) { +diff --git a/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java b/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java +index 045cfafb3afe8271d60852ae3c7cdcb039b44d4f..fc02c7ca984dd2bcfca47002d7353427a1a70264 100644 +--- a/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java ++++ b/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java +@@ -24,9 +24,25 @@ public class GroundPathNavigation extends PathNavigation { + super(mob, level); + } + ++ // DivineMC start - async path processing ++ protected static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { ++ WalkNodeEvaluator nodeEvaluator = new WalkNodeEvaluator(); ++ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); ++ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); ++ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); ++ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); ++ return nodeEvaluator; ++ }; ++ // DivineMC end - async path processing ++ + @Override + protected PathFinder createPathFinder(int maxVisitedNodes) { + this.nodeEvaluator = new WalkNodeEvaluator(); ++ // DivineMC start - async path processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator); ++ } ++ // DivineMC end - async path processing + return new PathFinder(this.nodeEvaluator, maxVisitedNodes); + } + +diff --git a/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/net/minecraft/world/entity/ai/navigation/PathNavigation.java +index b44f2c49509d847817a78e9c4fb1499fb378054b..aaa4e1d36d486baa767d427be51390b013214905 100644 +--- a/net/minecraft/world/entity/ai/navigation/PathNavigation.java ++++ b/net/minecraft/world/entity/ai/navigation/PathNavigation.java +@@ -169,6 +169,10 @@ public abstract class PathNavigation { + return null; + } else if (!this.canUpdatePath()) { + return null; ++ // DivineMC start - catch early if it's still processing these positions let it keep processing ++ } else if (this.path instanceof space.bxteam.divinemc.pathfinding.AsyncPath asyncPath && !asyncPath.isProcessed() && asyncPath.hasSameProcessingPositions(targets)) { ++ return this.path; ++ // DivineMC end - catch early if it's still processing these positions let it keep processing + } else if (this.path != null && !this.path.isDone() && targets.contains(this.targetPos)) { + return this.path; + } else { +@@ -195,12 +199,30 @@ public abstract class PathNavigation { + int i = (int)(followRange + regionOffset); + PathNavigationRegion pathNavigationRegion = new PathNavigationRegion(this.level, blockPos.offset(-i, -i, -i), blockPos.offset(i, i, i)); + Path path = this.pathFinder.findPath(pathNavigationRegion, this.mob, targets, followRange, accuracy, this.maxVisitedNodesMultiplier); +- profilerFiller.pop(); +- if (path != null && path.getTarget() != null) { +- this.targetPos = path.getTarget(); +- this.reachRange = accuracy; +- this.resetStuckTimeout(); ++ // DivineMC start - async path processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ // assign early a target position. most calls will only have 1 position ++ if (!targets.isEmpty()) this.targetPos = targets.iterator().next(); ++ ++ space.bxteam.divinemc.pathfinding.AsyncPathProcessor.awaitProcessing(path, processedPath -> { ++ // check that processing didn't take so long that we calculated a new path ++ if (processedPath != this.path) return; ++ ++ if (processedPath != null && processedPath.getTarget() != null) { ++ this.targetPos = processedPath.getTarget(); ++ this.reachRange = accuracy; ++ this.resetStuckTimeout(); ++ } ++ }); ++ } else { ++ profilerFiller.pop(); ++ if (path != null && path.getTarget() != null) { ++ this.targetPos = path.getTarget(); ++ this.reachRange = accuracy; ++ this.resetStuckTimeout(); ++ } + } ++ // DivineMC end - async path processing + + return path; + } +@@ -251,8 +273,8 @@ public abstract class PathNavigation { + if (this.isDone()) { + return false; + } else { +- this.trimPath(); +- if (this.path.getNodeCount() <= 0) { ++ if (path.isProcessed()) this.trimPath(); // DivineMC - only trim if processed ++ if (path.isProcessed() && this.path.getNodeCount() <= 0) { // DivineMC - only check node count if processed + return false; + } else { + this.speedModifier = speed; +@@ -275,6 +297,7 @@ public abstract class PathNavigation { + if (this.hasDelayedRecomputation) { + this.recomputePath(); + } ++ if (this.path != null && !this.path.isProcessed()) return; // DivineMC - skip pathfinding if we're still processing + + if (!this.isDone()) { + if (this.canUpdatePath()) { +@@ -304,6 +327,7 @@ public abstract class PathNavigation { + } + + protected void followThePath() { ++ if (!this.path.isProcessed()) return; // DivineMC - skip if not processed + Vec3 tempMobPos = this.getTempMobPos(); + this.maxDistanceToWaypoint = this.mob.getBbWidth() > 0.75F ? this.mob.getBbWidth() / 2.0F : 0.75F - this.mob.getBbWidth() / 2.0F; + Vec3i nextNodePos = this.path.getNextNodePos(); +@@ -460,7 +484,7 @@ public abstract class PathNavigation { + public boolean shouldRecomputePath(BlockPos pos) { + if (this.hasDelayedRecomputation) { + return false; +- } else if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) { ++ } else if (this.path != null && this.path.isProcessed() && !this.path.isDone() && this.path.getNodeCount() != 0) { // DivineMC - Skip if not processed + Node endNode = this.path.getEndNode(); + Vec3 vec3 = new Vec3((endNode.x + this.mob.getX()) / 2.0, (endNode.y + this.mob.getY()) / 2.0, (endNode.z + this.mob.getZ()) / 2.0); + return pos.closerToCenterThan(vec3, this.path.getNodeCount() - this.path.getNextNodeIndex()); +diff --git a/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java b/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java +index 2979846853898d78a2df19df2287da16dbe4ae71..fbecca6fa274bcc6d07b9d300c31873fdb64c773 100644 +--- a/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java ++++ b/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java +@@ -15,11 +15,27 @@ public class WaterBoundPathNavigation extends PathNavigation { + super(mob, level); + } + ++ // DivineMC start - async path processing ++ private static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { ++ SwimNodeEvaluator nodeEvaluator = new SwimNodeEvaluator(nodeEvaluatorFeatures.allowBreaching()); ++ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); ++ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); ++ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); ++ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); ++ return nodeEvaluator; ++ }; ++ // DivineMC end - async path processing ++ + @Override + protected PathFinder createPathFinder(int maxVisitedNodes) { + this.allowBreaching = this.mob.getType() == EntityType.DOLPHIN; + this.nodeEvaluator = new SwimNodeEvaluator(this.allowBreaching); + this.nodeEvaluator.setCanPassDoors(false); ++ // DivineMC start - async path processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator); ++ } ++ // DivineMC end - async path processing + return new PathFinder(this.nodeEvaluator, maxVisitedNodes); + } + +diff --git a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java +index 1f96fd5085bacb4c584576c7cb9f51e7898e9b03..cc202eb0848a2faecd556fa279565717be11b382 100644 +--- a/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java ++++ b/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java +@@ -57,17 +57,37 @@ public class NearestBedSensor extends Sensor { + java.util.List, BlockPos>> poiposes = new java.util.ArrayList<>(); + // don't ask me why it's unbounded. ask mojang. + io.papermc.paper.util.PoiAccess.findAnyPoiPositions(poiManager, type -> type.is(PoiTypes.HOME), predicate, entity.blockPosition(), level.purpurConfig.villagerNearestBedSensorSearchRadius, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes); // Purpur - Configurable villager search radius +- Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes)); + // Paper end - optimise POI access +- if (path != null && path.canReach()) { +- BlockPos target = path.getTarget(); +- Optional> type = poiManager.getType(target); +- if (type.isPresent()) { +- entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, target); ++ // DivineMC start - async pathfinding ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ Path possiblePath = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes)); ++ space.bxteam.divinemc.pathfinding.AsyncPathProcessor.awaitProcessing(possiblePath, path -> { ++ // read canReach check ++ if ((path == null || !path.canReach()) && this.triedCount < 5) { ++ this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate); ++ return; ++ } ++ if (path == null) return; ++ ++ BlockPos blockPos = path.getTarget(); ++ Optional> optional = poiManager.getType(blockPos); ++ if (optional.isPresent()) { ++ entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, blockPos); ++ } ++ }); ++ } else { ++ Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes)); ++ if (path != null && path.canReach()) { ++ BlockPos target = path.getTarget(); ++ Optional> type = poiManager.getType(target); ++ if (type.isPresent()) { ++ entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, target); ++ } ++ } else if (this.triedCount < 5) { ++ this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate); + } +- } else if (this.triedCount < 5) { +- this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate); + } ++ // DivineMC end - async pathfinding + } + } + } +diff --git a/net/minecraft/world/entity/animal/Bee.java b/net/minecraft/world/entity/animal/Bee.java +index 57c50ce5724b073b1aedf4df3129285143097303..5bdf82df2dbb79b8933813b61e617c4882fd5c60 100644 +--- a/net/minecraft/world/entity/animal/Bee.java ++++ b/net/minecraft/world/entity/animal/Bee.java +@@ -934,7 +934,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + } else { + Bee.this.pathfindRandomlyTowards(Bee.this.hivePos); + } +- } else { ++ } else if (navigation.getPath() != null && navigation.getPath().isProcessed()) { // DivineMC - check processing + boolean flag = this.pathfindDirectlyTowards(Bee.this.hivePos); + if (!flag) { + this.dropAndBlacklistHive(); +@@ -988,7 +988,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { + return true; + } else { + Path path = Bee.this.navigation.getPath(); +- return path != null && path.getTarget().equals(pos) && path.canReach() && path.isDone(); ++ return path != null && path.isProcessed() && path.getTarget().equals(pos) && path.canReach() && path.isDone(); // DivineMC - ensure path is processed + } + } + } +diff --git a/net/minecraft/world/entity/animal/frog/Frog.java b/net/minecraft/world/entity/animal/frog/Frog.java +index d286d4a45b6c8d5c684ad11500d2ad1a10a70c18..9c97f7bd3eaf1a962793287d1b6c7911cdf149fd 100644 +--- a/net/minecraft/world/entity/animal/frog/Frog.java ++++ b/net/minecraft/world/entity/animal/frog/Frog.java +@@ -496,6 +496,17 @@ public class Frog extends Animal implements VariantHolder> { + super(mob, level); + } + ++ // DivineMC start - async path processing ++ private static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { ++ Frog.FrogNodeEvaluator nodeEvaluator = new Frog.FrogNodeEvaluator(true); ++ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); ++ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); ++ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); ++ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); ++ return nodeEvaluator; ++ }; ++ // DivineMC end - async path processing ++ + @Override + public boolean canCutCorner(PathType pathType) { + return pathType != PathType.WATER_BORDER && super.canCutCorner(pathType); +@@ -504,6 +515,11 @@ public class Frog extends Animal implements VariantHolder> { + @Override + protected PathFinder createPathFinder(int maxVisitedNodes) { + this.nodeEvaluator = new Frog.FrogNodeEvaluator(true); ++ // DivineMC start - async path processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator); ++ } ++ // DivineMC end - async path processing + return new PathFinder(this.nodeEvaluator, maxVisitedNodes); + } + } +diff --git a/net/minecraft/world/entity/monster/Drowned.java b/net/minecraft/world/entity/monster/Drowned.java +index 6c73245b8d04f194e72165aa0000ca79a95db59d..2686df57d9d48db1438278d0d053bdbd3c65c0a7 100644 +--- a/net/minecraft/world/entity/monster/Drowned.java ++++ b/net/minecraft/world/entity/monster/Drowned.java +@@ -313,7 +313,7 @@ public class Drowned extends Zombie implements RangedAttackMob { + + protected boolean closeToNextPos() { + Path path = this.getNavigation().getPath(); +- if (path != null) { ++ if (path != null && path.isProcessed()) { // DivineMC - ensure path is processed + BlockPos target = path.getTarget(); + if (target != null) { + double d = this.distanceToSqr(target.getX(), target.getY(), target.getZ()); +diff --git a/net/minecraft/world/entity/monster/Strider.java b/net/minecraft/world/entity/monster/Strider.java +index 241526239bdbd5d9276f85e7fca46a7051f46a25..026ec71bff3e4e0c2098e331f6a2bd9ba5955ccb 100644 +--- a/net/minecraft/world/entity/monster/Strider.java ++++ b/net/minecraft/world/entity/monster/Strider.java +@@ -579,9 +579,25 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { + super(strider, level); + } + ++ // DivineMC start - async path processing ++ private static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { ++ WalkNodeEvaluator nodeEvaluator = new WalkNodeEvaluator(); ++ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); ++ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); ++ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); ++ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); ++ return nodeEvaluator; ++ }; ++ // DivineMC end - async path processing ++ + @Override + protected PathFinder createPathFinder(int maxVisitedNodes) { + this.nodeEvaluator = new WalkNodeEvaluator(); ++ // DivineMC start - async path processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, nodeEvaluatorGenerator); ++ } ++ // DivineMC end + return new PathFinder(this.nodeEvaluator, maxVisitedNodes); + } + +diff --git a/net/minecraft/world/entity/monster/warden/Warden.java b/net/minecraft/world/entity/monster/warden/Warden.java +index f74c784906208034f51b31bd9aba45733c3ebebe..a1fa17eaef088e36284fc79d6afd4c43b8a0a1a9 100644 +--- a/net/minecraft/world/entity/monster/warden/Warden.java ++++ b/net/minecraft/world/entity/monster/warden/Warden.java +@@ -619,6 +619,16 @@ public class Warden extends Monster implements VibrationSystem { + @Override + protected PathFinder createPathFinder(int maxVisitedNodes) { + this.nodeEvaluator = new WalkNodeEvaluator(); ++ // DivineMC start - async path processing ++ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { ++ return new PathFinder(this.nodeEvaluator, maxVisitedNodes, GroundPathNavigation.nodeEvaluatorGenerator) { ++ @Override ++ protected float distance(Node first, Node second) { ++ return first.distanceToXZ(second); ++ } ++ }; ++ } ++ // DivineMC end - async path processing + return new PathFinder(this.nodeEvaluator, maxVisitedNodes) { + @Override + protected float distance(Node first, Node second) { +diff --git a/net/minecraft/world/level/pathfinder/Path.java b/net/minecraft/world/level/pathfinder/Path.java +index d6d3c8f5e5dd4a8cab0d3fcc131c3a59f06130c6..839653a997f1e10970fa2956fadaf493808cb206 100644 +--- a/net/minecraft/world/level/pathfinder/Path.java ++++ b/net/minecraft/world/level/pathfinder/Path.java +@@ -26,6 +26,17 @@ public class Path { + this.reached = reached; + } + ++ // DivineMC start - async path processing ++ /** ++ * checks if the path is completely processed in the case of it being computed async ++ * ++ * @return true if the path is processed ++ */ ++ public boolean isProcessed() { ++ return true; ++ } ++ // DivineMC end - async path processing ++ + public void advance() { + this.nextNodeIndex++; + } +@@ -99,6 +110,7 @@ public class Path { + } + + public boolean sameAs(@Nullable Path pathentity) { ++ if (pathentity == this) return true; // DivineMC - async path processing + if (pathentity == null) { + return false; + } else if (pathentity.nodes.size() != this.nodes.size()) { +diff --git a/net/minecraft/world/level/pathfinder/PathFinder.java b/net/minecraft/world/level/pathfinder/PathFinder.java +index 81de6c1bbef1cafd3036e736dd305fbedc8368c6..4b8182414ca9aa22e096babb25a32f2b5ba60312 100644 +--- a/net/minecraft/world/level/pathfinder/PathFinder.java ++++ b/net/minecraft/world/level/pathfinder/PathFinder.java +@@ -25,11 +25,19 @@ public class PathFinder { + public final NodeEvaluator nodeEvaluator; + private static final boolean DEBUG = false; + private final BinaryHeap openSet = new BinaryHeap(); ++ private final @Nullable space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator; // DivineMC - we use this later to generate an evaluator + +- public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes) { ++ // DivineMC start - support nodeEvaluatorgenerators ++ public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes, @Nullable space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator) { // DivineMC - add nodeEvaluatorGenerator + this.nodeEvaluator = nodeEvaluator; + this.maxVisitedNodes = maxVisitedNodes; ++ this.nodeEvaluatorGenerator = nodeEvaluatorGenerator; ++ } ++ ++ public PathFinder(NodeEvaluator nodeEvaluator, int maxVisitedNodes) { ++ this(nodeEvaluator, maxVisitedNodes, null); + } ++ // DivineMC end - support nodeEvaluatorgenerators + + public void setMaxVisitedNodes(int maxVisitedNodes) { + this.maxVisitedNodes = maxVisitedNodes; +@@ -37,26 +45,63 @@ public class PathFinder { + + @Nullable + public Path findPath(PathNavigationRegion region, Mob mob, Set targetPositions, float maxRange, int accuracy, float searchDepthMultiplier) { +- this.openSet.clear(); +- this.nodeEvaluator.prepare(region, mob); +- Node start = this.nodeEvaluator.getStart(); ++ // DivineMC start - use a generated evaluator if we have one otherwise run sync ++ if (!space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) ++ this.openSet.clear(); // it's always cleared in processPath ++ NodeEvaluator nodeEvaluator = this.nodeEvaluatorGenerator == null ++ ? this.nodeEvaluator ++ : space.bxteam.divinemc.pathfinding.NodeEvaluatorCache.takeNodeEvaluator(this.nodeEvaluatorGenerator, this.nodeEvaluator); ++ nodeEvaluator.prepare(region, mob); ++ Node start = nodeEvaluator.getStart(); ++ // DivineMC end - use a generated evaluator if we have one otherwise run sync + if (start == null) { ++ space.bxteam.divinemc.pathfinding.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator); // DivineMC - handle nodeEvaluatorGenerator + return null; + } else { + // Paper start - Perf: remove streams and optimize collection + List> map = Lists.newArrayList(); + for (BlockPos pos : targetPositions) { +- map.add(new java.util.AbstractMap.SimpleEntry<>(this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos)); ++ map.add(new java.util.AbstractMap.SimpleEntry<>(nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos)); // DivineMC - handle nodeEvaluatorGenerator + } + // Paper end - Perf: remove streams and optimize collection +- Path path = this.findPath(start, map, maxRange, accuracy, searchDepthMultiplier); +- this.nodeEvaluator.done(); +- return path; ++ // DivineMC start - async path processing ++ if (this.nodeEvaluatorGenerator == null) { ++ // run sync :( ++ space.bxteam.divinemc.pathfinding.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator); ++ return this.findPath(start, map, maxRange, accuracy, searchDepthMultiplier); ++ } ++ ++ return new space.bxteam.divinemc.pathfinding.AsyncPath(Lists.newArrayList(), targetPositions, () -> { ++ try { ++ return this.processPath(nodeEvaluator, start, map, maxRange, accuracy, searchDepthMultiplier); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ return null; ++ } finally { ++ nodeEvaluator.done(); ++ space.bxteam.divinemc.pathfinding.NodeEvaluatorCache.returnNodeEvaluator(nodeEvaluator); ++ } ++ }); ++ // DivineMC end - async path processing + } + } + + @Nullable + private Path findPath(Node node, List> positions, float maxRange, int accuracy, float searchDepthMultiplier) { // Paper - optimize collection ++ // DivineMC start - split pathfinding into the original sync method for compat and processing for delaying ++ try { ++ return this.processPath(this.nodeEvaluator, node, positions, maxRange, accuracy, searchDepthMultiplier); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ return null; ++ } finally { ++ this.nodeEvaluator.done(); ++ } ++ } ++ ++ private synchronized @org.jetbrains.annotations.NotNull Path processPath(NodeEvaluator nodeEvaluator, Node node, List> positions, float maxRange, int accuracy, float searchDepthMultiplier) { // sync to only use the caching functions in this class on a single thread ++ org.apache.commons.lang3.Validate.isTrue(!positions.isEmpty()); // ensure that we have at least one position, which means we'll always return a path ++ // DivineMC end - split pathfinding into the original sync method for compat and processing for delaying + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push("find_path"); + profilerFiller.markForCharting(MetricCategory.PATH_FINDING); +@@ -95,7 +140,7 @@ public class PathFinder { + } + + if (!(node1.distanceTo(node) >= maxRange)) { +- int neighbors = this.nodeEvaluator.getNeighbors(this.neighbors, node1); ++ int neighbors = nodeEvaluator.getNeighbors(this.neighbors, node1); // DivineMC - use provided nodeEvaluator + + for (int i2 = 0; i2 < neighbors; i2++) { + Node node2 = this.neighbors[i2]; diff --git a/divinemc-server/minecraft-patches/features/0011-Implement-Secure-Seed.patch b/divinemc-server/minecraft-patches/features/0011-Implement-Secure-Seed.patch new file mode 100644 index 0000000..ba63ab3 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0011-Implement-Secure-Seed.patch @@ -0,0 +1,433 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sat, 20 Jul 2024 22:04:52 +0300 +Subject: [PATCH] Implement Secure Seed + +Original license: GPLv3 +Original project: https://github.com/plasmoapp/matter + +diff --git a/net/minecraft/server/commands/SeedCommand.java b/net/minecraft/server/commands/SeedCommand.java +index a65affc41a4fc299bc2281f0f53f2e075633899d..2ba3b8150bb753eebd5694275917c2ca8464b12e 100644 +--- a/net/minecraft/server/commands/SeedCommand.java ++++ b/net/minecraft/server/commands/SeedCommand.java +@@ -12,6 +12,17 @@ public class SeedCommand { + long seed = context.getSource().getLevel().getSeed(); + Component component = ComponentUtils.copyOnClickText(String.valueOf(seed)); + context.getSource().sendSuccess(() -> Component.translatable("commands.seed.success", component), false); ++ ++ // DivineMC start - Implement Secure Seed ++ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { ++ space.bxteam.divinemc.seed.Globals.setupGlobals(context.getSource().getLevel()); ++ String seedStr = space.bxteam.divinemc.seed.Globals.seedToString(space.bxteam.divinemc.seed.Globals.worldSeed); ++ Component featureSeedComponent = ComponentUtils.copyOnClickText(seedStr); ++ ++ context.getSource().sendSuccess(() -> Component.translatable(("Feature seed: %s"), featureSeedComponent), false); ++ } ++ // DivineMC end - Implement Secure Seed ++ + return (int)seed; + })); + } +diff --git a/net/minecraft/server/dedicated/DedicatedServerProperties.java b/net/minecraft/server/dedicated/DedicatedServerProperties.java +index 5748658abf0b90812005ae9d426df92daf5532f0..f4af49555bcf69cfaf7467f1fa7d4292a5652fa5 100644 +--- a/net/minecraft/server/dedicated/DedicatedServerProperties.java ++++ b/net/minecraft/server/dedicated/DedicatedServerProperties.java +@@ -114,7 +114,17 @@ public class DedicatedServerProperties extends Settings GsonHelper.parse(!property.isEmpty() ? property : "{}"), new JsonObject()), + this.get("level-type", property -> property.toLowerCase(Locale.ROOT), WorldPresets.NORMAL.location().toString()) +diff --git a/net/minecraft/server/level/ServerChunkCache.java b/net/minecraft/server/level/ServerChunkCache.java +index 6540b2d6a1062d883811ce240c49d30d1925b291..bceaf150f7e9b5c4a08be6102571d8fef68a2fc2 100644 +--- a/net/minecraft/server/level/ServerChunkCache.java ++++ b/net/minecraft/server/level/ServerChunkCache.java +@@ -652,6 +652,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + } + + public ChunkGenerator getGenerator() { ++ space.bxteam.divinemc.seed.Globals.setupGlobals(level); // DivineMC - Implement Secure Seed + return this.chunkMap.generator(); + } + +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index 3770dc90d9412c6378c0bd57a651b9c3e62b9a72..8127a71fd7d45541525be75e6699c2a5bae95f5f 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -634,6 +634,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + chunkGenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkGenerator, gen); + } + // CraftBukkit end ++ space.bxteam.divinemc.seed.Globals.setupGlobals(this); // DivineMC - Implement Secure Seed + boolean flag = server.forceSynchronousWrites(); + DataFixer fixerUpper = server.getFixerUpper(); + // Paper - rewrite chunk system +diff --git a/net/minecraft/world/entity/monster/Slime.java b/net/minecraft/world/entity/monster/Slime.java +index 240a54b210e23d5b79e6bcaf3806aa454668135d..f8cf35cf6837b810a28b64e834d810fc92bdb9d3 100644 +--- a/net/minecraft/world/entity/monster/Slime.java ++++ b/net/minecraft/world/entity/monster/Slime.java +@@ -423,8 +423,13 @@ public class Slime extends Mob implements Enemy { + return false; + } + +- ChunkPos chunkPos = new ChunkPos(pos); +- boolean flag = level.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(chunkPos.x, chunkPos.z, ((WorldGenLevel) level).getSeed(), level.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper ++ ChunkPos chunkPos = new ChunkPos(pos); ++ // DivineMC start - Implement Secure Seed ++ boolean isSlimeChunk = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed ++ ? level.getChunk(chunkPos.x, chunkPos.z).isSlimeChunk() ++ : WorldgenRandom.seedSlimeChunk(chunkPos.x, chunkPos.z, ((WorldGenLevel) level).getSeed(), level.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper ++ boolean flag = level.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || isSlimeChunk; ++ // DivineMC end - Implement Secure Seed + // Paper start - Replace rules for Height in Slime Chunks + final double maxHeightSlimeChunk = level.getMinecraftWorld().paperConfig().entities.spawning.slimeSpawnHeight.slimeChunk.maximum; + if (random.nextInt(10) == 0 && flag && pos.getY() < maxHeightSlimeChunk) { +diff --git a/net/minecraft/world/level/chunk/ChunkAccess.java b/net/minecraft/world/level/chunk/ChunkAccess.java +index 6d565b52552534ce9cacfc35ad1bf4adcb69eac3..7c3321b43e9eb1e6d15a571a8292853be4930448 100644 +--- a/net/minecraft/world/level/chunk/ChunkAccess.java ++++ b/net/minecraft/world/level/chunk/ChunkAccess.java +@@ -82,6 +82,10 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + public final Map blockEntities = new Object2ObjectOpenHashMap<>(); + protected final LevelHeightAccessor levelHeightAccessor; + protected final LevelChunkSection[] sections; ++ // DivineMC start - Implement Secure Seed ++ private boolean slimeChunk; ++ private boolean hasComputedSlimeChunk; ++ // DivineMC end - Implement Secure Seed + // 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); +@@ -191,6 +195,17 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh + return GameEventListenerRegistry.NOOP; + } + ++ // DivineMC start - Implement Secure Seed ++ public boolean isSlimeChunk() { ++ if (!hasComputedSlimeChunk) { ++ hasComputedSlimeChunk = true; ++ slimeChunk = space.bxteam.divinemc.seed.WorldgenCryptoRandom.seedSlimeChunk(chunkPos.x, chunkPos.z).nextInt(10) == 0; ++ } ++ ++ return slimeChunk; ++ } ++ // DivineMC end - Implement Secure Seed ++ + public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper + @Nullable + public abstract BlockState setBlockState(BlockPos pos, BlockState state, boolean isMoving); +diff --git a/net/minecraft/world/level/chunk/ChunkGenerator.java b/net/minecraft/world/level/chunk/ChunkGenerator.java +index 6ed51cf42b5864194d671b5b56f5b9bdf0291dc0..a565b61cd0919fa874d9ab8d00e879b860d3dab5 100644 +--- a/net/minecraft/world/level/chunk/ChunkGenerator.java ++++ b/net/minecraft/world/level/chunk/ChunkGenerator.java +@@ -343,7 +343,11 @@ public abstract class ChunkGenerator { + Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); + Map> map = registry.stream().collect(Collectors.groupingBy(structure1 -> structure1.step().ordinal())); + List list = this.featuresPerStep.get(); +- WorldgenRandom worldgenRandom = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed ++ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(blockPos.getX(), blockPos.getZ(), space.bxteam.divinemc.seed.Globals.Salt.UNDEFINED, 0) ++ : new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); ++ // DivineMC end - Implement Secure Seed + long l = worldgenRandom.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ()); + Set> set = new ObjectArraySet<>(); + ChunkPos.rangeClosed(sectionPos.chunk(), 1).forEach(chunkPos -> { +@@ -556,8 +560,18 @@ public abstract class ChunkGenerator { + } else { + ArrayList list1 = new ArrayList<>(list.size()); + list1.addAll(list); +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- worldgenRandom.setLargeFeatureSeed(structureState.getLevelSeed(), pos.x, pos.z); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom; ++ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { ++ worldgenRandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( ++ pos.x, pos.z, space.bxteam.divinemc.seed.Globals.Salt.GENERATE_FEATURE, 0 ++ ); ++ } else { ++ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); ++ ++ worldgenRandom.setLargeFeatureSeed(structureState.getLevelSeed(), pos.x, pos.z); ++ } ++ // DivineMC end - Implement Secure Seed + int i = 0; + + for (StructureSet.StructureSelectionEntry structureSelectionEntry1 : list1) { +diff --git a/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java b/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java +index 619b98e42e254c0c260c171a26a2472ddf59b885..797d0c8e836150b17dedb8ee00e0f245aff434ee 100644 +--- a/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java ++++ b/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java +@@ -205,14 +205,21 @@ public class ChunkGeneratorStructureState { + List> list = new ArrayList<>(count); + int spread = placement.spread(); + HolderSet holderSet = placement.preferredBiomes(); +- RandomSource randomSource = RandomSource.create(); +- // Paper start - Add missing structure set seed configs +- if (this.conf.strongholdSeed != null && structureSet.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { +- randomSource.setSeed(this.conf.strongholdSeed); +- } else { +- // Paper end - Add missing structure set seed configs +- randomSource.setSeed(this.concentricRingsSeed); +- } // Paper - Add missing structure set seed configs ++ // DivineMC start - Implement Secure Seed ++ RandomSource randomSource = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed ++ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(0, 0, space.bxteam.divinemc.seed.Globals.Salt.STRONGHOLDS, 0) ++ : RandomSource.create(); ++ ++ if (!space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { ++ // Paper start - Add missing structure set seed configs ++ if (this.conf.strongholdSeed != null && structureSet.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { ++ randomSource.setSeed(this.conf.strongholdSeed); ++ } else { ++ // Paper end - Add missing structure set seed configs ++ randomSource.setSeed(this.concentricRingsSeed); ++ } // Paper - Add missing structure set seed configs ++ } ++ // DivineMC end - Implement Secure Seed + double d = randomSource.nextDouble() * Math.PI * 2.0; + int i = 0; + int i1 = 0; +diff --git a/net/minecraft/world/level/chunk/status/ChunkStep.java b/net/minecraft/world/level/chunk/status/ChunkStep.java +index b8348976e80578d9eff64eea68c04c603fed49ad..d84099c67704881a99d371ff79177257faa1abfc 100644 +--- a/net/minecraft/world/level/chunk/status/ChunkStep.java ++++ b/net/minecraft/world/level/chunk/status/ChunkStep.java +@@ -60,6 +60,7 @@ public final class ChunkStep implements ca.spottedleaf.moonrise.patches.chunk_sy + } + + public CompletableFuture apply(WorldGenContext worldGenContext, StaticCache2D cache, ChunkAccess chunk) { ++ space.bxteam.divinemc.seed.Globals.setupGlobals(worldGenContext.level()); // DivineMC - Implement Secure Seed + if (chunk.getPersistedStatus().isBefore(this.targetStatus)) { + ProfiledDuration profiledDuration = JvmProfiler.INSTANCE + .onChunkGenerate(chunk.getPos(), worldGenContext.level().dimension(), this.targetStatus.getName()); +diff --git a/net/minecraft/world/level/levelgen/WorldOptions.java b/net/minecraft/world/level/levelgen/WorldOptions.java +index c92508741439a8d0d833ea02d0104416adb83c92..05a2c2e7830fda9d7c22904ee3ff44734e3a38af 100644 +--- a/net/minecraft/world/level/levelgen/WorldOptions.java ++++ b/net/minecraft/world/level/levelgen/WorldOptions.java +@@ -9,17 +9,28 @@ import net.minecraft.util.RandomSource; + import org.apache.commons.lang3.StringUtils; + + public class WorldOptions { ++ // DivineMC start - Implement Secure Seed ++ private static final boolean isSecureSeedEnabled = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed; + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( +- instance -> instance.group( ++ instance -> isSecureSeedEnabled ++ ? 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(worldOptions -> worldOptions.legacyCustomOptions) +- ) +- .apply(instance, instance.stable(WorldOptions::new)) ++ Codec.STRING.lenientOptionalFieldOf("legacy_custom_options").stable().forGetter(generatorOptions -> generatorOptions.legacyCustomOptions)).apply(instance, instance.stable(WorldOptions::new)) ++ : 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(worldOptions -> worldOptions.legacyCustomOptions)).apply(instance, instance.stable(WorldOptions::new)) + ); +- public static final WorldOptions DEMO_OPTIONS = new WorldOptions("North Carolina".hashCode(), true, true); ++ public static final WorldOptions DEMO_OPTIONS = isSecureSeedEnabled ++ ? new WorldOptions("North Carolina".hashCode(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), true, true) ++ : new WorldOptions("North Carolina".hashCode(), true, true); ++ // DivineMC end - Implement Secure Seed + private final long seed; ++ private long[] featureSeed = space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(); // DivineMC - Implement Secure Seed + private final boolean generateStructures; + private final boolean generateBonusChest; + private final Optional legacyCustomOptions; +@@ -28,9 +39,21 @@ public class WorldOptions { + this(seed, generateStructures, generateBonusChest, Optional.empty()); + } + ++ // DivineMC start - Implement Secure Seed ++ public WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest) { ++ this(seed, featureSeed, generateStructures, bonusChest, Optional.empty()); ++ } ++ + public static WorldOptions defaultWithRandomSeed() { +- return new WorldOptions(randomSeed(), true, false); ++ return isSecureSeedEnabled ++ ? new WorldOptions(randomSeed(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), true, false) ++ : new WorldOptions(randomSeed(), true, false); ++ } ++ ++ private WorldOptions(long seed, java.util.stream.LongStream featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { ++ this(seed, featureSeed.toArray(), generateStructures, bonusChest, legacyCustomOptions); + } ++ // DivineMC end - Implement Secure Seed + + public static WorldOptions testWorldWithRandomSeed() { + return new WorldOptions(randomSeed(), false, false); +@@ -43,10 +66,27 @@ public class WorldOptions { + this.legacyCustomOptions = legacyCustomOptions; + } + ++ // DivineMC start - Implement Secure Seed ++ private WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { ++ this(seed, generateStructures, bonusChest, legacyCustomOptions); ++ this.featureSeed = featureSeed; ++ } ++ // DivineMC end - Implement Secure Seed ++ + public long seed() { + return this.seed; + } + ++ // DivineMC start - Implement Secure Seed ++ public long[] featureSeed() { ++ return this.featureSeed; ++ } ++ ++ public java.util.stream.LongStream featureSeedStream() { ++ return java.util.stream.LongStream.of(this.featureSeed); ++ } ++ // DivineMC end - Implement Secure Seed ++ + public boolean generateStructures() { + return this.generateStructures; + } +@@ -59,17 +99,25 @@ public class WorldOptions { + return this.legacyCustomOptions.isPresent(); + } + ++ // DivineMC start - Implement Secure Seed + public WorldOptions withBonusChest(boolean generateBonusChest) { +- return new WorldOptions(this.seed, this.generateStructures, generateBonusChest, this.legacyCustomOptions); ++ return isSecureSeedEnabled ++ ? new WorldOptions(this.seed, this.featureSeed, this.generateStructures, generateBonusChest, this.legacyCustomOptions) ++ : new WorldOptions(this.seed, this.generateStructures, generateBonusChest, this.legacyCustomOptions); + } + + public WorldOptions withStructures(boolean generateStructures) { +- return new WorldOptions(this.seed, generateStructures, this.generateBonusChest, this.legacyCustomOptions); ++ return isSecureSeedEnabled ++ ? new WorldOptions(this.seed, this.featureSeed, generateStructures, this.generateBonusChest, this.legacyCustomOptions) ++ : new WorldOptions(this.seed, generateStructures, this.generateBonusChest, this.legacyCustomOptions); + } + + public WorldOptions withSeed(OptionalLong seed) { +- return new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions); ++ return isSecureSeedEnabled ++ ? new WorldOptions(seed.orElse(randomSeed()), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions) ++ : new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions); + } ++ // DivineMC end - Implement Secure Seed + + public static OptionalLong parseSeed(String seed) { + seed = seed.trim(); +diff --git a/net/minecraft/world/level/levelgen/feature/GeodeFeature.java b/net/minecraft/world/level/levelgen/feature/GeodeFeature.java +index 38475f6975533909924c8d54f438cf43cdfe31a3..528a69cf7c1f0f31988cc3902f41559826c4d27d 100644 +--- a/net/minecraft/world/level/levelgen/feature/GeodeFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/GeodeFeature.java +@@ -41,7 +41,11 @@ public class GeodeFeature extends Feature { + int i1 = geodeConfiguration.maxGenOffset; + List> list = Lists.newLinkedList(); + int i2 = geodeConfiguration.distributionPoints.sample(randomSource); +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(worldGenLevel.getSeed())); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed ++ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(0, 0, space.bxteam.divinemc.seed.Globals.Salt.GEODE_FEATURE, 0) ++ : new WorldgenRandom(new LegacyRandomSource(worldGenLevel.getSeed())); ++ // DivineMC end - Implement Secure Seed + NormalNoise normalNoise = NormalNoise.create(worldgenRandom, -4, 1.0); + List list1 = Lists.newLinkedList(); + double d = (double)i2 / geodeConfiguration.outerWallDistance.getMaxValue(); +diff --git a/net/minecraft/world/level/levelgen/structure/Structure.java b/net/minecraft/world/level/levelgen/structure/Structure.java +index 8328e864c72b7a358d6bb1f33459b8c4df2ecb1a..c8bf9b3c08903b95dd92c5eb7135d3426580c004 100644 +--- a/net/minecraft/world/level/levelgen/structure/Structure.java ++++ b/net/minecraft/world/level/levelgen/structure/Structure.java +@@ -249,6 +249,14 @@ public abstract class Structure { + } + + private static WorldgenRandom makeRandom(long seed, ChunkPos chunkPos) { ++ // DivineMC start - Implement Secure Seed ++ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { ++ return new space.bxteam.divinemc.seed.WorldgenCryptoRandom( ++ chunkPos.x, chunkPos.z, space.bxteam.divinemc.seed.Globals.Salt.GENERATE_FEATURE, seed ++ ); ++ } ++ // DivineMC end - Implement Secure Seed ++ + WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); + worldgenRandom.setLargeFeatureSeed(seed, chunkPos.x, chunkPos.z); + return worldgenRandom; +diff --git a/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java b/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java +index ee0d9dddb36b6879fa113299e24f1aa3b2b151cc..3859e80ef432a27df09fe89ef25e0a401cf970b5 100644 +--- a/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java ++++ b/net/minecraft/world/level/levelgen/structure/placement/RandomSpreadStructurePlacement.java +@@ -67,8 +67,17 @@ public class RandomSpreadStructurePlacement extends StructurePlacement { + public ChunkPos getPotentialStructureChunk(long seed, int regionX, int regionZ) { + int i = Math.floorDiv(regionX, this.spacing); + int i1 = Math.floorDiv(regionZ, this.spacing); +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- worldgenRandom.setLargeFeatureWithSalt(seed, i, i1, this.salt()); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom; ++ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { ++ worldgenRandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( ++ i, i1, space.bxteam.divinemc.seed.Globals.Salt.POTENTIONAL_FEATURE, this.salt ++ ); ++ } else { ++ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); ++ worldgenRandom.setLargeFeatureWithSalt(seed, i, i1, this.salt()); ++ } ++ // DivineMC end - Implement Secure Seed + int i2 = this.spacing - this.separation; + int i3 = this.spreadType.evaluate(worldgenRandom, i2); + int i4 = this.spreadType.evaluate(worldgenRandom, i2); +diff --git a/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java b/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java +index 670335a7bbfbc9da64c389977498c22dfcd03251..7b49200486e59a6546aa50d098492280e51d5873 100644 +--- a/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java ++++ b/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java +@@ -118,8 +118,17 @@ public abstract class StructurePlacement { + public abstract StructurePlacementType type(); + + private static boolean probabilityReducer(long levelSeed, int regionX, int regionZ, int salt, float probability, @org.jetbrains.annotations.Nullable Integer saltOverride) { // Paper - Add missing structure set seed configs; ignore here +- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); +- worldgenRandom.setLargeFeatureWithSalt(levelSeed, regionX, regionZ, salt); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom; ++ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { ++ worldgenRandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( ++ regionX, regionZ, space.bxteam.divinemc.seed.Globals.Salt.UNDEFINED, salt ++ ); ++ } else { ++ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); ++ worldgenRandom.setLargeFeatureWithSalt(levelSeed, salt, regionX, regionZ); ++ } ++ // DivineMC end - Implement Secure Seed + return worldgenRandom.nextFloat() < probability; + } + +diff --git a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java +index eb85edaa3b7fab4f11545b0fa8bfea882dedb67d..b9b15705ee20175451cfcac5795939d787ce0cef 100644 +--- a/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java ++++ b/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java +@@ -64,7 +64,11 @@ public class JigsawPlacement { + ChunkGenerator chunkGenerator = context.chunkGenerator(); + StructureTemplateManager structureTemplateManager = context.structureTemplateManager(); + LevelHeightAccessor levelHeightAccessor = context.heightAccessor(); +- WorldgenRandom worldgenRandom = context.random(); ++ // DivineMC start - Implement Secure Seed ++ WorldgenRandom worldgenRandom = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed ++ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(context.chunkPos().x, context.chunkPos().z, space.bxteam.divinemc.seed.Globals.Salt.JIGSAW_PLACEMENT, 0) ++ : context.random(); ++ // DivineMC end - Implement Secure Seed + Registry registry = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL); + Rotation random = Rotation.getRandom(worldgenRandom); + StructureTemplatePool structureTemplatePool = startPool.unwrapKey() diff --git a/divinemc-server/minecraft-patches/features/0012-Multithreaded-Tracker.patch b/divinemc-server/minecraft-patches/features/0012-Multithreaded-Tracker.patch new file mode 100644 index 0000000..e093a59 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0012-Multithreaded-Tracker.patch @@ -0,0 +1,335 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Wed, 15 Jan 2025 19:48:24 +0300 +Subject: [PATCH] Multithreaded Tracker + + +diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java +index 718487155b923bfffa215b70a12d42681a8ae141..19e98a8d905541f72becd0f61a86cc5c08e09daa 100644 +--- a/net/minecraft/server/level/ChunkMap.java ++++ b/net/minecraft/server/level/ChunkMap.java +@@ -250,9 +250,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + + final ServerPlayer[] backingSet = inRange.getRawDataUnchecked(); +- for (int i = 0, len = inRange.size(); i < len; i++) { +- ++(backingSet[i].mobCounts[index]); ++ // DivineMC start - Multithreaded tracker ++ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled) { ++ for (int i = 0, len = inRange.size(); i < len; i++) { ++ final ServerPlayer player = backingSet[i]; ++ if (player == null) continue; ++ ++(player.mobCounts[index]); ++ } ++ } else { ++ for (int i = 0, len = inRange.size(); i < len; i++) { ++ ++(backingSet[i].mobCounts[index]); ++ } + } ++ // DivineMC end - Multithreaded tracker + } + + // Paper start - per player mob count backoff +@@ -936,6 +946,21 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity)entity).moonrise$setTrackedEntity(null); // Paper - optimise entity tracker + } + ++ // DivineMC start - Multithreaded tracker ++ private final java.util.concurrent.ConcurrentLinkedQueue trackerMainThreadTasks = new java.util.concurrent.ConcurrentLinkedQueue<>(); ++ private boolean tracking = false; ++ ++ public void runOnTrackerMainThread(final Runnable runnable) { ++ //final boolean isOnMain = ca.spottedleaf.moonrise.common.util.TickThread.isTickThread(); ++ //System.out.println(isOnMain); ++ if (false && this.tracking) { // TODO: check here ++ this.trackerMainThreadTasks.add(runnable); ++ } else { ++ runnable.run(); ++ } ++ } ++ // DivineMC end - Multithreaded tracker ++ + // Paper start - optimise entity tracker + private void newTrackerTick() { + final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup entityLookup = (ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup)((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getEntityLookup();; +@@ -958,6 +983,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Paper end - optimise entity tracker + + protected void tick() { ++ // DivineMC start - Multithreaded tracker ++ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled) { ++ final ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel level = this.level; ++ space.bxteam.divinemc.tracker.MultithreadedTracker.tick(level); ++ return; ++ } ++ // DivineMC end - Multithreaded tracker + // Paper start - optimise entity tracker + if (true) { + this.newTrackerTick(); +@@ -1080,7 +1112,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + final Entity entity; + private final int range; + SectionPos lastSectionPos; +- public final Set seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl ++ // DivineMC start - Multithreaded tracker ++ public final Set seenBy = space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled ++ ? com.google.common.collect.Sets.newConcurrentHashSet() ++ : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl ++ // DivineMC end - Multithreaded tracker + + // Paper start - optimise entity tracker + private long lastChunkUpdate = -1L; +@@ -1107,21 +1143,55 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + this.lastTrackedChunk = chunk; + + final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); ++ final int playersLen = players.size(); // Ensure length won't change in the future tasks ++ ++ // DivineMC start - Multithreaded tracker ++ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled && space.bxteam.divinemc.configuration.DivineConfig.multithreadedCompatModeEnabled) { ++ final boolean isServerPlayer = this.entity instanceof ServerPlayer; ++ final boolean isRealPlayer = isServerPlayer && ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer) this.entity).moonrise$isRealPlayer(); ++ Runnable updatePlayerTasks = () -> { ++ for (int i = 0; i < playersLen; ++i) { ++ final ServerPlayer player = playersRaw[i]; ++ this.updatePlayer(player); ++ } + +- for (int i = 0, len = players.size(); i < len; ++i) { +- final ServerPlayer player = playersRaw[i]; +- this.updatePlayer(player); +- } ++ if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { ++ // need to purge any players possible not in the chunk list ++ for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { ++ final ServerPlayer player = conn.getPlayer(); ++ if (!players.contains(player)) { ++ this.removePlayer(player); ++ } ++ } ++ } ++ }; ++ ++ // Only update asynchronously for real player, and sync update for fake players ++ // This can fix compatibility issue with NPC plugins using real entity type, like Citizens ++ // To prevent visible issue with player type NPCs ++ // btw, still recommend to use packet based NPC plugins, like ZNPC Plus, Adyeshach, Fancy NPC, etc. ++ if (isRealPlayer || !isServerPlayer) { ++ space.bxteam.divinemc.tracker.MultithreadedTracker.getTrackerExecutor().execute(updatePlayerTasks); ++ } else { ++ updatePlayerTasks.run(); ++ } ++ } else { ++ for (int i = 0, len = players.size(); i < len; ++i) { ++ final ServerPlayer player = playersRaw[i]; ++ this.updatePlayer(player); ++ } + +- if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { +- // need to purge any players possible not in the chunk list +- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { +- final ServerPlayer player = conn.getPlayer(); +- if (!players.contains(player)) { +- this.removePlayer(player); ++ if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { ++ // need to purge any players possible not in the chunk list ++ for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { ++ final ServerPlayer player = conn.getPlayer(); ++ if (!players.contains(player)) { ++ this.removePlayer(player); ++ } + } + } + } ++ // DivineMC end - Multithreaded tracker + } + + @Override +@@ -1183,9 +1253,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + + public void broadcast(Packet packet) { +- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) { ++ // DivineMC start - Multithreaded tracker ++ for (ServerPlayerConnection serverPlayerConnection : this.seenBy.toArray(new ServerPlayerConnection[0])) { + serverPlayerConnection.send(packet); + } ++ // DivineMC end - Multithreaded tracker + } + + public void broadcastAndSend(Packet packet) { +@@ -1196,9 +1268,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + + public void broadcastRemoved() { +- for (ServerPlayerConnection serverPlayerConnection : this.seenBy) { ++ // DivineMC start - Multithreaded tracker ++ for (ServerPlayerConnection serverPlayerConnection : this.seenBy.toArray(new ServerPlayerConnection[0])) { + this.serverEntity.removePairing(serverPlayerConnection.getPlayer()); + } ++ // DivineMC end - Multithreaded tracker + } + + public void removePlayer(ServerPlayer player) { +@@ -1209,8 +1283,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + } + + public void updatePlayer(ServerPlayer player) { +- org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot ++ //org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // DivineMC - Multithreaded tracker - we don't need this + if (player != this.entity) { ++ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled && player == null) return; // DivineMC - Multithreaded tracker + // Paper start - remove allocation of Vec3D here + // Vec3 vec3 = player.position().subtract(this.entity.position()); + double vec3_dx = player.getX() - this.entity.getX(); +diff --git a/net/minecraft/server/level/ServerBossEvent.java b/net/minecraft/server/level/ServerBossEvent.java +index f106373ef3ac4a8685c2939c9e8361688a285913..7b0663caa87fa91c6eba3b88dfe9fe83a1cf5cbf 100644 +--- a/net/minecraft/server/level/ServerBossEvent.java ++++ b/net/minecraft/server/level/ServerBossEvent.java +@@ -13,7 +13,11 @@ import net.minecraft.util.Mth; + import net.minecraft.world.BossEvent; + + public class ServerBossEvent extends BossEvent { +- private final Set players = Sets.newHashSet(); ++ // DivineMC start - Multithreaded tracker - players can be removed in async tracking ++ private final Set players = space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled ++ ? Sets.newConcurrentHashSet() ++ : Sets.newHashSet(); ++ // DivineMC end - Multithreaded tracker + private final Set unmodifiablePlayers = Collections.unmodifiableSet(this.players); + public boolean visible = true; + +diff --git a/net/minecraft/server/level/ServerEntity.java b/net/minecraft/server/level/ServerEntity.java +index 6d2c892207c2299c64f59630fb7740d6407e76a7..50195569421527faf7d3a65cb6be3b559936af07 100644 +--- a/net/minecraft/server/level/ServerEntity.java ++++ b/net/minecraft/server/level/ServerEntity.java +@@ -110,8 +110,13 @@ public class ServerEntity { + .forEach( + removedPassenger -> { + if (removedPassenger instanceof ServerPlayer serverPlayer1) { +- serverPlayer1.connection +- .teleport(serverPlayer1.getX(), serverPlayer1.getY(), serverPlayer1.getZ(), serverPlayer1.getYRot(), serverPlayer1.getXRot()); ++ // DivineMC start - Multithreaded tracker ++ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled && Thread.currentThread() instanceof space.bxteam.divinemc.tracker.MultithreadedTracker.MultithreadedTrackerThread) { ++ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> serverPlayer1.connection.teleport(serverPlayer1.getX(), serverPlayer1.getY(), serverPlayer1.getZ(), serverPlayer1.getYRot(), serverPlayer1.getXRot())); ++ } else { ++ serverPlayer1.connection.teleport(serverPlayer1.getX(), serverPlayer1.getY(), serverPlayer1.getZ(), serverPlayer1.getYRot(), serverPlayer1.getXRot()); ++ } ++ // DivineMC end - Multithreaded tracker + } + } + ); +@@ -304,7 +309,11 @@ public class ServerEntity { + + public void removePairing(ServerPlayer player) { + this.entity.stopSeenByPlayer(player); +- player.connection.send(new ClientboundRemoveEntitiesPacket(this.entity.getId())); ++ // DivineMC start - Multithreaded tracker - send in main thread ++ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> ++ player.connection.send(new ClientboundRemoveEntitiesPacket(this.entity.getId())) ++ ); ++ // DivineMC end - Multithreaded tracker + } + + public void addPairing(ServerPlayer player) { +@@ -404,7 +413,11 @@ public class ServerEntity { + List> list = entityData.packDirty(); + if (list != null) { + this.trackedDataValues = entityData.getNonDefaultValues(); +- this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list)); ++ // DivineMC start - Multithreaded tracker - send in main thread ++ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> ++ this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list)) ++ ); ++ // DivineMC end - Multithreaded tracker + } + + if (this.entity instanceof LivingEntity) { +@@ -413,12 +426,17 @@ public class ServerEntity { + final Set attributesToSync = this.level.divinemcConfig.suppressErrorsFromDirtyAttributes ? Collections.synchronizedSet(attributes) : attributes; + // DivineMC end - Suppress errors from dirty attributes + if (!attributesToSync.isEmpty()) { +- // CraftBukkit start - Send scaled max health +- if (this.entity instanceof ServerPlayer serverPlayer) { +- serverPlayer.getBukkitEntity().injectScaledMaxHealth(attributesToSync, false); +- } +- // CraftBukkit end +- this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); ++ // DivineMC start - Multithreaded tracker ++ final Set copy = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(attributesToSync); ++ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> { ++ // CraftBukkit start - Send scaled max health ++ if (this.entity instanceof ServerPlayer serverPlayer) { ++ serverPlayer.getBukkitEntity().injectScaledMaxHealth(copy, false); ++ } ++ // CraftBukkit end ++ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), copy)); ++ }); ++ // DivineMC end - Multithreaded tracker + } + + attributes.clear(); +diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java +index 8127a71fd7d45541525be75e6699c2a5bae95f5f..e6a55f6fa9f4b827d14ae29c82cb7e30cfa5d56a 100644 +--- a/net/minecraft/server/level/ServerLevel.java ++++ b/net/minecraft/server/level/ServerLevel.java +@@ -2512,7 +2512,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + + @Override + public LevelEntityGetter getEntities() { +- org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot ++ //org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // DivineMC - Multithreaded tracker + return this.moonrise$getEntityLookup(); // Paper - rewrite chunk system + } + +diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index b2f2682c44eeed9ed4f5421113cc7cc704454ba5..33730967df54467b2f046f66a1590c7069e44c5d 100644 +--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -1806,7 +1806,7 @@ public class ServerGamePacketListenerImpl + } + + public void internalTeleport(PositionMoveRotation posMoveRotation, Set relatives) { +- org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper ++ //org.spigotmc.AsyncCatcher.catchOp("teleport"); // DivineMC - Multithreaded tracker + // Paper start - Prevent teleporting dead entities + if (this.player.isRemoved()) { + LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName()); +diff --git a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +index 8013594bb4844e7a8abf28123958e7f632d39341..55a2777ed76baef8fa6ed899e96028db0d4682f8 100644 +--- a/net/minecraft/world/entity/ai/attributes/AttributeInstance.java ++++ b/net/minecraft/world/entity/ai/attributes/AttributeInstance.java +@@ -24,8 +24,11 @@ public class AttributeInstance { + private final Map> modifiersByOperation = Maps.newEnumMap( + AttributeModifier.Operation.class + ); +- private final Map modifierById = new Object2ObjectArrayMap<>(); +- private final Map permanentModifiers = new Object2ObjectArrayMap<>(); ++ // DivineMC start - Multithreaded tracker ++ private final boolean multiThreadedTrackingEnabled = space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled; ++ private final Map modifierById = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>(); ++ private final Map permanentModifiers = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>(); ++ // DivineMC end - Multithreaded tracker + private double baseValue; + private boolean dirty = true; + private double cachedValue; +diff --git a/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/net/minecraft/world/entity/ai/attributes/AttributeMap.java +index a25d74592e89e3d6339479c6dc2b6f45d1932cfc..328474691eb42505cd535494c04fab0bdeb0a953 100644 +--- a/net/minecraft/world/entity/ai/attributes/AttributeMap.java ++++ b/net/minecraft/world/entity/ai/attributes/AttributeMap.java +@@ -19,9 +19,12 @@ import org.slf4j.Logger; + + public class AttributeMap { + private static final Logger LOGGER = LogUtils.getLogger(); +- private final Map, AttributeInstance> attributes = new Object2ObjectOpenHashMap<>(); +- private final Set attributesToSync = new ObjectOpenHashSet<>(); +- private final Set attributesToUpdate = new ObjectOpenHashSet<>(); ++ // DivineMC start - Multithreaded tracker ++ private final boolean multiThreadedTrackingEnabled = space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled; ++ private final Map, AttributeInstance> attributes = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); ++ private final Set attributesToSync = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); ++ private final Set attributesToUpdate = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); ++ // DivineMC end - Multithreaded tracker + private final AttributeSupplier supplier; + private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables + diff --git a/divinemc-server/minecraft-patches/features/0013-Implement-Linear-region-format.patch b/divinemc-server/minecraft-patches/features/0013-Implement-Linear-region-format.patch new file mode 100644 index 0000000..9d68c2c --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0013-Implement-Linear-region-format.patch @@ -0,0 +1,365 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sat, 18 Jan 2025 01:00:23 +0300 +Subject: [PATCH] Implement Linear region format + +Linear is a region file format that uses ZSTD compression instead of ZLIB. This format saves about 50% of disk space. +Documentation: https://github.com/xymb-endcrystalme/LinearRegionFileFormatTools + +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java +index a814512fcfb85312474ae2c2c21443843bf57831..c2bc8464cf3f1722394d55d91f638f576ee47f49 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java +@@ -8,9 +8,9 @@ public interface ChunkSystemRegionFileStorage { + + public boolean moonrise$doesRegionFileNotExistNoIO(final int chunkX, final int chunkZ); + +- public RegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ); ++ public space.bxteam.divinemc.region.AbstractRegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ); // DivineMC - linear region format + +- public RegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException; ++ public space.bxteam.divinemc.region.AbstractRegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException; // DivineMC - linear region format + + public MoonriseRegionFileIO.RegionDataController.WriteData moonrise$startWrite( + final int chunkX, final int chunkZ, final CompoundTag compound +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java +index 1acea58838f057ab87efd103cbecb6f5aeaef393..4fcf988346b8bf60ac0ad1165e18ed86fe52fd2d 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/io/MoonriseRegionFileIO.java +@@ -1462,7 +1462,7 @@ public final class MoonriseRegionFileIO { + + public static interface IORunnable { + +- public void run(final RegionFile regionFile) throws IOException; ++ public void run(final space.bxteam.divinemc.region.AbstractRegionFile regionFile) throws IOException; // DivineMC - linear region format + + } + } +diff --git a/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java b/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java +index 51c126735ace8fdde89ad97b5cab62f244212db0..cf497c3919a1d5114a18474f04ccce182a6b7e0b 100644 +--- a/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java ++++ b/ca/spottedleaf/moonrise/patches/chunk_system/storage/ChunkSystemChunkBuffer.java +@@ -8,5 +8,5 @@ public interface ChunkSystemChunkBuffer { + + public void moonrise$setWriteOnClose(final boolean value); + +- public void moonrise$write(final RegionFile regionFile) throws IOException; ++ public void moonrise$write(final space.bxteam.divinemc.region.AbstractRegionFile regionFile) throws IOException; // DivineMC - linear region format + } +diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java +index d04c06fafd133f773f311e7c2708fa8b049da67c..b3f2e7bb4519cc078d3cede11bce232f1255c6a0 100644 +--- a/net/minecraft/server/MinecraftServer.java ++++ b/net/minecraft/server/MinecraftServer.java +@@ -950,10 +950,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> progressMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap<>()); + volatile Component status = Component.translatable("optimizeWorld.stage.counting"); +- static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$"); ++ static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.(linear | mca)$"); // DivineMC - linear region format + final DimensionDataStorage overworldDataStorage; + + public WorldUpgrader( +@@ -261,7 +261,7 @@ public class WorldUpgrader implements AutoCloseable { + } + + private static List getAllChunkPositions(RegionStorageInfo regionStorageInfo, Path path) { +- File[] files = path.toFile().listFiles((directory, filename) -> filename.endsWith(".mca")); ++ File[] files = path.toFile().listFiles((directory, filename) -> filename.endsWith(".linear") || filename.endsWith(".mca")); // DivineMC - linear region format + if (files == null) { + return List.of(); + } else { +@@ -274,7 +274,7 @@ public class WorldUpgrader implements AutoCloseable { + int i1 = Integer.parseInt(matcher.group(2)) << 5; + List list1 = Lists.newArrayList(); + +- try (RegionFile regionFile = new RegionFile(regionStorageInfo, file.toPath(), path, true)) { ++ try (space.bxteam.divinemc.region.AbstractRegionFile regionFile = space.bxteam.divinemc.region.AbstractRegionFileFactory.getAbstractRegionFile(regionStorageInfo, file.toPath(), path, true)) { // DivineMC - linear region format + for (int i2 = 0; i2 < 32; i2++) { + for (int i3 = 0; i3 < 32; i3++) { + ChunkPos chunkPos = new ChunkPos(i2 + i, i3 + i1); +@@ -322,7 +322,7 @@ public class WorldUpgrader implements AutoCloseable { + + protected abstract boolean tryProcessOnePosition(T chunkStorage, ChunkPos chunkPos, ResourceKey dimension); + +- private void onFileFinished(RegionFile regionFile) { ++ private void onFileFinished(space.bxteam.divinemc.region.AbstractRegionFile regionFile) { // DivineMC - linear region format + if (WorldUpgrader.this.recreateRegionFiles) { + if (this.previousWriteFuture != null) { + this.previousWriteFuture.join(); +@@ -424,7 +424,7 @@ public class WorldUpgrader implements AutoCloseable { + } + } + +- record FileToUpgrade(RegionFile file, List chunksToUpgrade) { ++ record FileToUpgrade(space.bxteam.divinemc.region.AbstractRegionFile file, List chunksToUpgrade) { // DivineMC - linear region format + } + + class PoiUpgrader extends WorldUpgrader.SimpleRegionStorageUpgrader { +diff --git a/net/minecraft/world/level/chunk/storage/RegionFile.java b/net/minecraft/world/level/chunk/storage/RegionFile.java +index c72494e757a9dc50e053dbc873f7b30e83d5cb8c..236035219dd2442592bb94994a44fab712b5ca9d 100644 +--- a/net/minecraft/world/level/chunk/storage/RegionFile.java ++++ b/net/minecraft/world/level/chunk/storage/RegionFile.java +@@ -22,7 +22,7 @@ import net.minecraft.util.profiling.jfr.JvmProfiler; + import net.minecraft.world.level.ChunkPos; + import org.slf4j.Logger; + +-public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile { // Paper - rewrite chunk system ++public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile, space.bxteam.divinemc.region.AbstractRegionFile { // Paper - rewrite chunk system // DivineMC - linear region format + private static final Logger LOGGER = LogUtils.getLogger(); + public static final int MAX_CHUNK_SIZE = 500 * 1024 * 1024; // Paper - don't write garbage data to disk if writing serialization fails + private static final int SECTOR_BYTES = 4096; +@@ -904,7 +904,7 @@ public class RegionFile implements AutoCloseable, ca.spottedleaf.moonrise.patche + } + + @Override +- public final void moonrise$write(final RegionFile regionFile) throws IOException { ++ public final void moonrise$write(final space.bxteam.divinemc.region.AbstractRegionFile regionFile) throws IOException { // DivineMC - linear region format + regionFile.write(this.pos, ByteBuffer.wrap(this.buf, 0, this.count)); + } + // Paper end - rewrite chunk system +diff --git a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +index 6ebd1300c2561116b83cb2472ac7939ead36d576..6da756cb0a406a76151a5b2f624e08592f35d835 100644 +--- a/net/minecraft/world/level/chunk/storage/RegionFileStorage.java ++++ b/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +@@ -18,7 +18,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger(); // Paper + 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<>(); // DivineMC - linear region format + private final RegionStorageInfo info; + private final Path folder; + private final boolean sync; +@@ -33,7 +33,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + @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")) { // DivineMC - linear region format + return null; + } + +@@ -57,9 +57,14 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + private static final int REGION_SHIFT = 5; + private static final int MAX_NON_EXISTING_CACHE = 1024 * 4; + private final it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet nonExistingRegionFiles = new it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet(); +- private static String getRegionFileName(final int chunkX, final int chunkZ) { ++ // DivineMC start - Linear region format ++ private static String getRegionFileName(final RegionStorageInfo info, final int chunkX, final int chunkZ) { ++ if (info.regionFormat().equals(space.bxteam.divinemc.region.RegionFileFormat.LINEAR)) { ++ return "r." + (chunkX >> REGION_SHIFT) + "." + (chunkZ >> REGION_SHIFT) + ".linear"; ++ } + return "r." + (chunkX >> REGION_SHIFT) + "." + (chunkZ >> REGION_SHIFT) + ".mca"; + } ++ // DivineMC end - Linear region format + + private boolean doesRegionFilePossiblyExist(final long position) { + synchronized (this.nonExistingRegionFiles) { +@@ -93,15 +98,15 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + } + + @Override +- public synchronized final RegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ) { ++ public synchronized final space.bxteam.divinemc.region.AbstractRegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ) { // DivineMC - linear region format + return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT)); + } + + @Override +- public synchronized final RegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException { ++ public synchronized final space.bxteam.divinemc.region.AbstractRegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException { // DivineMC - linear region format + final long key = ChunkPos.asLong(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT); + +- RegionFile ret = this.regionCache.getAndMoveToFirst(key); ++ space.bxteam.divinemc.region.AbstractRegionFile ret = this.regionCache.getAndMoveToFirst(key); // DivineMC - linear region format + if (ret != null) { + return ret; + } +@@ -114,7 +119,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + this.regionCache.removeLast().close(); + } + +- final Path regionPath = this.folder.resolve(getRegionFileName(chunkX, chunkZ)); ++ final Path regionPath = this.folder.resolve(getRegionFileName(this.info, chunkX, chunkZ)); // DivineMC - linear region format + + if (!java.nio.file.Files.exists(regionPath)) { + this.markNonExisting(key); +@@ -125,7 +130,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + + FileUtil.createDirectoriesSafe(this.folder); + +- ret = new RegionFile(this.info, regionPath, this.folder, this.sync); ++ ret = space.bxteam.divinemc.region.AbstractRegionFileFactory.getAbstractRegionFile(this.info, regionPath, this.folder, this.sync); // DivineMC - linear region format + + this.regionCache.putAndMoveToFirst(key, ret); + +@@ -144,11 +149,11 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + } + + final ChunkPos pos = new ChunkPos(chunkX, chunkZ); +- final RegionFile regionFile = this.getRegionFile(pos); ++ final space.bxteam.divinemc.region.AbstractRegionFile regionFile = this.getRegionFile(pos); // DivineMC - linear region format + + // note: not required to keep regionfile loaded after this call, as the write param takes a regionfile as input + // (and, the regionfile parameter is unused for writing until the write call) +- final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData writeData = ((ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile)regionFile).moonrise$startWrite(compound, pos); ++ final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData writeData = regionFile.moonrise$startWrite(compound, pos); // DivineMC - linear region format + + try { // Paper - implement RegionFileSizeException + try { +@@ -178,7 +183,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + ) throws IOException { + final ChunkPos pos = new ChunkPos(chunkX, chunkZ); + if (writeData.result() == ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData.WriteResult.DELETE) { +- final RegionFile regionFile = this.moonrise$getRegionFileIfExists(chunkX, chunkZ); ++ final space.bxteam.divinemc.region.AbstractRegionFile regionFile = this.moonrise$getRegionFileIfExists(chunkX, chunkZ); // DivineMC - linear region format + if (regionFile != null) { + regionFile.clear(pos); + } // else: didn't exist +@@ -193,7 +198,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + public final ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.ReadData moonrise$readData( + final int chunkX, final int chunkZ + ) throws IOException { +- final RegionFile regionFile = this.moonrise$getRegionFileIfExists(chunkX, chunkZ); ++ final space.bxteam.divinemc.region.AbstractRegionFile regionFile = this.moonrise$getRegionFileIfExists(chunkX, chunkZ); // DivineMC - linear region format + + final DataInputStream input = regionFile == null ? null : regionFile.getChunkDataInputStream(new ChunkPos(chunkX, chunkZ)); + +@@ -237,7 +242,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + } + // Paper end - rewrite chunk system + // Paper start - rewrite chunk system +- public RegionFile getRegionFile(ChunkPos chunkcoordintpair) throws IOException { ++ public space.bxteam.divinemc.region.AbstractRegionFile getRegionFile(ChunkPos chunkcoordintpair) throws IOException { // DivineMC - linear region format + return this.getRegionFile(chunkcoordintpair, false); + } + // Paper end - rewrite chunk system +@@ -249,7 +254,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + this.isChunkData = isChunkDataFolder(this.folder); // Paper - recalculate region file headers + } + +- @org.jetbrains.annotations.Contract("_, false -> !null") @Nullable private RegionFile getRegionFile(ChunkPos chunkPos, boolean existingOnly) throws IOException { // CraftBukkit ++ @org.jetbrains.annotations.Contract("_, false -> !null") @Nullable private space.bxteam.divinemc.region.AbstractRegionFile getRegionFile(ChunkPos chunkPos, boolean existingOnly) throws IOException { // CraftBukkit // DivineMC - linear region format + // Paper start - rewrite chunk system + if (existingOnly) { + return this.moonrise$getRegionFileIfExists(chunkPos.x, chunkPos.z); +@@ -257,7 +262,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + synchronized (this) { + final long key = ChunkPos.asLong(chunkPos.x >> REGION_SHIFT, chunkPos.z >> REGION_SHIFT); + +- RegionFile ret = this.regionCache.getAndMoveToFirst(key); ++ space.bxteam.divinemc.region.AbstractRegionFile ret = this.regionCache.getAndMoveToFirst(key); // DivineMC - linear region format + if (ret != null) { + return ret; + } +@@ -266,13 +271,13 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + this.regionCache.removeLast().close(); + } + +- final Path regionPath = this.folder.resolve(getRegionFileName(chunkPos.x, chunkPos.z)); ++ final Path regionPath = this.folder.resolve(getRegionFileName(this.info, chunkPos.x, chunkPos.z)); // DivineMC - linear region format + + this.createRegionFile(key); + + FileUtil.createDirectoriesSafe(this.folder); + +- ret = new RegionFile(this.info, regionPath, this.folder, this.sync); ++ ret = space.bxteam.divinemc.region.AbstractRegionFileFactory.getAbstractRegionFile(this.info, regionPath, this.folder, this.sync); // DivineMC - linear region format + + this.regionCache.putAndMoveToFirst(key, ret); + +@@ -286,7 +291,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + 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 DIVINEMC - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); // DivineMC - Rebrand + } + +- private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { ++ private static CompoundTag readOversizedChunk(space.bxteam.divinemc.region.AbstractRegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { + synchronized (regionfile) { + try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) { + CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z); +@@ -321,7 +326,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + @Nullable + public CompoundTag read(ChunkPos chunkPos) 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); ++ space.bxteam.divinemc.region.AbstractRegionFile regionFile = this.getRegionFile(chunkPos, true); // DivineMC - linear region format + if (regionFile == null) { + return null; + } +@@ -360,7 +365,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + + public void scanChunk(ChunkPos chunkPos, StreamTagVisitor visitor) 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); ++ space.bxteam.divinemc.region.AbstractRegionFile regionFile = this.getRegionFile(chunkPos, true); // DivineMC - linear region format + if (regionFile == null) { + return; + } +@@ -374,7 +379,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + } + + public void write(ChunkPos chunkPos, @Nullable CompoundTag chunkData) throws IOException { // Paper - rewrite chunk system - public +- RegionFile regionFile = this.getRegionFile(chunkPos, chunkData == null); // CraftBukkit // Paper - rewrite chunk system ++ space.bxteam.divinemc.region.AbstractRegionFile regionFile = this.getRegionFile(chunkPos, chunkData == null); // CraftBukkit // Paper - rewrite chunk system // DivineMC - linear region format + // Paper start - rewrite chunk system + if (regionFile == null) { + // if the RegionFile doesn't exist, no point in deleting from it +@@ -404,7 +409,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + // Paper start - rewrite chunk system + synchronized (this) { + final ExceptionCollector exceptionCollector = new ExceptionCollector<>(); +- for (final RegionFile regionFile : this.regionCache.values()) { ++ for (final space.bxteam.divinemc.region.AbstractRegionFile regionFile : this.regionCache.values()) { // DivineMC - linear region format + try { + regionFile.close(); + } catch (final IOException ex) { +@@ -420,7 +425,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + // Paper start - rewrite chunk system + synchronized (this) { + final ExceptionCollector exceptionCollector = new ExceptionCollector<>(); +- for (final RegionFile regionFile : this.regionCache.values()) { ++ for (final space.bxteam.divinemc.region.AbstractRegionFile regionFile : this.regionCache.values()) { // DivineMC - linear region format + try { + regionFile.flush(); + } catch (final IOException ex) { +diff --git a/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java b/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java +index 6111631c6673948b266286894603cc5e30451b02..b49781472b34176503505e2cf2fbfd99f1d24e48 100644 +--- a/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java ++++ b/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java +@@ -7,4 +7,20 @@ public record RegionStorageInfo(String level, ResourceKey dimension, Stri + public RegionStorageInfo withTypeSuffix(String suffix) { + return new RegionStorageInfo(this.level, this.dimension, this.type + suffix); + } ++ ++ // DivineMC start - Linear Region format ++ public space.bxteam.divinemc.region.RegionFileFormat regionFormat() { ++ return ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(level)) ++ .getHandle() ++ .divinemcConfig ++ .regionFormatName; ++ } ++ ++ public int linearCompressionLevel() { ++ return ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(level)) ++ .getHandle() ++ .divinemcConfig ++ .linearCompressionLevel; ++ } ++ // DivineMC end - Linear Region format + } diff --git a/divinemc-server/minecraft-patches/features/0014-Snowball-and-Egg-knockback.patch b/divinemc-server/minecraft-patches/features/0014-Snowball-and-Egg-knockback.patch new file mode 100644 index 0000000..eba2da4 --- /dev/null +++ b/divinemc-server/minecraft-patches/features/0014-Snowball-and-Egg-knockback.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sat, 18 Jan 2025 15:44:29 +0300 +Subject: [PATCH] Snowball and Egg knockback + + +diff --git a/net/minecraft/world/entity/projectile/Snowball.java b/net/minecraft/world/entity/projectile/Snowball.java +index 1d399532c67c213c95c06837b0c7855384f1a25c..9e9afd77ba50c2ab06e97f1c80b1290fb8f91715 100644 +--- a/net/minecraft/world/entity/projectile/Snowball.java ++++ b/net/minecraft/world/entity/projectile/Snowball.java +@@ -54,6 +54,12 @@ public class Snowball extends ThrowableItemProjectile { + Entity entity = result.getEntity(); + int i = entity.level().purpurConfig.snowballDamage >= 0 ? entity.level().purpurConfig.snowballDamage : entity instanceof Blaze ? 3 : 0; // Purpur - Add configurable snowball damage + entity.hurt(this.damageSources().thrown(this, this.getOwner()), i); ++ // DivineMC start - Make snowball can knockback player ++ if (this.level().divinemcConfig.snowballCanKnockback && entity instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { ++ entity.hurt(this.damageSources().thrown(this, this.getOwner()), 0.0000001F); ++ serverPlayer.knockback(0.4000000059604645D, this.getX() - entity.getX(), this.getZ() - entity.getZ()); ++ } ++ // DivineMC end - Make snowball can knockback player + } + + // Purpur start - options to extinguish fire blocks with snowballs - borrowed and modified code from ThrownPotion#onHitBlock and ThrownPotion#dowseFire +diff --git a/net/minecraft/world/entity/projectile/ThrownEgg.java b/net/minecraft/world/entity/projectile/ThrownEgg.java +index 76481c0e77fc3a2e4be8eeb9de8d1e6de5507c64..7165bad72f761a0a3b2168d6df5f154b4a82d74e 100644 +--- a/net/minecraft/world/entity/projectile/ThrownEgg.java ++++ b/net/minecraft/world/entity/projectile/ThrownEgg.java +@@ -52,7 +52,14 @@ public class ThrownEgg extends ThrowableItemProjectile { + @Override + protected void onHitEntity(EntityHitResult result) { + super.onHitEntity(result); ++ net.minecraft.world.entity.Entity entity = result.getEntity(); // DivineMC - make egg can knockback player + result.getEntity().hurt(this.damageSources().thrown(this, this.getOwner()), 0.0F); ++ // DivineMC start - Make egg can knockback player ++ if (this.level().divinemcConfig.eggCanKnockback && entity instanceof net.minecraft.server.level.ServerPlayer serverPlayer) { ++ entity.hurt(this.damageSources().thrown(this, this.getOwner()), 0.0000001F); ++ serverPlayer.knockback(0.4000000059604645D, this.getX() - entity.getX(), this.getZ() - entity.getZ()); ++ } ++ // DivineMC end - Make egg can knockback player + } + + @Override diff --git a/patches/server/0037-Parchment-Make-FixLight-use-action-bar.patch b/divinemc-server/minecraft-patches/sources/io/papermc/paper/command/subcommands/FixLightCommand.java.patch similarity index 56% rename from patches/server/0037-Parchment-Make-FixLight-use-action-bar.patch rename to divinemc-server/minecraft-patches/sources/io/papermc/paper/command/subcommands/FixLightCommand.java.patch index 0d6d2e4..502b179 100644 --- a/patches/server/0037-Parchment-Make-FixLight-use-action-bar.patch +++ b/divinemc-server/minecraft-patches/sources/io/papermc/paper/command/subcommands/FixLightCommand.java.patch @@ -1,15 +1,6 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Fri, 21 Jun 2024 04:19:11 +0300 -Subject: [PATCH] Parchment: Make FixLight use action bar - -Original project: https://github.com/ProjectEdenGG/Parchment - -diff --git a/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java -index 85950a1aa732ab8c01ad28bec9e0de140e1a172e..a6df6eeeda88c50894e03742e25aa5652a770ba9 100644 ---- a/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java -+++ b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java -@@ -95,17 +95,20 @@ public final class FixLightCommand implements PaperSubcommand { +--- a/io/papermc/paper/command/subcommands/FixLightCommand.java ++++ b/io/papermc/paper/command/subcommands/FixLightCommand.java +@@ -95,17 +_,22 @@ ((StarLightLightingProvider)lightengine).starlight$serverRelightChunks(chunks, (final ChunkPos chunkPos) -> { ++relitChunks[0]; @@ -17,13 +8,14 @@ index 85950a1aa732ab8c01ad28bec9e0de140e1a172e..a6df6eeeda88c50894e03742e25aa565 - text("Relit chunk ", BLUE), text(chunkPos.toString()), - text(", progress: ", BLUE), text(ONE_DECIMAL_PLACES.get().format(100.0 * (double) (relitChunks[0]) / (double) pending[0]) + "%") - )); ++ // DivineMC start - Make FixLight use action bar + sender.getBukkitEntity().sendActionBar(text().color(DARK_AQUA).append( + text("Relighting Chunks: ", DARK_AQUA), text(chunkPos.toString()), + text(" " + relitChunks[0], net.kyori.adventure.text.format.NamedTextColor.YELLOW), + text("/", DARK_AQUA), + text(pending[0] + " ", net.kyori.adventure.text.format.NamedTextColor.YELLOW), + text("(" + (int) (Math.round(100.0 * (double) (relitChunks[0]) / (double) pending[0])) + "%)", net.kyori.adventure.text.format.NamedTextColor.YELLOW) -+ )); // DivineMC // Parchment - Make FixLight use action bar ++ )); // DivineMC end - Make FixLight use action bar }, (final int totalRelit) -> { final long end = System.nanoTime(); @@ -31,9 +23,10 @@ index 85950a1aa732ab8c01ad28bec9e0de140e1a172e..a6df6eeeda88c50894e03742e25aa565 - text("Relit ", BLUE), text(totalRelit), - text(" chunks. Took ", BLUE), text(ONE_DECIMAL_PLACES.get().format(1.0e-6 * (end - start)) + "ms") - )); -+ text("Relit ", DARK_AQUA), text(totalRelit, net.kyori.adventure.text.format.NamedTextColor.YELLOW), -+ text(" chunks. Took ", DARK_AQUA), text(ONE_DECIMAL_PLACES.get().format(1.0e-6 * (end - start)) + "ms", net.kyori.adventure.text.format.NamedTextColor.YELLOW) -+ )); // DivineMC // Parchment - Make FixLight use action bar ++ // DivineMC start - Make FixLight use action bar ++ text("Relit ", DARK_AQUA), text(totalRelit, net.kyori.adventure.text.format.NamedTextColor.YELLOW), ++ text(" chunks. Took ", DARK_AQUA), text(ONE_DECIMAL_PLACES.get().format(1.0e-6 * (end - start)) + "ms", net.kyori.adventure.text.format.NamedTextColor.YELLOW) ++ )); // DivineMC end - Make FixLight use action bar if (done != null) { done.run(); } diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch new file mode 100644 index 0000000..f049cdd --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/server/dedicated/DedicatedServer.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/net/minecraft/server/dedicated/DedicatedServer.java +@@ -326,7 +_,7 @@ + String proxyFlavor = (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) ? "Velocity" : "BungeeCord"; + String proxyLink = (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) ? "https://docs.papermc.io/velocity/security" : "http://www.spigotmc.org/wiki/firewall-guide/"; + // Paper end - Add Velocity IP Forwarding Support +- if (!this.usesAuthentication()) { ++ if (!io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode()) { // DivineMC - if server uses proxy (velocity or bungee), disable offline warning + LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); + LOGGER.warn("The server will make no attempt to authenticate usernames. Beware."); + // Spigot start diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ChunkMap.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ChunkMap.java.patch new file mode 100644 index 0000000..9c3b8e0 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ChunkMap.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/server/level/ChunkMap.java ++++ b/net/minecraft/server/level/ChunkMap.java +@@ -129,7 +_,7 @@ + public final AtomicInteger tickingGenerated = new AtomicInteger(); // Paper - public + private final String storageName; + private final PlayerMap playerMap = new PlayerMap(); +- public final Int2ObjectMap entityMap = new Int2ObjectOpenHashMap<>(); ++ public final Int2ObjectMap entityMap = new it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap<>(); // DivineMC - vmp: use linked map for entity trackers for faster iteration + private final Long2ByteMap chunkTypeCache = new Long2ByteOpenHashMap(); + // Paper - rewrite chunk system + public int serverViewDistance; diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ServerEntity.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ServerEntity.java.patch new file mode 100644 index 0000000..4404eed --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ServerEntity.java.patch @@ -0,0 +1,23 @@ +--- a/net/minecraft/server/level/ServerEntity.java ++++ b/net/minecraft/server/level/ServerEntity.java +@@ -408,7 +_,10 @@ + } + + if (this.entity instanceof LivingEntity) { +- Set attributesToSync = ((LivingEntity)this.entity).getAttributes().getAttributesToSync(); ++ // DivineMC start - Suppress errors from dirty attributes ++ Set attributes = ((LivingEntity) this.entity).getAttributes().getAttributesToSync(); ++ final Set attributesToSync = this.level.divinemcConfig.suppressErrorsFromDirtyAttributes ? Collections.synchronizedSet(attributes) : attributes; ++ // DivineMC end - Suppress errors from dirty attributes + if (!attributesToSync.isEmpty()) { + // CraftBukkit start - Send scaled max health + if (this.entity instanceof ServerPlayer serverPlayer) { +@@ -418,7 +_,7 @@ + this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), attributesToSync)); + } + +- attributesToSync.clear(); ++ attributes.clear(); + } + } + diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayer.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayer.java.patch new file mode 100644 index 0000000..f759c2f --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/server/level/ServerPlayer.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -2259,6 +_,7 @@ + this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, gameMode.getId())); + if (gameMode == GameType.SPECTATOR) { + this.removeEntitiesOnShoulder(); ++ this.stopSleeping(); // DivineMC - Fix MC-119417 + this.stopRiding(); + EnchantmentHelper.stopLocationBasedEffects(this); + } else { diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch new file mode 100644 index 0000000..a3499c5 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -0,0 +1,44 @@ +--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -572,7 +_,7 @@ + return; + } + // Paper end - Prevent moving into unloaded chunks +- if (d7 - d6 > Math.max(100.0, Math.pow((double) (org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed), 2)) && !this.isSingleplayerOwner()) { ++ if (!space.bxteam.divinemc.configuration.DivineConfig.disableMovedWronglyThreshold && d7 - d6 > Math.max(100.0, Math.pow((double) (org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed), 2)) && !this.isSingleplayerOwner()) { // DivineMC - Option to disable moved wrongly threshold + // CraftBukkit end + LOGGER.warn( + "{} (vehicle of {}) moved too quickly! {},{},{}", rootVehicle.getName().getString(), this.player.getName().getString(), d3, d4, d5 +@@ -602,7 +_,7 @@ + d5 = d2 - rootVehicle.getZ(); + d7 = d3 * d3 + d4 * d4 + d5 * d5; + boolean flag2 = false; +- if (d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot ++ if (!space.bxteam.divinemc.configuration.DivineConfig.disableMovedWronglyThreshold && d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot // DivineMC - Option to disable moved wrongly threshold + flag2 = true; // Paper - diff on change, this should be moved wrongly + LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getName().getString(), this.player.getName().getString(), Math.sqrt(d7)); + } +@@ -2397,6 +_,7 @@ + } + + private void tryHandleChat(String message, Runnable handler, boolean sync) { // CraftBukkit ++ if (ServerGamePacketListenerImpl.isLog4ShellExploit(message)) return; // DivineMC - Block Log4Shell exploit + if (isChatMessageIllegal(message)) { + this.disconnectAsync(Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper - add proper async disconnect + } else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales +@@ -2424,6 +_,15 @@ + return optional; + } + } ++ ++ // DivineMC start - Block Log4Shell exploit ++ public static boolean isLog4ShellExploit(String message) { ++ java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(".*\\$\\{[^}]*}.*"); ++ java.util.regex.Matcher matcher = pattern.matcher(message); ++ ++ return matcher.find(); ++ } ++ // DivineMC end - Block Log4Shell exploit + + public static boolean isChatMessageIllegal(String message) { + for (int i = 0; i < message.length(); i++) { diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch new file mode 100644 index 0000000..445899c --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/server/network/ServerLoginPacketListenerImpl.java.patch @@ -0,0 +1,12 @@ +--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java ++++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +@@ -172,7 +_,8 @@ + public void handleHello(ServerboundHelloPacket packet) { + Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet"); + // Paper start - Validate usernames +- if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ++ if (!space.bxteam.divinemc.configuration.DivineConfig.removeVanillaUsernameCheck // DivineMC - Remove vanilla username check ++ && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() + && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.performUsernameValidation + && !this.iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation) { + Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username"); diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/server/players/StoredUserList.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/server/players/StoredUserList.java.patch new file mode 100644 index 0000000..a19a24d --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/server/players/StoredUserList.java.patch @@ -0,0 +1,29 @@ +--- a/net/minecraft/server/players/StoredUserList.java ++++ b/net/minecraft/server/players/StoredUserList.java +@@ -97,13 +_,20 @@ + } + + public void save() throws IOException { +- this.removeExpired(); // Paper - remove expired values before saving +- JsonArray jsonArray = new JsonArray(); +- this.map.values().stream().map(storedEntry -> Util.make(new JsonObject(), storedEntry::serialize)).forEach(jsonArray::add); ++ // DivineMC start - Save json list async ++ Runnable saveTask = () -> { ++ this.removeExpired(); // Paper - remove expired values before saving ++ JsonArray jsonArray = new JsonArray(); ++ this.map.values().stream().map(storedEntry -> Util.make(new JsonObject(), storedEntry::serialize)).forEach(jsonArray::add); + +- try (BufferedWriter writer = Files.newWriter(this.file, StandardCharsets.UTF_8)) { +- GSON.toJson(jsonArray, GSON.newJsonWriter(writer)); +- } ++ try (BufferedWriter writer = Files.newWriter(this.file, StandardCharsets.UTF_8)) { ++ GSON.toJson(jsonArray, GSON.newJsonWriter(writer)); ++ } catch (java.io.IOException e) { ++ StoredUserList.LOGGER.warn("Failed to async save " + this.file, e); ++ } ++ }; ++ io.papermc.paper.util.MCUtil.scheduleAsyncTask(saveTask); ++ // DivineMC end - Save json list async + } + + public void load() throws IOException { diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/server/rcon/RconConsoleSource.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/server/rcon/RconConsoleSource.java.patch new file mode 100644 index 0000000..8711080 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/server/rcon/RconConsoleSource.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/server/rcon/RconConsoleSource.java ++++ b/net/minecraft/server/rcon/RconConsoleSource.java +@@ -51,7 +_,7 @@ + + @Override + public void sendSystemMessage(Component component) { +- this.buffer.append(component.getString()); ++ this.buffer.append(component.getString()).append(System.lineSeparator()); // DivineMC - Fix MC-7569 + } + + @Override diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/util/ClassInstanceMultiMap.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/util/ClassInstanceMultiMap.java.patch new file mode 100644 index 0000000..ddce835 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/util/ClassInstanceMultiMap.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/util/ClassInstanceMultiMap.java ++++ b/net/minecraft/util/ClassInstanceMultiMap.java +@@ -14,7 +_,7 @@ + import net.minecraft.Util; + + public class ClassInstanceMultiMap extends AbstractCollection { +- private final Map, List> byClass = Maps.newHashMap(); ++ private final Map, List> byClass = new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(); // DivineMC - lithium: collections.entity_by_type + private final Class baseClass; + private final List allInstances = Lists.newArrayList(); + diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/util/Mth.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/util/Mth.java.patch new file mode 100644 index 0000000..2c4477a --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/util/Mth.java.patch @@ -0,0 +1,25 @@ +--- a/net/minecraft/util/Mth.java ++++ b/net/minecraft/util/Mth.java +@@ -29,7 +_,7 @@ + public static final Vector3f Y_AXIS = new Vector3f(0.0F, 1.0F, 0.0F); + public static final Vector3f X_AXIS = new Vector3f(1.0F, 0.0F, 0.0F); + public static final Vector3f Z_AXIS = new Vector3f(0.0F, 0.0F, 1.0F); +- private static final float[] SIN = Util.make(new float[65536], floats -> { ++ public static final float[] SIN = Util.make(new float[65536], floats -> { // DivineMC - lithium: math.sine_lut + for (int i1 = 0; i1 < floats.length; i1++) { + floats[i1] = (float)Math.sin(i1 * Math.PI * 2.0 / 65536.0); + } +@@ -46,11 +_,11 @@ + private static final double[] COS_TAB = new double[257]; + + public static float sin(float value) { +- return SIN[(int)(value * 10430.378F) & 65535]; ++ return space.bxteam.divinemc.util.lithium.CompactSineLUT.sin(value); // DivineMC - lithium: math.sine_lut + } + + public static float cos(float value) { +- return SIN[(int)(value * 10430.378F + 16384.0F) & 65535]; ++ return space.bxteam.divinemc.util.lithium.CompactSineLUT.cos(value); // DivineMC - lithium: math.sine_lut + } + + public static float sqrt(float value) { diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/util/thread/BlockableEventLoop.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/util/thread/BlockableEventLoop.java.patch new file mode 100644 index 0000000..e8a5a70 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/util/thread/BlockableEventLoop.java.patch @@ -0,0 +1,21 @@ +--- a/net/minecraft/util/thread/BlockableEventLoop.java ++++ b/net/minecraft/util/thread/BlockableEventLoop.java +@@ -22,7 +_,7 @@ + import org.slf4j.Logger; + + public abstract class BlockableEventLoop implements ProfilerMeasured, TaskScheduler, Executor { +- public static final long BLOCK_TIME_NANOS = 100000L; ++ public static final long BLOCK_TIME_NANOS = 2000000L; // DivineMC - Fix MC-183518 + private final String name; + private static final Logger LOGGER = LogUtils.getLogger(); + private final Queue pendingRunnables = Queues.newConcurrentLinkedQueue(); +@@ -146,8 +_,7 @@ + } + + protected void waitForTasks() { +- Thread.yield(); +- LockSupport.parkNanos("waiting for tasks", 100000L); ++ LockSupport.parkNanos("waiting for tasks", 2000000L); // DivineMC - Fix MC-183518 + } + + protected void doRunTask(R task) { diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/Entity.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/Entity.java.patch new file mode 100644 index 0000000..714c8df --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/Entity.java.patch @@ -0,0 +1,31 @@ +--- a/net/minecraft/world/entity/Entity.java ++++ b/net/minecraft/world/entity/Entity.java +@@ -235,6 +_,7 @@ + public float yRotO; + public float xRotO; + private AABB bb = INITIAL_AABB; ++ private boolean boundingBoxChanged = false; // DivineMC - vmp: skip entity move if movement is zero + public boolean onGround; + public boolean horizontalCollision; + public boolean verticalCollision; +@@ -1120,6 +_,12 @@ + // Paper end - detailed watchdog information + + public void move(MoverType type, Vec3 movement) { ++ // DivineMC start - vmp: skip entity move if movement is zero ++ if (!boundingBoxChanged && movement.equals(Vec3.ZERO)) { ++ boundingBoxChanged = false; ++ return; ++ } ++ // DivineMC end - vmp: skip entity move if movement is zero + final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity + // Paper start - detailed watchdog information + ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main"); +@@ -4251,6 +_,7 @@ + } + + public final void setBoundingBox(AABB bb) { ++ if (!this.bb.equals(bb)) boundingBoxChanged = true; // DivineMC - vmp: skip entity move if movement is zero + // CraftBukkit start - block invalid bounding boxes + double minX = bb.minX, + minY = bb.minY, diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch new file mode 100644 index 0000000..545cd23 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/LivingEntity.java.patch @@ -0,0 +1,42 @@ +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -1385,7 +_,7 @@ + player.setRealHealth(health); + } + +- player.updateScaledHealth(false); ++ this.entityData.set(LivingEntity.DATA_HEALTH_ID, player.getScaledHealth()); // DivineMC - Fix sprint glitch + return; + } + // CraftBukkit end +@@ -2666,6 +_,7 @@ + } + + protected void updateSwingTime() { ++ if (!this.swinging && this.swingTime == 0) return; // DivineMC - lithium: entity.fast_hand_swing + int currentSwingDuration = this.getCurrentSwingDuration(); + if (this.swinging) { + this.swingTime++; +@@ -3150,7 +_,13 @@ + } + + protected float getFlyingSpeed() { +- return this.getControllingPassenger() instanceof net.minecraft.world.entity.player.Player ? this.getSpeed() * 0.1F : 0.02F; ++ // DivineMC start - Fix MC-172801 ++ float flyingSpeed = 0.02F; ++ if (this.getAttributes().hasAttribute(Attributes.FLYING_SPEED)) { ++ flyingSpeed = (float) (this.getAttribute(Attributes.FLYING_SPEED).getValue() * 0.049999999254942D); ++ } ++ return this.getControllingPassenger() instanceof net.minecraft.world.entity.player.Player ? this.getSpeed() * 0.1F : flyingSpeed; ++ // DivineMC end - Fix MC-172801 + } + + public float getSpeed() { +@@ -3617,6 +_,7 @@ + protected void updateFallFlying() { + this.checkSlowFallDistance(); + if (!this.level().isClientSide) { ++ if (!this.isFallFlying() && this.fallFlyTicks == 0) return; // DivineMC - lithium: entity.fast_elytra_check + if (!this.canGlide()) { + if (this.getSharedFlag(7) != false && !CraftEventFactory.callToggleGlideEvent(this, false).isCancelled()) // CraftBukkit + this.setSharedFlag(7, false); diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java.patch new file mode 100644 index 0000000..494bacd --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java ++++ b/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java +@@ -114,6 +_,7 @@ + } + + protected void alertOther(Mob mob, LivingEntity target) { ++ if (mob == target) return; // DivineMC - Fix MC-110386 + mob.setTarget(target, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY, true); // CraftBukkit - reason + } + } diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Animal.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Animal.java.patch new file mode 100644 index 0000000..bb1eb0d --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/animal/Animal.java.patch @@ -0,0 +1,23 @@ +--- a/net/minecraft/world/entity/animal/Animal.java ++++ b/net/minecraft/world/entity/animal/Animal.java +@@ -41,6 +_,7 @@ + public UUID loveCause; + public ItemStack breedItem; // CraftBukkit - Add breedItem variable + public abstract int getPurpurBreedTime(); // Purpur - Make entity breeding times configurable ++ private Object level; // DivineMC - Add level variable + + protected Animal(EntityType entityType, Level level) { + super(entityType, level); +@@ -74,7 +_,11 @@ + double d = this.random.nextGaussian() * 0.02; + double d1 = this.random.nextGaussian() * 0.02; + double d2 = this.random.nextGaussian() * 0.02; +- this.level().addParticle(ParticleTypes.HEART, this.getRandomX(1.0), this.getRandomY() + 0.5, this.getRandomZ(1.0), d, d1, d2); ++ // DivineMC start - Fix MC-93826 ++ if (this.level instanceof ServerLevel serverLevel) { ++ serverLevel.sendParticles(ParticleTypes.HEART, this.getRandomX(1.0D), this.getRandomY() + 0.5D, this.getRandomZ(1.0D), 1, d, d1, d2, 0); ++ } ++ // DivineMC end - Fix MC-93826 + } + } + } diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/monster/ZombieVillager.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/monster/ZombieVillager.java.patch new file mode 100644 index 0000000..2837559 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/monster/ZombieVillager.java.patch @@ -0,0 +1,15 @@ +--- a/net/minecraft/world/entity/monster/ZombieVillager.java ++++ b/net/minecraft/world/entity/monster/ZombieVillager.java +@@ -320,6 +_,12 @@ + if (!this.isSilent()) { + serverLevel.levelEvent(null, 1027, this.blockPosition(), 0); + } ++ ++ // DivineMC start - Fix MC-200418 ++ if (villager.isPassenger() && villager.getVehicle() instanceof net.minecraft.world.entity.animal.Chicken && villager.isBaby()) { ++ villager.removeVehicle(); ++ } ++ // DivineMC end - Fix MC-200418 + // CraftBukkit start + }, org.bukkit.event.entity.EntityTransformEvent.TransformReason.CURED, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CURED // CraftBukkit + ); diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch new file mode 100644 index 0000000..e159043 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/player/Player.java.patch @@ -0,0 +1,14 @@ +--- a/net/minecraft/world/entity/player/Player.java ++++ b/net/minecraft/world/entity/player/Player.java +@@ -1877,6 +_,11 @@ + } + + public void causeFoodExhaustion(float exhaustion, org.bukkit.event.entity.EntityExhaustionEvent.ExhaustionReason reason) { ++ // DivineMC start - Fix MC-31819 ++ if (this.level().getDifficulty() == Difficulty.PEACEFUL) { ++ return; ++ } ++ // DivineMC end - Fix MC-31819 + // CraftBukkit end + if (!this.abilities.invulnerable) { + if (!this.level().isClientSide) { diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch new file mode 100644 index 0000000..beef79f --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/entity/projectile/AbstractArrow.java ++++ b/net/minecraft/world/entity/projectile/AbstractArrow.java +@@ -344,7 +_,7 @@ + return this.isInGround() && this.level().noCollision(new AABB(this.position(), this.position()).inflate(0.06)); + } + +- private void startFalling() { ++ public void startFalling() { // DivineMC - private -> public - AbstractArrow#startFalling + this.setInGround(false); + Vec3 deltaMovement = this.getDeltaMovement(); + this.setDeltaMovement(deltaMovement.multiply(this.random.nextFloat() * 0.2F, this.random.nextFloat() * 0.2F, this.random.nextFloat() * 0.2F)); diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch new file mode 100644 index 0000000..707c8b7 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java ++++ b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java +@@ -354,6 +_,13 @@ + return false; + } + ++ // DivineMC start - Don't save Fireworks ++ @Override ++ public boolean shouldBeSaved() { ++ return this.level().divinemcConfig.saveFireworks; ++ } ++ // DivineMC end - Don't save Fireworks ++ + public static ItemStack getDefaultItem() { + return new ItemStack(Items.FIREWORK_ROCKET); + } diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/ShulkerBullet.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/ShulkerBullet.java.patch new file mode 100644 index 0000000..dfe8585 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/projectile/ShulkerBullet.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/entity/projectile/ShulkerBullet.java ++++ b/net/minecraft/world/entity/projectile/ShulkerBullet.java +@@ -215,6 +_,17 @@ + super.tick(); + HitResult hitResult = null; + if (!this.level().isClientSide) { ++ // DivineMC start - despawn shulker bullets on owner death ++ if (this.level().divinemcConfig.despawnShulkerBulletsOnOwnerDeath) { ++ if (!isInvulnerable()) { ++ var owner = getOwner(); ++ if (owner == null || !owner.isAlive()) { ++ discard(); ++ return; ++ } ++ } ++ } ++ // DivineMC end - despawn shulker bullets on owner death + if (this.finalTarget == null && this.targetId != null) { + this.finalTarget = ((ServerLevel)this.level()).getEntity(this.targetId); + if (this.finalTarget == null) { diff --git a/patches/server/0014-lithium-ai.raid.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/raid/Raid.java.patch similarity index 51% rename from patches/server/0014-lithium-ai.raid.patch rename to divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/raid/Raid.java.patch index 7a17ebb..93d2ae9 100644 --- a/patches/server/0014-lithium-ai.raid.patch +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/raid/Raid.java.patch @@ -1,27 +1,14 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 8 Apr 2023 23:45:11 +0300 -Subject: [PATCH] lithium: ai.raid - -This patch is based on the following mixin: -"me/jellysquid/mods/lithium/mixin/ai/raid/RaidMixin.java" -By: Angeline -As part of: Lithium (https://github.com/CaffeineMC/lithium-fabric) -Licensed under: LGPL-3.0 (https://www.gnu.org/licenses/lgpl-3.0.html) - -diff --git a/src/main/java/net/minecraft/world/entity/raid/Raid.java b/src/main/java/net/minecraft/world/entity/raid/Raid.java -index 11cf2d9def087b0898c828eaa21eb5f7b8811d5f..c834c29683b586cc347006f8f1298df366d8ca1b 100644 ---- a/src/main/java/net/minecraft/world/entity/raid/Raid.java -+++ b/src/main/java/net/minecraft/world/entity/raid/Raid.java -@@ -107,6 +107,7 @@ public class Raid { +--- a/net/minecraft/world/entity/raid/Raid.java ++++ b/net/minecraft/world/entity/raid/Raid.java +@@ -104,6 +_,7 @@ private Raid.RaidStatus status; private int celebrationTicks; - private Optional waveSpawnPos; + private Optional waveSpawnPos = Optional.empty(); + private boolean isBarDirty; // DivineMC - lithium: ai.raid // Paper start private static final String PDC_NBT_KEY = "BukkitValues"; private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry PDC_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); -@@ -292,6 +293,12 @@ public class Raid { +@@ -264,6 +_,12 @@ public void tick() { if (!this.isStopped()) { @@ -30,12 +17,12 @@ index 11cf2d9def087b0898c828eaa21eb5f7b8811d5f..c834c29683b586cc347006f8f1298df3 + this.updateBossbarInternal(); + this.isBarDirty = false; + } -+ // DivineMC end ++ // DivineMC end - lithium: ai.raid if (this.status == Raid.RaidStatus.ONGOING) { boolean flag = this.active; - -@@ -651,9 +658,15 @@ public class Raid { - + this.active = this.level.hasChunkAt(this.center); +@@ -587,9 +_,15 @@ + } } + // DivineMC start - lithium: ai.raid @@ -46,7 +33,7 @@ index 11cf2d9def087b0898c828eaa21eb5f7b8811d5f..c834c29683b586cc347006f8f1298df3 + private void updateBossbarInternal() { this.raidEvent.setProgress(Mth.clamp(this.getHealthOfLivingRaiders() / this.totalHealth, 0.0F, 1.0F)); } -+ // DivineMC end ++ // DivineMC end - lithium: ai.raid public float getHealthOfLivingRaiders() { float f = 0.0F; diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/MinecartHopper.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/MinecartHopper.java.patch new file mode 100644 index 0000000..d158030 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/entity/vehicle/MinecartHopper.java.patch @@ -0,0 +1,16 @@ +--- a/net/minecraft/world/entity/vehicle/MinecartHopper.java ++++ b/net/minecraft/world/entity/vehicle/MinecartHopper.java +@@ -99,6 +_,13 @@ + } + } + ++ // DivineMC start - tick minecart hopper without players ++ @Override ++ public void inactiveTick() { ++ this.tick(); ++ } ++ // DivineMC end - tick minecart hopper without players ++ + public boolean suckInItems() { + if (HopperBlockEntity.suckInItems(this.level(), this)) { + this.immunize(); // Paper diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/GameRules.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/GameRules.java.patch new file mode 100644 index 0000000..56a0ade --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/GameRules.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/GameRules.java ++++ b/net/minecraft/world/level/GameRules.java +@@ -249,7 +_,7 @@ + } + + private GameRules(Map, GameRules.Value> rules, FeatureFlagSet enabledFeatures) { +- this.rules = rules; ++ this.rules = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(rules); // DivineMC - lithium: collections.gamerules + this.enabledFeatures = enabledFeatures; + + // Paper start - Perf: Use array for gamerule storage diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/Level.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/Level.java.patch new file mode 100644 index 0000000..3d38bc6 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/Level.java.patch @@ -0,0 +1,32 @@ +--- a/net/minecraft/world/level/Level.java ++++ b/net/minecraft/world/level/Level.java +@@ -115,9 +_,9 @@ + public static final int TICKS_PER_DAY = 24000; + public static final int MAX_ENTITY_SPAWN_Y = 20000000; + public static final int MIN_ENTITY_SPAWN_Y = -20000000; +- public final List blockEntityTickers = Lists.newArrayList(); // Paper - public ++ public final List blockEntityTickers = new space.bxteam.divinemc.util.lithium.HashedReferenceList<>(Lists.newArrayList()); // Paper - public // DivineMC - lithium: hashed_list + protected final NeighborUpdater neighborUpdater; +- private final List pendingBlockEntityTickers = Lists.newArrayList(); ++ private final List pendingBlockEntityTickers = new space.bxteam.divinemc.util.lithium.HashedReferenceList<>(Lists.newArrayList()); // DivineMC - lithium: hashed_list + private boolean tickingBlockEntities; + public final Thread thread; + private final boolean isDebug; +@@ -172,8 +_,6 @@ + public final io.papermc.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray + public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur - Purpur config files + public static BlockPos lastPhysicsProblem; // Spigot +- private org.spigotmc.TickLimiter entityLimiter; +- private org.spigotmc.TickLimiter tileLimiter; + private int tileTickPosition; + public final Map explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions + public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Faster redstone torch rapid clock removal; Move from Map in BlockRedstoneTorch to here +@@ -974,8 +_,6 @@ + public void onBorderSetDamageSafeZOne(WorldBorder border, double safeZoneRadius) {} + }); + // CraftBukkit end +- this.entityLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.entityMaxTickTime); +- this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime); + this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new io.papermc.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : io.papermc.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray + this.entityLookup = new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEntityLookup(this); // Paper - rewrite chunk system + } diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/LocalMobCapCalculator.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/LocalMobCapCalculator.java.patch new file mode 100644 index 0000000..ddedc73 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/LocalMobCapCalculator.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/level/LocalMobCapCalculator.java ++++ b/net/minecraft/world/level/LocalMobCapCalculator.java +@@ -42,14 +_,14 @@ + } + + static class MobCounts { +- private final Object2IntMap counts = new Object2IntOpenHashMap<>(MobCategory.values().length); ++ private final int[] spawnGroupDensities = new int[MobCategory.values().length]; // DivineMC - vmp: spawn_density_cap + + public void add(MobCategory category) { +- this.counts.computeInt(category, (key, value) -> value == null ? 1 : value + 1); ++ this.spawnGroupDensities[category.ordinal()]++; // DivineMC - vmp: spawn_density_cap + } + + public boolean canSpawn(MobCategory category) { +- return this.counts.getOrDefault(category, 0) < category.getMaxInstancesPerChunk(); ++ return this.spawnGroupDensities[category.ordinal()] < category.getMaxInstancesPerChunk(); // DivineMC - vmp: spawn_density_cap + } + } + } diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/biome/BiomeManager.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/biome/BiomeManager.java.patch new file mode 100644 index 0000000..bc0c686 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/biome/BiomeManager.java.patch @@ -0,0 +1,108 @@ +--- a/net/minecraft/world/level/biome/BiomeManager.java ++++ b/net/minecraft/world/level/biome/BiomeManager.java +@@ -14,6 +_,7 @@ + private static final int ZOOM_MASK = 3; + private final BiomeManager.NoiseBiomeSource noiseBiomeSource; + private final long biomeZoomSeed; ++ private static final double maxOffset = 0.4500000001D; // DivineMC - Optimize BiomeManager#getBiome + + public BiomeManager(BiomeManager.NoiseBiomeSource noiseBiomeSource, long biomeZoomSeed) { + this.noiseBiomeSource = noiseBiomeSource; +@@ -29,39 +_,66 @@ + } + + public Holder getBiome(BlockPos pos) { +- int i = pos.getX() - 2; +- int i1 = pos.getY() - 2; +- int i2 = pos.getZ() - 2; +- int i3 = i >> 2; +- int i4 = i1 >> 2; +- int i5 = i2 >> 2; +- double d = (i & 3) / 4.0; +- double d1 = (i1 & 3) / 4.0; +- double d2 = (i2 & 3) / 4.0; +- int i6 = 0; +- double d3 = Double.POSITIVE_INFINITY; +- +- for (int i7 = 0; i7 < 8; i7++) { +- boolean flag = (i7 & 4) == 0; +- boolean flag1 = (i7 & 2) == 0; +- boolean flag2 = (i7 & 1) == 0; +- int i8 = flag ? i3 : i3 + 1; +- int i9 = flag1 ? i4 : i4 + 1; +- int i10 = flag2 ? i5 : i5 + 1; +- double d4 = flag ? d : d - 1.0; +- double d5 = flag1 ? d1 : d1 - 1.0; +- double d6 = flag2 ? d2 : d2 - 1.0; +- double fiddledDistance = getFiddledDistance(this.biomeZoomSeed, i8, i9, i10, d4, d5, d6); +- if (d3 > fiddledDistance) { +- i6 = i7; +- d3 = fiddledDistance; ++ // DivineMC start - Optimize BiomeManager#getBiome ++ int xMinus2 = pos.getX() - 2; ++ int yMinus2 = pos.getY() - 2; ++ int zMinus2 = pos.getZ() - 2; ++ int x = xMinus2 >> 2; ++ int y = yMinus2 >> 2; ++ int z = zMinus2 >> 2; ++ double quartX = (double) (xMinus2 & 3) / 4.0; // quartLocal divided by 4 ++ double quartY = (double) (yMinus2 & 3) / 4.0; // 0/4, 1/4, 2/4, 3/4 ++ double quartZ = (double) (zMinus2 & 3) / 4.0; // [0, 0.25, 0.5, 0.75] ++ int smallestX = 0; ++ double smallestDist = Double.POSITIVE_INFINITY; ++ for (int biomeX = 0; biomeX < 8; ++biomeX) { ++ boolean everyOtherQuad = (biomeX & 4) == 0; // 1 1 1 1 0 0 0 0 ++ boolean everyOtherPair = (biomeX & 2) == 0; // 1 1 0 0 1 1 0 0 ++ boolean everyOther = (biomeX & 1) == 0; // 1 0 1 0 1 0 1 0 ++ double quartXX = everyOtherQuad ? quartX : quartX - 1.0; //[-1.0,-0.75,-0.5,-0.25,0.0,0.25,0.5,0.75] ++ double quartYY = everyOtherPair ? quartY : quartY - 1.0; ++ double quartZZ = everyOther ? quartZ : quartZ - 1.0; ++ ++ double maxQuartYY = 0.0, maxQuartZZ = 0.0; ++ if (biomeX != 0) { ++ maxQuartYY = Mth.square(Math.max(quartYY + maxOffset, Math.abs(quartYY - maxOffset))); ++ maxQuartZZ = Mth.square(Math.max(quartZZ + maxOffset, Math.abs(quartZZ - maxOffset))); ++ double maxQuartXX = Mth.square(Math.max(quartXX + maxOffset, Math.abs(quartXX - maxOffset))); ++ if (smallestDist < maxQuartXX + maxQuartYY + maxQuartZZ) continue; ++ } ++ ++ int xx = everyOtherQuad ? x : x + 1; ++ int yy = everyOtherPair ? y : y + 1; ++ int zz = everyOther ? z : z + 1; ++ ++ long seed = LinearCongruentialGenerator.next(this.biomeZoomSeed, xx); ++ seed = LinearCongruentialGenerator.next(seed, yy); ++ seed = LinearCongruentialGenerator.next(seed, zz); ++ seed = LinearCongruentialGenerator.next(seed, xx); ++ seed = LinearCongruentialGenerator.next(seed, yy); ++ seed = LinearCongruentialGenerator.next(seed, zz); ++ double offsetX = getFiddle(seed); ++ double sqrX = Mth.square(quartXX + offsetX); ++ if (biomeX != 0 && smallestDist < sqrX + maxQuartYY + maxQuartZZ) continue; ++ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); ++ double offsetY = getFiddle(seed); ++ double sqrY = Mth.square(quartYY + offsetY); ++ if (biomeX != 0 && smallestDist < sqrX + sqrY + maxQuartZZ) continue; ++ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); ++ double offsetZ = getFiddle(seed); ++ double biomeDist = sqrX + sqrY + Mth.square(quartZZ + offsetZ); ++ ++ if (smallestDist > biomeDist) { ++ smallestX = biomeX; ++ smallestDist = biomeDist; + } + } +- +- int i7x = (i6 & 4) == 0 ? i3 : i3 + 1; +- int i11 = (i6 & 2) == 0 ? i4 : i4 + 1; +- int i12 = (i6 & 1) == 0 ? i5 : i5 + 1; +- return this.noiseBiomeSource.getNoiseBiome(i7x, i11, i12); ++ return this.noiseBiomeSource.getNoiseBiome( ++ (smallestX & 4) == 0 ? x : x + 1, ++ (smallestX & 2) == 0 ? y : y + 1, ++ (smallestX & 1) == 0 ? z : z + 1 ++ ); ++ // DivineMC end - Optimize BiomeManager#getBiome + } + + public Holder getNoiseBiomeAtPosition(double x, double y, double z) { diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/Blocks.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/Blocks.java.patch new file mode 100644 index 0000000..4cd6154 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/Blocks.java.patch @@ -0,0 +1,10 @@ +--- a/net/minecraft/world/level/block/Blocks.java ++++ b/net/minecraft/world/level/block/Blocks.java +@@ -6632,6 +_,7 @@ + .mapColor(MapColor.COLOR_ORANGE) + .instrument(NoteBlockInstrument.BASEDRUM) + .requiresCorrectToolForDrops() ++ .sound(SoundType.COPPER) // DivineMC - Fix MC-223153 + .strength(5.0F, 6.0F) + ); + public static final Block RAW_GOLD_BLOCK = register( diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch new file mode 100644 index 0000000..dc0218d --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/EnderChestBlock.java.patch @@ -0,0 +1,20 @@ +--- a/net/minecraft/world/level/block/EnderChestBlock.java ++++ b/net/minecraft/world/level/block/EnderChestBlock.java +@@ -43,7 +_,7 @@ + public static final EnumProperty FACING = HorizontalDirectionalBlock.FACING; + public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; + protected static final VoxelShape SHAPE = Block.box(1.0, 0.0, 1.0, 15.0, 14.0, 15.0); +- private static final Component CONTAINER_TITLE = Component.translatable("container.enderchest"); ++ public static final Component CONTAINER_TITLE = Component.translatable("container.enderchest"); // DivineMC - private -> public - Open Ender Chest API + + @Override + public MapCodec codec() { +@@ -101,7 +_,7 @@ + } + + // Purpur start - Barrels and enderchests 6 rows +- private ChestMenu getEnderChestSixRows(int syncId, net.minecraft.world.entity.player.Inventory inventory, Player player, PlayerEnderChestContainer playerEnderChestContainer) { ++ public static ChestMenu getEnderChestSixRows(int syncId, net.minecraft.world.entity.player.Inventory inventory, Player player, PlayerEnderChestContainer playerEnderChestContainer) { // DivineMC - private -> public static - Open Ender Chest API + if (org.purpurmc.purpur.PurpurConfig.enderChestPermissionRows) { + org.bukkit.craftbukkit.entity.CraftHumanEntity bukkitPlayer = player.getBukkitEntity(); + if (bukkitPlayer.hasPermission("purpur.enderchest.rows.six")) { diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch new file mode 100644 index 0000000..de4c77a --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/NoteBlock.java.patch @@ -0,0 +1,28 @@ +--- a/net/minecraft/world/level/block/NoteBlock.java ++++ b/net/minecraft/world/level/block/NoteBlock.java +@@ -40,6 +_,7 @@ + public static final BooleanProperty POWERED = BlockStateProperties.POWERED; + public static final IntegerProperty NOTE = BlockStateProperties.NOTE; + public static final int NOTE_VOLUME = 3; ++ private @Nullable Player lastPlayedBy = null; // DivineMC - Add player to NotePlayEvent + + @Override + public MapCodec codec() { +@@ -108,6 +_,7 @@ + + private void playNote(@Nullable Entity entity, BlockState state, Level level, BlockPos pos) { + if (level.purpurConfig.noteBlockIgnoreAbove || state.getValue(INSTRUMENT).worksAboveNoteBlock() || level.getBlockState(pos.above()).isAir()) { // Purpur - Config to allow Note Block sounds when blocked ++ if (entity instanceof Player player) this.lastPlayedBy = player; // DivineMC - Add player to NotePlayEvent + level.blockEvent(pos, this, 0, 0); + level.gameEvent(entity, GameEvent.NOTE_BLOCK_PLAY, pos); + } +@@ -150,7 +_,8 @@ + protected boolean triggerEvent(BlockState state, Level level, BlockPos pos, int id, int param) { + NoteBlockInstrument noteBlockInstrument = state.getValue(INSTRUMENT); + // Paper start - move NotePlayEvent call to fix instrument/note changes +- org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(level, pos, noteBlockInstrument, state.getValue(NOTE)); ++ org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(level, pos, noteBlockInstrument, state.getValue(NOTE), this.lastPlayedBy); // DivineMC - Add player to NotePlayEvent ++ this.lastPlayedBy = null; // DivineMC - Add player to NotePlayEvent + if (event.isCancelled()) return false; + // Paper end - move NotePlayEvent call to fix instrument/note changes + float pitchFromNote; diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch new file mode 100644 index 0000000..1c48a1e --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/block/entity/SignBlockEntity.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/block/entity/SignBlockEntity.java ++++ b/net/minecraft/world/level/block/entity/SignBlockEntity.java +@@ -152,7 +_,7 @@ + this.setAllowedPlayerEditor(null); + this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3); + } else { +- LOGGER.warn("Player {} just tried to change non-editable sign", player.getName().getString()); ++ if (!space.bxteam.divinemc.configuration.DivineConfig.disableNonEditableSignWarning) LOGGER.warn("Player {} just tried to change non-editable sign", player.getName().getString()); // DivineMC - Option to disable warning + if (player.distanceToSqr(this.getBlockPos().getX(), this.getBlockPos().getY(), this.getBlockPos().getZ()) < Mth.square(32)) // Paper - Don't send far away sign update + ((net.minecraft.server.level.ServerPlayer) player).connection.send(this.getUpdatePacket()); // CraftBukkit + } diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/dimension/end/EndDragonFight.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/dimension/end/EndDragonFight.java.patch new file mode 100644 index 0000000..899cf3a --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/dimension/end/EndDragonFight.java.patch @@ -0,0 +1,82 @@ +--- a/net/minecraft/world/level/dimension/end/EndDragonFight.java ++++ b/net/minecraft/world/level/dimension/end/EndDragonFight.java +@@ -274,8 +_,67 @@ + return false; + } + ++ // DivineMC start - Optimized dragon respawn ++ private int cachePortalChunkIteratorX = -8; ++ private int cachePortalChunkIteratorZ = -8; ++ private int cachePortalOriginIteratorY = -1; ++ + @Nullable + public BlockPattern.BlockPatternMatch findExitPortal() { ++ if (space.bxteam.divinemc.configuration.DivineConfig.optimizedDragonRespawn) { ++ int i, j; ++ for (i = cachePortalChunkIteratorX; i <= 8; ++i) { ++ for (j = cachePortalChunkIteratorZ; j <= 8; ++j) { ++ LevelChunk worldChunk = this.level.getChunk(i, j); ++ for (BlockEntity blockEntity : worldChunk.getBlockEntities().values()) { ++ if (blockEntity instanceof net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity) { ++ continue; ++ } ++ if (blockEntity instanceof TheEndPortalBlockEntity) { ++ BlockPattern.BlockPatternMatch blockPatternMatch = this.exitPortalPattern.find(this.level, blockEntity.getBlockPos()); ++ if (blockPatternMatch != null) { ++ BlockPos blockPos = blockPatternMatch.getBlock(3, 3, 3).getPos(); ++ if (this.portalLocation == null) { ++ this.portalLocation = blockPos; ++ } ++ //No need to judge whether optimizing option is open ++ cachePortalChunkIteratorX = i; ++ cachePortalChunkIteratorZ = j; ++ return blockPatternMatch; ++ } ++ } ++ } ++ } ++ } ++ ++ if (this.needsStateScanning || this.portalLocation == null) { ++ if (cachePortalOriginIteratorY != -1) { ++ i = cachePortalOriginIteratorY; ++ } else { ++ i = this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(BlockPos.ZERO)).getY(); ++ } ++ boolean notFirstSearch = false; ++ for (j = i; j >= 0; --j) { ++ BlockPattern.BlockPatternMatch result2 = null; ++ if (notFirstSearch) { ++ result2 = space.bxteam.divinemc.util.carpetams.BlockPatternHelper.partialSearchAround(this.exitPortalPattern, this.level, new BlockPos(EndPodiumFeature.getLocation(BlockPos.ZERO).getY(), j, EndPodiumFeature.getLocation(BlockPos.ZERO).getZ())); ++ } else { ++ result2 = this.exitPortalPattern.find(this.level, new BlockPos(EndPodiumFeature.getLocation(BlockPos.ZERO).getX(), j, EndPodiumFeature.getLocation(BlockPos.ZERO).getZ())); ++ } ++ if (result2 != null) { ++ if (this.portalLocation == null) { ++ this.portalLocation = result2.getBlock(3, 3, 3).getPos(); ++ } ++ cachePortalOriginIteratorY = j; ++ return result2; ++ } ++ notFirstSearch = true; ++ } ++ } ++ ++ return null; ++ } ++ // DivineMC end - Optimized dragon respawn + ChunkPos chunkPos = new ChunkPos(this.origin); + + for (int i = -8 + chunkPos.x; i <= 8 + chunkPos.x; i++) { +@@ -571,6 +_,11 @@ + } + + public boolean respawnDragon(List crystals) { // CraftBukkit - return boolean ++ // DivineMC start - Optimized dragon respawn ++ cachePortalChunkIteratorX = -8; ++ cachePortalChunkIteratorZ = -8; ++ cachePortalOriginIteratorY = -1; ++ // DivineMC end - Optimized dragon respawn + if (this.dragonKilled && this.respawnStage == null) { + for (BlockPattern.BlockPatternMatch blockPatternMatch = this.findExitPortal(); blockPatternMatch != null; blockPatternMatch = this.findExitPortal()) { + for (int i = 0; i < this.exitPortalPattern.getWidth(); i++) { diff --git a/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/levelgen/feature/OreFeature.java.patch b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/levelgen/feature/OreFeature.java.patch new file mode 100644 index 0000000..40c0da1 --- /dev/null +++ b/divinemc-server/minecraft-patches/sources/net/minecraft/world/level/levelgen/feature/OreFeature.java.patch @@ -0,0 +1,11 @@ +--- a/net/minecraft/world/level/levelgen/feature/OreFeature.java ++++ b/net/minecraft/world/level/levelgen/feature/OreFeature.java +@@ -69,7 +_,7 @@ + int height + ) { + int i = 0; +- BitSet bitSet = new BitSet(width * height * width); ++ BitSet bitSet = space.bxteam.divinemc.util.c2me.ObjectCachingUtils.getCachedOrNewBitSet(width * height * width); // DivineMC - C2ME: reduce_allocs + BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); + int i1 = config.size; + double[] doubles = new double[i1 * 4]; diff --git a/patches/server/0001-Divine-Branding.patch b/divinemc-server/paper-patches/features/0001-Rebrand.patch similarity index 70% rename from patches/server/0001-Divine-Branding.patch rename to divinemc-server/paper-patches/features/0001-Rebrand.patch index e7a4606..dd2e214 100644 --- a/patches/server/0001-Divine-Branding.patch +++ b/divinemc-server/paper-patches/features/0001-Rebrand.patch @@ -1,68 +1,14 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Fri, 16 Jun 2023 01:08:36 +0300 -Subject: [PATCH] Divine Branding +Date: Sat, 11 Jan 2025 22:11:40 +0300 +Subject: [PATCH] Rebrand -diff --git a/build.gradle.kts b/build.gradle.kts -index 624588207afdc52adae14e5de3d18fe1c330d832..1dc3378c0cdda49ca0cee17cd2959fcb32c4cfcc 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -25,7 +25,7 @@ abstract class MockitoAgentProvider : CommandLineArgumentProvider { - // Paper end - configure mockito agent that is needed in newer java versions - - dependencies { -- implementation(project(":purpur-api")) // Pufferfish // Paper // Purpur - Rebrand -+ implementation(project(":divinemc-api")) // DivineMC - implementation("ca.spottedleaf:concurrentutil:0.0.2") // Paper - Add ConcurrentUtil dependency - // Paper start - implementation("org.jline:jline-terminal-ffm:3.27.1") // use ffm on java 22+ -@@ -119,14 +119,14 @@ tasks.jar { - val gitBranch = git("rev-parse", "--abbrev-ref", "HEAD").getText().trim() // Paper - attributes( - "Main-Class" to "org.bukkit.craftbukkit.Main", -- "Implementation-Title" to "Purpur", // Pufferfish // Purpur - Rebrand -+ "Implementation-Title" to "DivineMC", // DivineMC - "Implementation-Version" to implementationVersion, - "Implementation-Vendor" to date, // Paper -- "Specification-Title" to "Purpur", // Pufferfish // Purpur - Rebrand -+ "Specification-Title" to "DivineMC", // DivineMC - "Specification-Version" to project.version, -- "Specification-Vendor" to "Purpur Team", // Pufferfish // Purpur - Rebrand -- "Brand-Id" to "purpurmc:purpur", // Pufferfish // Purpur - Rebrand -- "Brand-Name" to "Purpur", // Pufferfish // Purpur - Rebrand -+ "Specification-Vendor" to "BX Team", // DivineMC + BX Team -+ "Brand-Id" to "divinemc:divinemc", // DivineMC -+ "Brand-Name" to "DivineMC", // DivineMC - "Build-Number" to (build ?: ""), - "Build-Time" to Instant.now().toString(), - "Git-Branch" to gitBranch, // Paper -@@ -271,3 +271,12 @@ tasks.registerRunTask("runReobfPaperclip") { - classpath(rootProject.tasks.named("createReobfPaperclipJar").flatMap { it.outputZip }) - mainClass.set(null as String?) - } -+ -+// DivineMC start - Hide irrelevant compilation warnings -+tasks.withType { -+ val compilerArgs = options.compilerArgs -+ compilerArgs.add("-Xlint:-module") -+ compilerArgs.add("-Xlint:-removal") -+ compilerArgs.add("-Xlint:-dep-ann") -+} -+// DivineMC end diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java -index be1bb14dca9367b9685841985b6198376986c496..50f93c65f3461ae66963f603f76989f23e585fc0 100644 +index be1bb14dca9367b9685841985b6198376986c496..58f16cdf593dbf874652a46799ecfa5c418c0486 100644 --- a/src/main/java/com/destroystokyo/paper/Metrics.java +++ b/src/main/java/com/destroystokyo/paper/Metrics.java -@@ -3,6 +3,7 @@ package com.destroystokyo.paper; - import net.minecraft.server.MinecraftServer; - import org.bukkit.Bukkit; - import org.bukkit.configuration.file.YamlConfiguration; -+import org.bukkit.craftbukkit.CraftServer; - import org.bukkit.craftbukkit.util.CraftMagicNumbers; - import org.bukkit.plugin.Plugin; - -@@ -592,7 +593,7 @@ public class Metrics { +@@ -592,7 +592,7 @@ public class Metrics { boolean logFailedRequests = config.getBoolean("logFailedRequests", false); // Only start Metrics, if it's enabled in the config if (config.getBoolean("enabled", true)) { @@ -71,29 +17,25 @@ index be1bb14dca9367b9685841985b6198376986c496..50f93c65f3461ae66963f603f76989f2 metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { String minecraftVersion = Bukkit.getVersion(); -@@ -601,8 +602,8 @@ public class Metrics { - })); +@@ -602,7 +602,7 @@ public class Metrics { metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size())); -- metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur - Purpur config files + metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur - Purpur config files - metrics.addCustomChart(new Metrics.SimplePie("purpur_version", () -> (org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() != null) ? org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() : "unknown")); // Purpur - Purpur config files -+ metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur -+ metrics.addCustomChart(new Metrics.SimplePie("divinemc_version", () -> (CraftServer.class.getPackage().getImplementationVersion() != null) ? CraftServer.class.getPackage().getImplementationVersion() : "unknown")); // DivineMC ++ metrics.addCustomChart(new Metrics.SimplePie("divinemc_version", () -> (org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() != null) ? org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion() : "unknown")); // DivineMC metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> { Map> map = new HashMap<>(); diff --git a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -index fe66e43c27e0798770e102d1385bacbaa90bda07..2ce62b1465f6653937082c17721a953b0d812750 100644 +index fe66e43c27e0798770e102d1385bacbaa90bda07..6e11b6d871a1122ac324ad19aa45a3dbecdc52e8 100644 --- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java +++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java -@@ -35,14 +35,14 @@ public class PaperVersionFetcher implements VersionFetcher { - private static final Logger LOGGER = LogUtils.getClassLogger(); +@@ -36,13 +36,13 @@ public class PaperVersionFetcher implements VersionFetcher { private static final int DISTANCE_ERROR = -1; private static final int DISTANCE_UNKNOWN = -2; -- // Purpur start - Rebrand + // Purpur start - Rebrand - private static final String DOWNLOAD_PAGE = "https://purpurmc.org/downloads"; -+ // Purpur start -+ private static final String DOWNLOAD_PAGE = "https://github.com/DivineMC/DivineMC/releases/latest"; // DivineMC - Branding ++ private static final String DOWNLOAD_PAGE = "https://github.com/DivineMC/DivineMC/releases/latest"; // DivineMC - Rebrand private static int distance = DISTANCE_UNKNOWN; public int distance() { return distance; } // Purpur end - Rebrand @@ -134,7 +76,7 @@ index fe66e43c27e0798770e102d1385bacbaa90bda07..2ce62b1465f6653937082c17721a953b + if (gitBranch.isPresent() && gitCommit.isPresent()) { + distance = fetchDistanceFromGitHub(repo, gitBranch.get(), gitCommit.get()); } -+ // DivineMC end ++ // DivineMC end - Branding return switch (distance) { case DISTANCE_ERROR -> text("* Error obtaining version information", NamedTextColor.RED); // Purpur - Rebrand @@ -166,7 +108,7 @@ index fe66e43c27e0798770e102d1385bacbaa90bda07..2ce62b1465f6653937082c17721a953b private static int fetchDistanceFromGitHub(final String repo, final String branch, final String hash) { try { diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java -index bc7e4e5560708fea89c584b1d8b471f4966f311a..fe195f103456d5826e06b88bf2c97877c06dcf26 100644 +index bc7e4e5560708fea89c584b1d8b471f4966f311a..6567ff18cb1c21230565c2d92caf3a7f7f915c17 100644 --- a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java +++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java @@ -20,7 +20,7 @@ public final class PaperConsole extends SimpleTerminalConsole { @@ -174,23 +116,23 @@ index bc7e4e5560708fea89c584b1d8b471f4966f311a..fe195f103456d5826e06b88bf2c97877 protected LineReader buildReader(LineReaderBuilder builder) { builder - .appName("Purpur") // Purpur - Rebrand -+ .appName("DivineMC") // DivineMC - Branding ++ .appName("DivineMC") // DivineMC - Rebrand .variable(LineReader.HISTORY_FILE, java.nio.file.Paths.get(".console_history")) .completer(new ConsoleCommandCompleter(this.server)) .option(LineReader.Option.COMPLETE_IN_WORD, true); diff --git a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java -index 0843e7c5c335a58d955a0841f2e02a9e4ac824d9..6db69de0761d04dd74860d97006f82d6755abb42 100644 +index b36e30fd4057a938e4d90cb42a2dca661f16478e..fd860b1f3acbffa923dcfd39f7cb6f23cacbd408 100644 --- a/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java +++ b/src/main/java/io/papermc/paper/ServerBuildInfoImpl.java -@@ -33,6 +33,7 @@ public record ServerBuildInfoImpl( +@@ -32,6 +32,7 @@ public record ServerBuildInfoImpl( + private static final String BRAND_PAPER_NAME = "Paper"; - private static final String BRAND_PUFFERFISH_NAME = "Pufferfish"; // Purpur - Fix pufferfish issues private static final String BRAND_PURPUR_NAME = "Purpur"; // Purpur - Rebrand + private static final String BRAND_DIVINEMC_NAME = "DivineMC"; // DivineMC - Branding private static final String BUILD_DEV = "DEV"; -@@ -44,9 +45,9 @@ public record ServerBuildInfoImpl( +@@ -43,9 +44,9 @@ public record ServerBuildInfoImpl( this( getManifestAttribute(manifest, ATTRIBUTE_BRAND_ID) .map(Key::key) @@ -202,65 +144,8 @@ index 0843e7c5c335a58d955a0841f2e02a9e4ac824d9..6db69de0761d04dd74860d97006f82d6 SharedConstants.getCurrentVersion().getId(), SharedConstants.getCurrentVersion().getName(), getManifestAttribute(manifest, ATTRIBUTE_BUILD_NUMBER) -diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java -index 710477ae27ebc5afdf0012ef0867d05efd293c24..972391f88d9a5fbf6e28ddbf853d610bbcc7f074 100644 ---- a/src/main/java/net/minecraft/CrashReport.java -+++ b/src/main/java/net/minecraft/CrashReport.java -@@ -32,7 +32,7 @@ public class CrashReport { - private boolean trackingStackTrace = true; - private StackTraceElement[] uncategorizedStackTrace = new StackTraceElement[0]; - private final SystemReport systemReport = new SystemReport(); -- private List extraInfo = List.of("", "DO NOT REPORT THIS TO PAPER! REPORT TO PURPUR INSTEAD!", ""); // Purpur - Rebrand -+ private List extraInfo = List.of("", "DO NOT REPORT THIS TO PAPER! REPORT TO DIVINEMC INSTEAD!", ""); // Purpur - Rebrand // DivineMC - - public CrashReport(String message, Throwable cause) { - io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(cause); // Paper -diff --git a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java -index 8f74c2ec5252b6265549589310d742337c91cb2c..1f4c965531393dd81246de487bc86f6d6f73b723 100644 ---- a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java -+++ b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java -@@ -56,7 +56,7 @@ public class MinecraftServerGui extends JComponent { - ; - } - -- final JFrame jframe = new JFrame("Purpur Minecraft server"); // Purpur -+ final JFrame jframe = new JFrame("DivineMC Minecraft server"); // Purpur // DivineMC - final MinecraftServerGui servergui = new MinecraftServerGui(server); - - jframe.setDefaultCloseOperation(2); -@@ -64,7 +64,7 @@ public class MinecraftServerGui extends JComponent { - jframe.pack(); - jframe.setLocationRelativeTo((Component) null); - jframe.setVisible(true); -- jframe.setName("Purpur Minecraft server"); // Paper - Improve ServerGUI // Purpur -+ jframe.setName("DivineMC Minecraft server"); // Paper - Improve ServerGUI // Purpur // DivineMC - - // Paper start - Improve ServerGUI - try { -@@ -76,7 +76,7 @@ public class MinecraftServerGui extends JComponent { - jframe.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent windowevent) { - if (!servergui.isClosing.getAndSet(true)) { -- jframe.setTitle("Purpur Minecraft server - shutting down!"); // Purpur -+ jframe.setTitle("DivineMC Minecraft server - shutting down!"); // Purpur // DivineMC - server.halt(true); - servergui.runFinalizers(); - } -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 a68f27288604b6f6755efe3c8ea612e295cb1656..012e058ee31414b7c7666147336118bf44043357 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 -@@ -289,7 +289,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - - // Paper start - private static void printOversizedLog(String msg, Path file, int x, int z) { -- 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 PURPUR - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); // Purpur - Rebrand -+ 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 DIVINEMC - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); // DivineMC - Branding - } - - private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java -index 2e7c3d4befeb6256ce81ecaa9ed4e8fbcb21651e..5a95655d925c212e5919540fc3b6154521aff2a2 100644 +index 2e7c3d4befeb6256ce81ecaa9ed4e8fbcb21651e..7f3d6dc1c37ffce387ddd04bdd7328e0154b33ed 100644 --- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java @@ -491,7 +491,7 @@ public class CraftScheduler implements BukkitScheduler { @@ -268,12 +153,12 @@ index 2e7c3d4befeb6256ce81ecaa9ed4e8fbcb21651e..5a95655d925c212e5919540fc3b61545 } else { // this.debugTail = this.debugTail.setNext(new CraftAsyncDebugger(this.currentTick + CraftScheduler.RECENT_TICKS, task.getOwner(), task.getTaskClass())); // Paper - task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to Purpur"); // Paper // Purpur - Rebrand -+ task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to DivineMC"); // Paper // DivineMC - Branding ++ task.getOwner().getLogger().log(Level.SEVERE, "Unexpected Async Task in the Sync Scheduler. Report this to DivineMC"); // Paper // DivineMC - Rebrand // We don't need to parse pending // (async tasks must live with race-conditions if they attempt to cancel between these few lines of code) } diff --git a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java -index 99eb04643fce44c37fd96c99756837ccafe7b559..c6c7ae53203fa49d36077c42a8895d4285cd042f 100644 +index 99eb04643fce44c37fd96c99756837ccafe7b559..7f5678aa5c6d6d4365ea219c7c78ff234c554499 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/Versioning.java +++ b/src/main/java/org/bukkit/craftbukkit/util/Versioning.java @@ -11,7 +11,7 @@ public final class Versioning { @@ -281,56 +166,56 @@ index 99eb04643fce44c37fd96c99756837ccafe7b559..c6c7ae53203fa49d36077c42a8895d42 String result = "Unknown-Version"; - InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/org.purpurmc.purpur/purpur-api/pom.properties"); // Pufferfish // Purpur - Rebrand -+ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/space.bxteam.divinemc/divinemc-api/pom.properties"); // DivineMC - Branding ++ InputStream stream = Bukkit.class.getClassLoader().getResourceAsStream("META-INF/maven/space.bxteam.divinemc/divinemc-api/pom.properties"); // DivineMC - Rebrand Properties properties = new Properties(); if (stream != null) { diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java -index 47f168b2d62c9a0eebdd8ab678afd857e7622571..733e6a5b4e00e14a7b907fa0dceaedda93b3c903 100644 +index 6e1e1b45c178b8917cb63d62dec8538a7eb7aa1a..41618cd4f38167d89ca73391fe6d89123a6f88a8 100644 --- a/src/main/java/org/spigotmc/WatchdogThread.java +++ b/src/main/java/org/spigotmc/WatchdogThread.java -@@ -155,14 +155,14 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre +@@ -77,14 +77,14 @@ public class WatchdogThread extends Thread { if (isLongTimeout) { - // Paper end - log.log( Level.SEVERE, "------------------------------" ); -- log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Purpur bug." ); // Paper // Purpur - Rebrand -+ log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a DivineMC bug." ); // Paper // DivineMC - Branding - log.log( Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author" ); - log.log( Level.SEVERE, "\t *Especially* if it looks like HTTP or MySQL operations are occurring" ); - log.log( Level.SEVERE, "If you see a world save or edit, then it means you did far more than your server can handle at once" ); - log.log( Level.SEVERE, "\t If this is the case, consider increasing timeout-time in spigot.yml but note that this will replace the crash with LARGE lag spikes" ); -- log.log( Level.SEVERE, "If you are unsure or still think this is a Purpur bug, please report this to https://github.com/PurpurMC/Purpur/issues" ); // Purpur - Rebrand -+ log.log( Level.SEVERE, "If you are unsure or still think this is a DivineMC bug, please report this to https://github.com/DivineMC/DivineMC/issues" ); // DivineMC - Branding - log.log( Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports" ); -- log.log( Level.SEVERE, "Purpur version: " + Bukkit.getServer().getVersion() ); // Purpur - Rebrand -+ log.log( Level.SEVERE, "DivineMC version: " + Bukkit.getServer().getVersion() ); // DivineMC - Branding - // - if ( net.minecraft.world.level.Level.lastPhysicsProblem != null ) - { -@@ -184,12 +184,12 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre - // Paper end - } else - { -- log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); // Purpur - Rebrand -+ log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO DIVINEMC - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); // DivineMC - Branding - log.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump"); + // Paper end + logger.log(Level.SEVERE, "------------------------------"); +- logger.log(Level.SEVERE, "The server has stopped responding! This is (probably) not a Purpur bug."); // Paper // Purpur - Rebrand ++ logger.log(Level.SEVERE, "The server has stopped responding! This is (probably) not a DivineMC bug."); // Paper // DivineMC - Rebrand + logger.log(Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author"); + logger.log(Level.SEVERE, "\t *Especially* if it looks like HTTP or MySQL operations are occurring"); + logger.log(Level.SEVERE, "If you see a world save or edit, then it means you did far more than your server can handle at once"); + logger.log(Level.SEVERE, "\t If this is the case, consider increasing timeout-time in spigot.yml but note that this will replace the crash with LARGE lag spikes"); +- logger.log(Level.SEVERE, "If you are unsure or still think this is a Purpur bug, please report this to https://github.com/PurpurMC/Purpur/issues"); // Purpur - Rebrand ++ logger.log(Level.SEVERE, "If you are unsure or still think this is a DivineMC bug, please report this to https://github.com/DivineMC/DivineMC/issues"); // DivineMC - Rebrand + logger.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports"); +- logger.log(Level.SEVERE, "Purpur version: " + Bukkit.getServer().getVersion()); // Purpur - Rebrand ++ logger.log(Level.SEVERE, "DivineMC version: " + Bukkit.getServer().getVersion()); // DivineMC - Rebrand + + if (net.minecraft.world.level.Level.lastPhysicsProblem != null) { + logger.log(Level.SEVERE, "------------------------------"); +@@ -104,12 +104,12 @@ public class WatchdogThread extends Thread { + } + // Paper end + } else { +- logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); // Purpur - Rebrand ++ logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO DIVINEMC - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); // DivineMC - Rebrand + logger.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump"); } // Paper end - Different message for short timeout - log.log( Level.SEVERE, "------------------------------" ); -- log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Purpur!):" ); // Paper // Purpur - Rebrand -+ log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to DivineMC!):" ); // Paper // DivineMC - Branding - ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler.dumpAllChunkLoadInfo(MinecraftServer.getServer(), isLongTimeout); // Paper - rewrite chunk system - this.dumpTickingInfo(); // Paper - log detailed tick information - WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); -@@ -205,7 +205,7 @@ public class WatchdogThread extends ca.spottedleaf.moonrise.common.util.TickThre - WatchdogThread.dumpThread( thread, log ); - } + logger.log(Level.SEVERE, "------------------------------"); +- logger.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Purpur!):" ); // Paper // Purpur - Rebrand ++ logger.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to DivineMC!):" ); // Paper // DivineMC - Rebrand + FeatureHooks.dumpAllChunkLoadInfo(MinecraftServer.getServer(), isLongTimeout); // Paper - log detailed tick information + WatchdogThread.dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE), logger); + logger.log(Level.SEVERE, "------------------------------"); +@@ -122,7 +122,7 @@ public class WatchdogThread extends Thread { + WatchdogThread.dumpThread(thread, logger); + } } else { -- log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH ---"); // Purpur - Rebrand -+ log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO DIVINEMC - THIS IS NOT A BUG OR A CRASH ---"); // DivineMC - Branding +- logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PURPUR - THIS IS NOT A BUG OR A CRASH ---"); // Purpur - Rebrand ++ logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO DIVINEMC - THIS IS NOT A BUG OR A CRASH ---"); // DivineMC - Rebrand } - log.log( Level.SEVERE, "------------------------------" ); + logger.log(Level.SEVERE, "------------------------------"); diff --git a/src/main/resources/logo.png b/src/main/resources/logo.png index 518591dd83289e041a16e2c2e7d7e7640d4b2e1b..f54753531b3bf2e8b5377f342465e727c7da98f2 100644 GIT binary patch diff --git a/divinemc-server/paper-patches/features/0002-DivineMC-Configuration.patch b/divinemc-server/paper-patches/features/0002-DivineMC-Configuration.patch new file mode 100644 index 0000000..8295eec --- /dev/null +++ b/divinemc-server/paper-patches/features/0002-DivineMC-Configuration.patch @@ -0,0 +1,60 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sun, 12 Jan 2025 16:19:01 +0300 +Subject: [PATCH] DivineMC Configuration + + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index d072bd2e46abfa9366bc973028e068849c0a0a45..55f9267c83427dbe4ab28580ec515539565f3bdb 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1103,6 +1103,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); + org.purpurmc.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur - Purpur config files ++ space.bxteam.divinemc.configuration.DivineConfig.init((File) console.options.valueOf("divinemc-settings")); // DivineMC - DivineMC config files + for (ServerLevel world : this.console.getAllLevels()) { + // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty + world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) +@@ -1119,6 +1120,7 @@ public final class CraftServer implements Server { + } + world.spigotConfig.init(); // Spigot + world.purpurConfig.init(); // Purpur - Purpur config files ++ world.divinemcConfig.init(); // DivineMC - DivineMC config files + } + + Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper +@@ -3130,6 +3132,13 @@ public final class CraftServer implements Server { + return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console); + } + ++ // DivineMC start - DivineMC configuration ++ @Override ++ public YamlConfiguration getDivineConfig() { ++ return space.bxteam.divinemc.configuration.DivineConfig.config; ++ } ++ // DivineMC end - DivineMC configuration ++ + // Purpur start - Purpur config files + @Override + public YamlConfiguration getPurpurConfig() { +diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java +index 90fb036607e752fcf43575be4f03bbf00bbb05c7..cefb51630b26cac0dd3d69973437b14532f59710 100644 +--- a/src/main/java/org/bukkit/craftbukkit/Main.java ++++ b/src/main/java/org/bukkit/craftbukkit/Main.java +@@ -183,6 +183,15 @@ public class Main { + .defaultsTo(new File("purpur.yml")) + .describedAs("Yml file"); + // Purpur end - Purpur config files ++ ++ // DivineMC start - DivineMC config files ++ acceptsAll(asList("divinemc", "divinemc-settings"), "File for divinemc settings") ++ .withRequiredArg() ++ .ofType(File.class) ++ .defaultsTo(new File("divinemc.yml")) ++ .describedAs("Yml file"); ++ // DivineMC end - DivineMC config files ++ + // Paper start + acceptsAll(asList("server-name"), "Name of the server") + .withRequiredArg() diff --git a/patches/server/0003-Optimize-default-values-for-configs.patch b/divinemc-server/paper-patches/features/0003-Optimize-default-values-for-configs.patch similarity index 60% rename from patches/server/0003-Optimize-default-values-for-configs.patch rename to divinemc-server/paper-patches/features/0003-Optimize-default-values-for-configs.patch index afb4286..1f0bf79 100644 --- a/patches/server/0003-Optimize-default-values-for-configs.patch +++ b/divinemc-server/paper-patches/features/0003-Optimize-default-values-for-configs.patch @@ -1,30 +1,30 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 27 Mar 2023 13:16:41 +0300 +Date: Sat, 11 Jan 2025 23:18:11 +0300 Subject: [PATCH] Optimize default values for configs diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java -index 088b8fe5d144807f4da1e85b2fa34dfd21286f8c..0e35a057eb408942f49f472bb36f8c77af8aa364 100644 +index d4a0b684e7e5a5d7ea1106d0adeccf12d501676c..5ff509211843028ed692b37e286db425607bfed3 100644 --- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java -@@ -336,9 +336,9 @@ public class GlobalConfiguration extends ConfigurationPart { +@@ -340,9 +340,9 @@ public class GlobalConfiguration extends ConfigurationPart { public boolean fixEntityPositionDesync = true; public boolean loadPermissionsYmlBeforePlugins = true; @Constraints.Min(4) - public int regionFileCacheSize = 256; -+ public int regionFileCacheSize = 512; // DivineMC - optimize default values for configs ++ public int regionFileCacheSize = 512; // DivineMC - Optimize default values for configs @Comment("See https://luckformula.emc.gs") - public boolean useAlternativeLuckFormula = false; -+ public boolean useAlternativeLuckFormula = true; // DivineMC - optimize default values for configs ++ public boolean useAlternativeLuckFormula = true; // DivineMC - Optimize default values for configs public boolean useDimensionTypeForCustomSpawners = false; public boolean strictAdvancementDimensionCheck = false; public IntOr.Default compressionLevel = IntOr.Default.USE_DEFAULT; diff --git a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java -index 82210667376fd466d5d4cdcb56b62f6165bd5cde..0fef474e1bd0298c212b98369042917ebd6f475e 100644 +index a426ba82af695426952bb5e04fa721e6ccff2f89..6ae624f873c77625c7ff9a1b94ff015cc6d321f0 100644 --- a/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java +++ b/src/main/java/io/papermc/paper/configuration/WorldConfiguration.java -@@ -156,9 +156,9 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -145,9 +145,9 @@ public class WorldConfiguration extends ConfigurationPart { public ArmorStands armorStands; @@ -37,85 +37,85 @@ index 82210667376fd466d5d4cdcb56b62f6165bd5cde..0fef474e1bd0298c212b98369042917e } public Markers markers; -@@ -281,8 +281,8 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -270,8 +270,8 @@ public class WorldConfiguration extends ConfigurationPart { public Behavior behavior; public class Behavior extends ConfigurationPart { - public boolean disableChestCatDetection = false; - public boolean spawnerNerfedMobsShouldJump = false; -+ public boolean disableChestCatDetection = true; // DivineMC - optimize default values for configs -+ public boolean spawnerNerfedMobsShouldJump = true; // DivineMC - optimize default values for configs ++ public boolean disableChestCatDetection = true; // DivineMC - Optimize default values for configs ++ public boolean spawnerNerfedMobsShouldJump = true; // DivineMC - Optimize default values for configs public int experienceMergeMaxValue = -1; public boolean shouldRemoveDragon = false; public boolean zombiesTargetTurtleEggs = true; -@@ -306,7 +306,7 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -295,7 +295,7 @@ public class WorldConfiguration extends ConfigurationPart { public int playerInsomniaStartTicks = 72000; public int phantomsSpawnAttemptMinSeconds = 60; public int phantomsSpawnAttemptMaxSeconds = 119; - public boolean parrotsAreUnaffectedByPlayerMovement = false; -+ public boolean parrotsAreUnaffectedByPlayerMovement = true; // DivineMC - optimize default values for configs ++ public boolean parrotsAreUnaffectedByPlayerMovement = true; // DivineMC - Optimize default values for configs @BelowZeroToEmpty public DoubleOr.Default zombieVillagerInfectionChance = DoubleOr.Default.USE_DEFAULT; public MobsCanAlwaysPickUpLoot mobsCanAlwaysPickUpLoot; -@@ -317,7 +317,7 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -306,7 +306,7 @@ public class WorldConfiguration extends ConfigurationPart { } public boolean disablePlayerCrits = false; - public boolean nerfPigmenFromNetherPortals = false; -+ public boolean nerfPigmenFromNetherPortals = true; // DivineMC - optimize default values for configs ++ public boolean nerfPigmenFromNetherPortals = true; // DivineMC - Optimize default values for configs @Comment("Prevents merging items that are not on the same y level, preventing potential visual artifacts.") public boolean onlyMergeItemsHorizontally = false; public PillagerPatrols pillagerPatrols; -@@ -415,7 +415,7 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -404,7 +404,7 @@ public class WorldConfiguration extends ConfigurationPart { public class Environment extends ConfigurationPart { public boolean disableThunder = false; public boolean disableIceAndSnow = false; - public boolean optimizeExplosions = false; -+ public boolean optimizeExplosions = true; // DivineMC - optimize default values for configs ++ public boolean optimizeExplosions = true; // DivineMC - Optimize default values for configs public boolean disableExplosionKnockback = false; public boolean generateFlatBedrock = false; public FrostedIce frostedIce; -@@ -465,7 +465,7 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -453,7 +453,7 @@ public class WorldConfiguration extends ConfigurationPart { public class Maps extends ConfigurationPart { public int itemFrameCursorLimit = 128; - public int itemFrameCursorUpdateInterval = 10; -+ public int itemFrameCursorUpdateInterval = 20; // DivineMC - optimize default values for configs ++ public int itemFrameCursorUpdateInterval = 20; // DivineMC - Optimize default values for configs } public Fixes fixes; -@@ -491,7 +491,7 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -479,7 +479,7 @@ public class WorldConfiguration extends ConfigurationPart { public class Hopper extends ConfigurationPart { public boolean cooldownWhenFull = true; public boolean disableMoveEvent = false; - public boolean ignoreOccludingBlocks = false; -+ public boolean ignoreOccludingBlocks = true; // DivineMC - optimize default values for configs ++ public boolean ignoreOccludingBlocks = true; // DivineMC - Optimize default values for configs } public Collisions collisions; -@@ -499,9 +499,9 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -487,9 +487,9 @@ public class WorldConfiguration extends ConfigurationPart { public class Collisions extends ConfigurationPart { public boolean onlyPlayersCollide = false; public boolean allowVehicleCollisions = true; - public boolean fixClimbingBypassingCrammingRule = false; -+ public boolean fixClimbingBypassingCrammingRule = true; // DivineMC - optimize default values for configs ++ public boolean fixClimbingBypassingCrammingRule = true; // DivineMC - Optimize default values for configs @RequiresSpigotInitialization(MaxEntityCollisionsInitializer.class) - public int maxEntityCollisions = 8; -+ public int maxEntityCollisions = 2; // DivineMC - optimize default values for configs ++ public int maxEntityCollisions = 2; // DivineMC - Optimize default values for configs public boolean allowPlayerCrammingDamage = false; } -@@ -509,18 +509,31 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -497,18 +497,31 @@ public class WorldConfiguration extends ConfigurationPart { public class Chunks extends ConfigurationPart { public AutosavePeriod autoSaveInterval = AutosavePeriod.def(); - public int maxAutoSaveChunksPerTick = 24; -+ public int maxAutoSaveChunksPerTick = 12; ++ public int maxAutoSaveChunksPerTick = 12; // DivineMC - Optimize default values for configs public int fixedChunkInhabitedTime = -1; - public boolean preventMovingIntoUnloadedChunks = false; - public Duration delayChunkUnloadsBy = Duration.of("10s"); -+ public boolean preventMovingIntoUnloadedChunks = true; // DivineMC - optimize default values for configs -+ public Duration delayChunkUnloadsBy = Duration.of("5s"); // DivineMC - optimize default values for configs ++ public boolean preventMovingIntoUnloadedChunks = true; ++ public Duration delayChunkUnloadsBy = Duration.of("5s"); public Reference2IntMap> entityPerChunkSaveLimit = Util.make(new Reference2IntOpenHashMap<>(BuiltInRegistries.ENTITY_TYPE.size()), map -> { - map.defaultReturnValue(-1); - map.put(EntityType.EXPERIENCE_ORB, -1); @@ -124,7 +124,7 @@ index 82210667376fd466d5d4cdcb56b62f6165bd5cde..0fef474e1bd0298c212b98369042917e - map.put(EntityType.ARROW, -1); - map.put(EntityType.FIREBALL, -1); - map.put(EntityType.SMALL_FIREBALL, -1); -+ // DivineMC start - optimize default values for configs ++ // DivineMC start - Optimize default values for configs + map.put(EntityType.EXPERIENCE_ORB, 16); + map.put(EntityType.SNOWBALL, 8); + map.put(EntityType.ENDER_PEARL, 8); @@ -147,203 +147,173 @@ index 82210667376fd466d5d4cdcb56b62f6165bd5cde..0fef474e1bd0298c212b98369042917e }); public boolean flushRegionsOnSave = false; } -@@ -535,13 +548,15 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -523,13 +536,13 @@ public class WorldConfiguration extends ConfigurationPart { public TickRates tickRates; public class TickRates extends ConfigurationPart { - public int grassSpread = 1; - public int containerUpdate = 1; - public int mobSpawner = 1; -+ // DivineMC start - optimize default values for configs -+ public int grassSpread = 4; -+ public int containerUpdate = 3; -+ public int mobSpawner = 2; ++ public int grassSpread = 4; // DivineMC - Optimize default values for configs ++ public int containerUpdate = 3; // DivineMC - Optimize default values for configs ++ public int mobSpawner = 2; // DivineMC - Optimize default values for configs public int wetFarmland = 1; public int dryFarmland = 1; - public Table, String, Integer> sensor = Util.make(HashBasedTable.create(), table -> table.put(EntityType.VILLAGER, "secondarypoisensor", 40)); - public Table, String, Integer> behavior = Util.make(HashBasedTable.create(), table -> table.put(EntityType.VILLAGER, "validatenearbypoi", -1)); -+ public Table, String, Integer> sensor = Util.make(HashBasedTable.create(), table -> table.put(EntityType.VILLAGER, "secondarypoisensor", 80)); -+ public Table, String, Integer> behavior = Util.make(HashBasedTable.create(), table -> table.put(EntityType.VILLAGER, "validatenearbypoi", 60)); -+ // DivineMC end ++ public Table, String, Integer> sensor = Util.make(HashBasedTable.create(), table -> table.put(EntityType.VILLAGER, "secondarypoisensor", 80)); // DivineMC - Optimize default values for configs ++ public Table, String, Integer> behavior = Util.make(HashBasedTable.create(), table -> table.put(EntityType.VILLAGER, "validatenearbypoi", 60)); // DivineMC - Optimize default values for configs } @Setting(FeatureSeedsGeneration.FEATURE_SEEDS_KEY) -@@ -550,7 +565,7 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -538,7 +551,7 @@ public class WorldConfiguration extends ConfigurationPart { public class FeatureSeeds extends ConfigurationPart { @SuppressWarnings("unused") // Is used in FeatureSeedsGeneration @Setting(FeatureSeedsGeneration.GENERATE_KEY) - public boolean generateRandomSeedsForAll = false; -+ public boolean generateRandomSeedsForAll = true; // DivineMC - optimize default values for configs ++ public boolean generateRandomSeedsForAll = true; // DivineMC - Optimize default values for configs @Setting(FeatureSeedsGeneration.FEATURES_KEY) public Reference2LongMap>> features = new Reference2LongOpenHashMap<>(); -@@ -571,9 +586,9 @@ public class WorldConfiguration extends ConfigurationPart { +@@ -559,9 +572,9 @@ public class WorldConfiguration extends ConfigurationPart { public class Misc extends ConfigurationPart { public int lightQueueSize = 20; - public boolean updatePathfindingOnBlockUpdate = true; -+ public boolean updatePathfindingOnBlockUpdate = false; // DivineMC - optimize default values for configs ++ public boolean updatePathfindingOnBlockUpdate = false; // DivineMC - Optimize default values for configs public boolean showSignClickCommandFailureMsgsToPlayer = false; - public RedstoneImplementation redstoneImplementation = RedstoneImplementation.VANILLA; -+ public RedstoneImplementation redstoneImplementation = RedstoneImplementation.ALTERNATE_CURRENT; // DivineMC - optimize default values for configs ++ public RedstoneImplementation redstoneImplementation = RedstoneImplementation.ALTERNATE_CURRENT; // DivineMC - Optimize default values for configs public AlternateCurrentUpdateOrder alternateCurrentUpdateOrder = AlternateCurrentUpdateOrder.HORIZONTAL_FIRST_OUTWARD; public boolean disableEndCredits = false; public DoubleOr.Default maxLeashDistance = DoubleOr.Default.USE_DEFAULT; -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index c9fc57d6b00bf5ca57805c9ad2829646de347475..75919064593167b51c4325ba6d500366ba8408f1 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -352,7 +352,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - String proxyFlavor = (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) ? "Velocity" : "BungeeCord"; - String proxyLink = (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) ? "https://docs.papermc.io/velocity/security" : "http://www.spigotmc.org/wiki/firewall-guide/"; - // Paper end - Add Velocity IP Forwarding Support -- if (!this.usesAuthentication()) { -+ if (!io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode()) { // DivineMC - if server uses proxy (velocity or bungee), disable offline warning - DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); - DedicatedServer.LOGGER.warn("The server will make no attempt to authenticate usernames. Beware."); - // Spigot start -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 7690441b5059ae6c7ca8519875ea8a515c5c5e93..6a161bb00b4665cf0b148bc7eabd8b07c6b07e99 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -244,7 +244,7 @@ public class PurpurConfig { - laggingThreshold = getDouble("settings.lagging-threshold", laggingThreshold); - } - -- public static boolean useAlternateKeepAlive = false; -+ public static boolean useAlternateKeepAlive = true; - private static void useAlternateKeepAlive() { - useAlternateKeepAlive = getBoolean("settings.use-alternate-keepalive", useAlternateKeepAlive); - } diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java -index 4dbb109d0526afee99b9190fc256585121aac9b5..85a5d8e26832c88afc212c3f600e686076596fbb 100644 +index e0d4222a99f22d7130d95cf29b034a98f2f3b76e..ecedb1ba79fa40180ff7eb16d12a4602ab357de3 100644 --- a/src/main/java/org/spigotmc/SpigotConfig.java +++ b/src/main/java/org/spigotmc/SpigotConfig.java -@@ -331,7 +331,7 @@ public class SpigotConfig +@@ -269,7 +269,7 @@ public class SpigotConfig { + public static boolean saveUserCacheOnStopOnly; - private static void saveUserCacheOnStopOnly() - { -- SpigotConfig.saveUserCacheOnStopOnly = SpigotConfig.getBoolean( "settings.save-user-cache-on-stop-only", false ); -+ SpigotConfig.saveUserCacheOnStopOnly = SpigotConfig.getBoolean( "settings.save-user-cache-on-stop-only", true ); // DivineMC - optimize default values for configs + private static void saveUserCacheOnStopOnly() { +- SpigotConfig.saveUserCacheOnStopOnly = SpigotConfig.getBoolean("settings.save-user-cache-on-stop-only", false); ++ SpigotConfig.saveUserCacheOnStopOnly = SpigotConfig.getBoolean("settings.save-user-cache-on-stop-only", true); // DivineMC - Optimize default values for configs } public static double movedWronglyThreshold; -@@ -395,8 +395,10 @@ public class SpigotConfig +@@ -323,9 +323,9 @@ public class SpigotConfig { + public static boolean logVillagerDeaths; public static boolean logNamedDeaths; - private static void logDeaths() { +- private static void logDeaths() { - SpigotConfig.logVillagerDeaths = SpigotConfig.getBoolean("settings.log-villager-deaths", true); - SpigotConfig.logNamedDeaths = SpigotConfig.getBoolean("settings.log-named-deaths", true); -+ // DivineMC start - optimize default values for configs ++ private static void logDeaths() { // DivineMC - Optimize default values for configs + SpigotConfig.logVillagerDeaths = SpigotConfig.getBoolean("settings.log-villager-deaths", false); + SpigotConfig.logNamedDeaths = SpigotConfig.getBoolean("settings.log-named-deaths", false); -+ // DivineMC end } public static boolean disablePlayerDataSaving; diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java -index 2c408fa4abcbe1171c58aee8799c8cf7867d0f0a..0e6b697c6e8503beeed9412abddad2b512246b37 100644 +index 89e2adbc1e1a0709d03e151e3ffcdbff10a44098..5b305092a808c2b9b339b9072bf7f7bfc00f0b8a 100644 --- a/src/main/java/org/spigotmc/SpigotWorldConfig.java +++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java -@@ -150,14 +150,14 @@ public class SpigotWorldConfig +@@ -135,13 +135,13 @@ public class SpigotWorldConfig { + public double itemMerge; - private void itemMerge() - { -- this.itemMerge = this.getDouble("merge-radius.item", 0.5 ); -+ this.itemMerge = this.getDouble("merge-radius.item", 3.5 ); // DivineMC - optimize default values for configs - this.log( "Item Merge Radius: " + this.itemMerge ); + private void itemMerge() { +- this.itemMerge = this.getDouble("merge-radius.item", 0.5); ++ this.itemMerge = this.getDouble("merge-radius.item", 3.5); // DivineMC - Optimize default values for configs + this.log("Item Merge Radius: " + this.itemMerge); } public double expMerge; - private void expMerge() - { -- this.expMerge = this.getDouble("merge-radius.exp", -1 ); -+ this.expMerge = this.getDouble("merge-radius.exp", 4.0 ); // DivineMC - optimize default values for configs - this.log( "Experience Merge Radius: " + this.expMerge ); + private void expMerge() { +- this.expMerge = this.getDouble("merge-radius.exp", -1); ++ this.expMerge = this.getDouble("merge-radius.exp", 4.0); // DivineMC - Optimize default values for configs + this.log("Experience Merge Radius: " + this.expMerge); } -@@ -196,7 +196,7 @@ public class SpigotWorldConfig +@@ -174,7 +174,7 @@ public class SpigotWorldConfig { + public byte mobSpawnRange; - private void mobSpawnRange() - { -- this.mobSpawnRange = (byte) getInt( "mob-spawn-range", 8 ); // Paper - Vanilla -+ this.mobSpawnRange = (byte) getInt( "mob-spawn-range", 2 ); // Paper - Vanilla // DivineMC - optimize default values for configs - this.log( "Mob Spawn Range: " + this.mobSpawnRange ); + private void mobSpawnRange() { +- this.mobSpawnRange = (byte) getInt("mob-spawn-range", 8); // Paper - Vanilla ++ this.mobSpawnRange = (byte) getInt("mob-spawn-range", 2); // DivineMC - Optimize default values for configs + this.log("Mob Spawn Range: " + this.mobSpawnRange); } -@@ -207,14 +207,16 @@ public class SpigotWorldConfig - this.log( "Item Despawn Rate: " + this.itemDespawnRate ); +@@ -184,14 +184,16 @@ public class SpigotWorldConfig { + this.log("Item Despawn Rate: " + this.itemDespawnRate); } - public int animalActivationRange = 32; - public int monsterActivationRange = 32; - public int raiderActivationRange = 64; - public int miscActivationRange = 16; -+ // DivineMC start - optimize default values for configs ++ // DivineMC start - Optimize default values for configs + public int animalActivationRange = 16; + public int monsterActivationRange = 24; + public int raiderActivationRange = 48; + public int miscActivationRange = 8; -+ // DivineMC end ++ // DivineMC end - Optimize default values for configs // Paper start public int flyingMonsterActivationRange = 32; - public int waterActivationRange = 16; - public int villagerActivationRange = 32; -+ public int waterActivationRange = 8; // DivineMC - optimize default values for configs -+ public int villagerActivationRange = 16; // DivineMC - optimize default values for configs ++ public int waterActivationRange = 8; // DivineMC - Optimize default values for configs ++ public int villagerActivationRange = 16; // DivineMC - Optimize default values for configs public int wakeUpInactiveAnimals = 4; - public int wakeUpInactiveAnimalsEvery = 60*20; - public int wakeUpInactiveAnimalsFor = 5*20; -@@ -231,7 +233,7 @@ public class SpigotWorldConfig + public int wakeUpInactiveAnimalsEvery = 60 * 20; + public int wakeUpInactiveAnimalsFor = 5 * 20; +@@ -208,7 +210,7 @@ public class SpigotWorldConfig { public int villagersWorkImmunityFor = 20; public boolean villagersActiveForPanic = true; // Paper end - public boolean tickInactiveVillagers = true; -+ public boolean tickInactiveVillagers = false; // DivineMC - optimize default values for configs ++ public boolean tickInactiveVillagers = false; // DivineMC - Optimize default values for configs public boolean ignoreSpectatorActivation = false; - private void activationRange() - { -@@ -299,7 +301,7 @@ public class SpigotWorldConfig - { - this.set( "ticks-per.hopper-check", 1 ); + + private void activationRange() { +@@ -273,7 +275,7 @@ public class SpigotWorldConfig { + if (SpigotConfig.version < 11) { + this.set("ticks-per.hopper-check", 1); } -- this.hopperCheck = this.getInt( "ticks-per.hopper-check", 1 ); -+ this.hopperCheck = this.getInt( "ticks-per.hopper-check", 8 ); // DivineMC - optimize default values for configs - this.hopperAmount = this.getInt( "hopper-amount", 1 ); - this.hopperCanLoadChunks = this.getBoolean( "hopper-can-load-chunks", false ); - this.log( "Hopper Transfer: " + this.hopperTransfer + " Hopper Check: " + this.hopperCheck + " Hopper Amount: " + this.hopperAmount + " Hopper Can Load Chunks: " + this.hopperCanLoadChunks ); -@@ -309,7 +311,7 @@ public class SpigotWorldConfig +- this.hopperCheck = this.getInt("ticks-per.hopper-check", 1); ++ this.hopperCheck = this.getInt("ticks-per.hopper-check", 8); // DivineMC - Optimize default values for configs + this.hopperAmount = this.getInt("hopper-amount", 1); + this.hopperCanLoadChunks = this.getBoolean("hopper-can-load-chunks", false); + this.log("Hopper Transfer: " + this.hopperTransfer + " Hopper Check: " + this.hopperCheck + " Hopper Amount: " + this.hopperAmount + " Hopper Can Load Chunks: " + this.hopperCanLoadChunks); +@@ -282,7 +284,7 @@ public class SpigotWorldConfig { + public int arrowDespawnRate; public int tridentDespawnRate; - private void arrowDespawnRate() - { -- this.arrowDespawnRate = this.getInt( "arrow-despawn-rate", 1200 ); -+ this.arrowDespawnRate = this.getInt( "arrow-despawn-rate", 300 ); // DivineMC - optimize default values for configs - this.tridentDespawnRate = this.getInt( "trident-despawn-rate", this.arrowDespawnRate ); - this.log( "Arrow Despawn Rate: " + this.arrowDespawnRate + " Trident Respawn Rate:" + this.tridentDespawnRate ); + private void arrowDespawnRate() { +- this.arrowDespawnRate = this.getInt("arrow-despawn-rate", 1200); ++ this.arrowDespawnRate = this.getInt("arrow-despawn-rate", 300); // DivineMC - Optimize default values for configs + this.tridentDespawnRate = this.getInt("trident-despawn-rate", this.arrowDespawnRate); + this.log("Arrow Despawn Rate: " + this.arrowDespawnRate + " Trident Respawn Rate:" + this.tridentDespawnRate); } -@@ -324,14 +326,14 @@ public class SpigotWorldConfig +@@ -295,13 +297,13 @@ public class SpigotWorldConfig { + public boolean nerfSpawnerMobs; - private void nerfSpawnerMobs() - { -- this.nerfSpawnerMobs = this.getBoolean( "nerf-spawner-mobs", false ); -+ this.nerfSpawnerMobs = this.getBoolean( "nerf-spawner-mobs", true ); // DivineMC - optimize default values for configs - this.log( "Nerfing mobs spawned from spawners: " + this.nerfSpawnerMobs ); + private void nerfSpawnerMobs() { +- this.nerfSpawnerMobs = this.getBoolean("nerf-spawner-mobs", false); ++ this.nerfSpawnerMobs = this.getBoolean("nerf-spawner-mobs", true); // DivineMC - Optimize default values for configs + this.log("Nerfing mobs spawned from spawners: " + this.nerfSpawnerMobs); } public boolean enableZombiePigmenPortalSpawns; - private void enableZombiePigmenPortalSpawns() - { -- this.enableZombiePigmenPortalSpawns = this.getBoolean( "enable-zombie-pigmen-portal-spawns", true ); -+ this.enableZombiePigmenPortalSpawns = this.getBoolean( "enable-zombie-pigmen-portal-spawns", false ); // DivineMC - optimize default values for configs - this.log( "Allow Zombie Pigmen to spawn from portal blocks: " + this.enableZombiePigmenPortalSpawns ); + private void enableZombiePigmenPortalSpawns() { +- this.enableZombiePigmenPortalSpawns = this.getBoolean("enable-zombie-pigmen-portal-spawns", true); ++ this.enableZombiePigmenPortalSpawns = this.getBoolean("enable-zombie-pigmen-portal-spawns", false); // DivineMC - Optimize default values for configs + this.log("Allow Zombie Pigmen to spawn from portal blocks: " + this.enableZombiePigmenPortalSpawns); } -@@ -449,7 +451,7 @@ public class SpigotWorldConfig +@@ -413,7 +415,7 @@ public class SpigotWorldConfig { + public int hangingTickFrequency; - private void hangingTickFrequency() - { -- this.hangingTickFrequency = this.getInt( "hanging-tick-frequency", 100 ); -+ this.hangingTickFrequency = this.getInt( "hanging-tick-frequency", 200 ); // DivineMC - optimize default values for configs + private void hangingTickFrequency() { +- this.hangingTickFrequency = this.getInt("hanging-tick-frequency", 100); ++ this.hangingTickFrequency = this.getInt("hanging-tick-frequency", 200); // DivineMC - Optimize default values for configs } public int tileMaxTickTime; diff --git a/divinemc-server/paper-patches/features/0004-Remove-Spigot-tick-limiter.patch b/divinemc-server/paper-patches/features/0004-Remove-Spigot-tick-limiter.patch new file mode 100644 index 0000000..f1c0c6a --- /dev/null +++ b/divinemc-server/paper-patches/features/0004-Remove-Spigot-tick-limiter.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sun, 12 Jan 2025 01:47:27 +0300 +Subject: [PATCH] Remove Spigot tick limiter + + +diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java +index 5b305092a808c2b9b339b9072bf7f7bfc00f0b8a..c9ff04efcdf063326119b9f65630979d38dc7abf 100644 +--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java ++++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java +@@ -418,6 +418,7 @@ public class SpigotWorldConfig { + this.hangingTickFrequency = this.getInt("hanging-tick-frequency", 200); // DivineMC - Optimize default values for configs + } + ++ /* DivineMC - remove tick limiter + public int tileMaxTickTime; + public int entityMaxTickTime; + private void maxTickTimes() { +@@ -425,6 +426,7 @@ public class SpigotWorldConfig { + this.entityMaxTickTime = this.getInt("max-tick-time.entity", 50); + this.log("Tile Max Tick Time: " + this.tileMaxTickTime + "ms Entity max Tick Time: " + this.entityMaxTickTime + "ms"); + } ++ */ + + public int thunderChance; + private void thunderChance() { +diff --git a/src/main/java/org/spigotmc/TickLimiter.java b/src/main/java/org/spigotmc/TickLimiter.java +deleted file mode 100644 +index 961489499e220d71339771dcabf151edeaf6d231..0000000000000000000000000000000000000000 +--- a/src/main/java/org/spigotmc/TickLimiter.java ++++ /dev/null +@@ -1,20 +0,0 @@ +-package org.spigotmc; +- +-public class TickLimiter { +- +- private final int maxTime; +- private long startTime; +- +- public TickLimiter(int maxTime) { +- this.maxTime = maxTime; +- } +- +- public void initTick() { +- this.startTime = System.currentTimeMillis(); +- } +- +- public boolean shouldContinue() { +- long remaining = System.currentTimeMillis() - this.startTime; +- return remaining < this.maxTime; +- } +-} diff --git a/divinemc-server/paper-patches/features/0005-Implement-Secure-Seed.patch b/divinemc-server/paper-patches/features/0005-Implement-Secure-Seed.patch new file mode 100644 index 0000000..317e53a --- /dev/null +++ b/divinemc-server/paper-patches/features/0005-Implement-Secure-Seed.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sat, 20 Jul 2024 22:04:52 +0300 +Subject: [PATCH] Implement Secure Seed + +Original license: GPLv3 +Original project: https://github.com/plasmoapp/matter + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +index de8b9048c8395c05b8688bc9d984b8ad680f15b3..287fd2f3931ab776203f322fb05dfa236d94c59b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +@@ -206,7 +206,12 @@ public class CraftChunk implements Chunk { + @Override + public boolean isSlimeChunk() { + // 987234911L is deterimined in EntitySlime when seeing if a slime can spawn in a chunk +- return this.worldServer.paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(this.getX(), this.getZ(), this.getWorld().getSeed(), worldServer.spigotConfig.slimeSeed).nextInt(10) == 0; // Paper ++ // DivineMC start - Implement Secure Seed ++ boolean isSlimeChunk = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed ++ ? worldServer.getChunk(this.getX(), this.getZ()).isSlimeChunk() ++ : WorldgenRandom.seedSlimeChunk(this.getX(), this.getZ(), this.getWorld().getSeed(), worldServer.spigotConfig.slimeSeed).nextInt(10) == 0; // Paper ++ return this.worldServer.paperConfig().entities.spawning.allChunksAreSlimeChunks || isSlimeChunk; ++ // DivineMC end - Implement Secure Seed + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 55f9267c83427dbe4ab28580ec515539565f3bdb..bfcf494a34fad26f282b705270725b724d5719a7 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1404,7 +1404,11 @@ public final class CraftServer implements Server { + registryAccess = levelDataAndDimensions.dimensions().dimensionsRegistryAccess(); + } else { + LevelSettings levelSettings; +- WorldOptions worldOptions = new WorldOptions(creator.seed(), creator.generateStructures(), false); ++ // DivineMC start - Implement Secure Seed ++ WorldOptions worldOptions = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed ++ ? new WorldOptions(creator.seed(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), creator.generateStructures(), false) ++ : new WorldOptions(creator.seed(), creator.generateStructures(), false); ++ // DivineMC end - Implement Secure Seed + WorldDimensions worldDimensions; + + DedicatedServerProperties.WorldDimensionData properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse((creator.generatorSettings().isEmpty()) ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT)); diff --git a/divinemc-server/paper-patches/files/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java.patch b/divinemc-server/paper-patches/files/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java.patch new file mode 100644 index 0000000..88bc6e5 --- /dev/null +++ b/divinemc-server/paper-patches/files/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java ++++ b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java +@@ -295,7 +_,7 @@ + + private static GameProfile createAuthLibProfile(UUID uniqueId, String name) { + Preconditions.checkArgument(name == null || name.length() <= 16, "Name cannot be longer than 16 characters"); +- Preconditions.checkArgument(name == null || StringUtil.isValidPlayerName(name), "The name of the profile contains invalid characters: %s", name); ++ Preconditions.checkArgument(name == null || space.bxteam.divinemc.configuration.DivineConfig.removeVanillaUsernameCheck || StringUtil.isValidPlayerName(name), "The name of the profile contains invalid characters: %s", name); // DivineMC - Remove vanilla username check + return new GameProfile( + uniqueId != null ? uniqueId : Util.NIL_UUID, + name != null ? name : "" diff --git a/divinemc-server/paper-patches/files/src/main/java/io/papermc/paper/adventure/ChatProcessor.java.patch b/divinemc-server/paper-patches/files/src/main/java/io/papermc/paper/adventure/ChatProcessor.java.patch new file mode 100644 index 0000000..d1cbdce --- /dev/null +++ b/divinemc-server/paper-patches/files/src/main/java/io/papermc/paper/adventure/ChatProcessor.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/io/papermc/paper/adventure/ChatProcessor.java ++++ b/src/main/java/io/papermc/paper/adventure/ChatProcessor.java +@@ -317,7 +_,7 @@ + + private void sendToServer(final ChatType.Bound chatType, final @Nullable Function msgFunction) { + final PlayerChatMessage toConsoleMessage = msgFunction == null ? ChatProcessor.this.message : ChatProcessor.this.message.withUnsignedContent(msgFunction.apply(ChatProcessor.this.server.console)); +- ChatProcessor.this.server.logChatMessage(toConsoleMessage.decoratedContent(), chatType, ChatProcessor.this.server.getPlayerList().verifyChatTrusted(toConsoleMessage) ? null : "Not Secure"); ++ ChatProcessor.this.server.logChatMessage(toConsoleMessage.decoratedContent(), chatType, ChatProcessor.this.server.getPlayerList().verifyChatTrusted(toConsoleMessage) || space.bxteam.divinemc.configuration.DivineConfig.noChatSign ? null : "Not Secure"); // DivineMC - No chat sign + } + } + diff --git a/divinemc-server/paper-patches/files/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java.patch b/divinemc-server/paper-patches/files/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java.patch new file mode 100644 index 0000000..a9a4413 --- /dev/null +++ b/divinemc-server/paper-patches/files/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java.patch @@ -0,0 +1,52 @@ +--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java ++++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java +@@ -1,6 +_,5 @@ + package io.papermc.paper.plugin.manager; + +-import co.aikar.timings.TimedEventExecutor; + import com.destroystokyo.paper.event.server.ServerExceptionEvent; + import com.destroystokyo.paper.exception.ServerEventException; + import com.google.common.collect.Sets; +@@ -36,15 +_,22 @@ + + // SimplePluginManager + public void callEvent(@NotNull Event event) { ++ // DivineMC start - Skip event if no listeners ++ RegisteredListener[] listeners = event.getHandlers().getRegisteredListeners(); ++ if (listeners.length == 0) return; ++ // DivineMC end - Skip event if no listeners + if (event.isAsynchronous() && this.server.isPrimaryThread()) { + throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously."); + } else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) { ++ // DivineMC start - Multithreaded tracker ++ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled) { ++ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(event::callEvent); ++ return; ++ } ++ // DivineMC end - Multithreaded tracker + throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously."); + } + +- HandlerList handlers = event.getHandlers(); +- RegisteredListener[] listeners = handlers.getRegisteredListeners(); +- + for (RegisteredListener registration : listeners) { + if (!registration.getPlugin().isEnabled()) { + continue; +@@ -95,7 +_,6 @@ + throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled"); + } + +- executor = new TimedEventExecutor(executor, plugin, null, event); + this.getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); + } + +@@ -182,7 +_,7 @@ + } + } + +- EventExecutor executor = new TimedEventExecutor(EventExecutor.create(method, eventClass), plugin, method, eventClass); ++ EventExecutor executor = EventExecutor.create(method, eventClass); + eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); + } + return ret; diff --git a/divinemc-server/paper-patches/files/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java.patch b/divinemc-server/paper-patches/files/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java.patch new file mode 100644 index 0000000..1e596c9 --- /dev/null +++ b/divinemc-server/paper-patches/files/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java.patch @@ -0,0 +1,11 @@ +--- a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java ++++ b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java +@@ -232,7 +_,7 @@ + + @Override + public boolean useTimings() { +- return co.aikar.timings.Timings.isTimingsEnabled(); ++ return false; + } + + @Override diff --git a/patches/server/0029-Optimize-CraftServer.getWorld-UUID.patch b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch similarity index 69% rename from patches/server/0029-Optimize-CraftServer.getWorld-UUID.patch rename to divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch index 6a53d28..6fbf1cc 100644 --- a/patches/server/0029-Optimize-CraftServer.getWorld-UUID.patch +++ b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/CraftServer.java.patch @@ -1,15 +1,6 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 13 Jan 2024 13:36:55 +0300 -Subject: [PATCH] Optimize CraftServer.getWorld(UUID) - -Original code by MultiPaper - https://github.com/MultiPaper/MultiPaper - -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index eab64b3f812c9ab12793ad200f58ae8c49e214e1..88343cbdaa95f7f2bf6a196b58f23195186749a2 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -285,6 +285,7 @@ public final class CraftServer implements Server { +@@ -286,6 +_,7 @@ protected final DedicatedPlayerList playerList; private final Map worlds = new LinkedHashMap(); // private final Map, Registry> registries = new HashMap<>(); // Paper - replace with RegistryAccess @@ -17,7 +8,16 @@ index eab64b3f812c9ab12793ad200f58ae8c49e214e1..88343cbdaa95f7f2bf6a196b58f23195 private YamlConfiguration configuration; private YamlConfiguration commandsConfiguration; private final Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); -@@ -1519,6 +1520,7 @@ public final class CraftServer implements Server { +@@ -988,7 +_,7 @@ + + @Override + public List getWorlds() { +- return new ArrayList(this.worlds.values()); ++ return new it.unimi.dsi.fastutil.objects.ObjectArrayList(this.worlds.values()); // DivineMC - optimize getWorlds + } + + @Override +@@ -1536,6 +_,7 @@ this.getLogger().log(Level.SEVERE, null, ex); } @@ -25,7 +25,7 @@ index eab64b3f812c9ab12793ad200f58ae8c49e214e1..88343cbdaa95f7f2bf6a196b58f23195 this.worlds.remove(world.getName().toLowerCase(Locale.ROOT)); this.console.removeLevel(handle); return true; -@@ -1537,6 +1539,7 @@ public final class CraftServer implements Server { +@@ -1554,6 +_,7 @@ @Override public World getWorld(UUID uid) { @@ -33,7 +33,7 @@ index eab64b3f812c9ab12793ad200f58ae8c49e214e1..88343cbdaa95f7f2bf6a196b58f23195 for (World world : this.worlds.values()) { if (world.getUID().equals(uid)) { return world; -@@ -1560,6 +1563,7 @@ public final class CraftServer implements Server { +@@ -1577,6 +_,7 @@ System.out.println("World " + world.getName() + " is a duplicate of another world and has been prevented from loading. Please delete the uid.dat file from " + world.getName() + "'s world directory if you want to be able to load the duplicate world."); return; } diff --git a/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java.patch b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java.patch new file mode 100644 index 0000000..fe38f76 --- /dev/null +++ b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java.patch @@ -0,0 +1,14 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftAbstractArrow.java +@@ -185,4 +_,11 @@ + this.getHandle().projectileSource = shooter; + } + // Paper end - Fix PickupStatus getting reset ++ ++ // DivineMC start - Add startFalling method to AbstractArrow ++ @Override ++ public void startFalling() { ++ this.getHandle().startFalling(); ++ } ++ // DivineMC end - Add startFalling method to AbstractArrow + } diff --git a/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch new file mode 100644 index 0000000..03ca598 --- /dev/null +++ b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java.patch @@ -0,0 +1,22 @@ +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -3640,4 +_,19 @@ + this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundPlayerCombatKillPacket(getEntityId(), io.papermc.paper.adventure.PaperAdventure.asVanilla(message))); + } + // Purpur end - Death screen API ++ ++ // DivineMC start - Open Ender Chest API ++ /** ++ * Opens ender chest for the player ++ * ++ * @param enderChest ender chest ++ */ ++ @Override ++ public boolean openEnderChest(@NotNull org.bukkit.block.EnderChest enderChest) { ++ net.minecraft.world.inventory.PlayerEnderChestContainer playerEnderChestContainer = this.getHandle().getEnderChestInventory(); ++ net.minecraft.world.level.block.entity.EnderChestBlockEntity blockEntity = ((org.bukkit.craftbukkit.block.CraftEnderChest) enderChest).getTileEntity(); ++ playerEnderChestContainer.setActiveChest(blockEntity); ++ return this.getHandle().openMenu(new net.minecraft.world.SimpleMenuProvider((i, inventory, playerx) -> org.purpurmc.purpur.PurpurConfig.enderChestSixRows ? net.minecraft.world.level.block.EnderChestBlock.getEnderChestSixRows(i, inventory, this.getHandle(), playerEnderChestContainer) : net.minecraft.world.inventory.ChestMenu.threeRows(i, inventory, playerEnderChestContainer), net.minecraft.world.level.block.EnderChestBlock.CONTAINER_TITLE)).isPresent(); ++ } ++ // DivineMC end - Open Ender Chest API + } diff --git a/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch new file mode 100644 index 0000000..9c2de93 --- /dev/null +++ b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java.patch @@ -0,0 +1,16 @@ +--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java ++++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +@@ -1566,7 +_,12 @@ + } + + public static NotePlayEvent callNotePlayEvent(Level world, BlockPos pos, NoteBlockInstrument instrument, int note) { +- NotePlayEvent event = new NotePlayEvent(world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), org.bukkit.Instrument.getByType((byte) instrument.ordinal()), new org.bukkit.Note(note)); ++ // DivineMC start - Add player to NotePlayEvent ++ return callNotePlayEvent(world, pos, instrument, note, null); ++ } ++ public static NotePlayEvent callNotePlayEvent(Level world, BlockPos pos, NoteBlockInstrument instrument, int note, @Nullable net.minecraft.world.entity.player.Player player) { ++ NotePlayEvent event = new NotePlayEvent(world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), org.bukkit.Instrument.getByType((byte) instrument.ordinal()), new org.bukkit.Note(note), player instanceof ServerPlayer serverPlayer ? serverPlayer.getBukkitEntity() : null); ++ // DivineMC end - Add player to NotePlayEvent + world.getCraftServer().getPluginManager().callEvent(event); + return event; + } diff --git a/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java.patch b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java.patch new file mode 100644 index 0000000..48afc6a --- /dev/null +++ b/divinemc-server/paper-patches/files/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java.patch @@ -0,0 +1,10 @@ +--- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java +@@ -5,6 +_,7 @@ + + public final class CraftDefaultPermissions { + private static final String ROOT = "minecraft"; ++ public static final String DIVINEMC_ROOT = "divinemc"; // DivineMC - permission root for commands + + private CraftDefaultPermissions() {} + diff --git a/divinemc-server/purpur-patches/features/0001-Optimize-default-values-for-configs.patch b/divinemc-server/purpur-patches/features/0001-Optimize-default-values-for-configs.patch new file mode 100644 index 0000000..cd48d9c --- /dev/null +++ b/divinemc-server/purpur-patches/features/0001-Optimize-default-values-for-configs.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sat, 11 Jan 2025 23:22:12 +0300 +Subject: [PATCH] Optimize default values for configs + + +diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java +index 702f71bed6634042bc306fa365ff00b4053d8661..8208a91dd9d15a23b17590f88ab2fefdc0fcfcf3 100644 +--- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java ++++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java +@@ -240,7 +240,7 @@ public class PurpurConfig { + laggingThreshold = getDouble("settings.lagging-threshold", laggingThreshold); + } + +- public static boolean useAlternateKeepAlive = false; ++ public static boolean useAlternateKeepAlive = true; // DivineMC - Optimize default values for configs + private static void useAlternateKeepAlive() { + useAlternateKeepAlive = getBoolean("settings.use-alternate-keepalive", useAlternateKeepAlive); + } diff --git a/divinemc-server/purpur-patches/features/0002-Add-missing-purpur-config-options.patch b/divinemc-server/purpur-patches/features/0002-Add-missing-purpur-config-options.patch new file mode 100644 index 0000000..5a9685d --- /dev/null +++ b/divinemc-server/purpur-patches/features/0002-Add-missing-purpur-config-options.patch @@ -0,0 +1,160 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> +Date: Sun, 12 Jan 2025 00:14:21 +0300 +Subject: [PATCH] Add missing purpur config options + + +diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java +index 8208a91dd9d15a23b17590f88ab2fefdc0fcfcf3..0ead27518fc23943b29f163ed0feb6c61c119468 100644 +--- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java ++++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java +@@ -327,6 +327,7 @@ public class PurpurConfig { + } + + public static int barrelRows = 3; ++ public static int chestBoatRows = 3; // DivineMC - Add missing purpur config options + public static boolean enderChestSixRows = false; + public static boolean enderChestPermissionRows = false; + public static boolean cryingObsidianValidForPortalFrame = false; +@@ -369,6 +370,7 @@ public class PurpurConfig { + case 1 -> 9; + default -> 27; + }); ++ chestBoatRows = getInt("settings.blocks.chest_boat.rows", chestBoatRows); // DivineMC - Add missing purpur config options + enderChestSixRows = getBoolean("settings.blocks.ender_chest.six-rows", enderChestSixRows); + org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27); + enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); +diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java +index 22e793c40f3a784b514905c1d756d4cbdf1fd740..235c851e8dd202690a5eac6537d22d244c012494 100644 +--- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java ++++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java +@@ -1144,12 +1144,20 @@ public class PurpurWorldConfig { + public boolean allayControllable = true; + public double allayMaxHealth = 20.0D; + public double allayScale = 1.0D; ++ // DivineMC start - Add missing purpur config options ++ public boolean allayTakeDamageFromWater = false; ++ public boolean allayAlwaysDropExp = false; ++ // DivineMC end + private void allaySettings() { + allayRidable = getBoolean("mobs.allay.ridable", allayRidable); + allayRidableInWater = getBoolean("mobs.allay.ridable-in-water", allayRidableInWater); + allayControllable = getBoolean("mobs.allay.controllable", allayControllable); + allayMaxHealth = getDouble("mobs.allay.attributes.max_health", allayMaxHealth); + allayScale = Mth.clamp(getDouble("mobs.allay.attributes.scale", allayScale), 0.0625D, 16.0D); ++ // DivineMC start - Add missing purpur config options ++ allayTakeDamageFromWater = getBoolean("mobs.allay.take-damage-from-water", allayTakeDamageFromWater); ++ allayAlwaysDropExp = getBoolean("mobs.allay.always-drop-exp", allayAlwaysDropExp); ++ // DivineMC end - Add missing purpur config options + } + + public boolean armadilloRidable = false; +@@ -1299,6 +1307,10 @@ public class PurpurWorldConfig { + public double camelMovementSpeedMin = 0.09D; + public double camelMovementSpeedMax = 0.09D; + public int camelBreedingTicks = 6000; ++ // DivineMC start - Add missing purpur config options ++ public boolean camelTakeDamageFromWater = false; ++ public boolean camelAlwaysDropExp = false; ++ // DivineMC end - Add missing purpur config options + private void camelSettings() { + camelRidableInWater = getBoolean("mobs.camel.ridable-in-water", camelRidableInWater); + camelMaxHealthMin = getDouble("mobs.camel.attributes.max_health.min", camelMaxHealthMin); +@@ -1308,6 +1320,10 @@ public class PurpurWorldConfig { + camelMovementSpeedMin = getDouble("mobs.camel.attributes.movement_speed.min", camelMovementSpeedMin); + camelMovementSpeedMax = getDouble("mobs.camel.attributes.movement_speed.max", camelMovementSpeedMax); + camelBreedingTicks = getInt("mobs.camel.breeding-delay-ticks", camelBreedingTicks); ++ // DivineMC start - Add missing purpur config options ++ camelTakeDamageFromWater = getBoolean("mobs.camel.takes-damage-from-water", camelTakeDamageFromWater); ++ camelAlwaysDropExp = getBoolean("mobs.camel.always-drop-exp", camelAlwaysDropExp); ++ // DivineMC end - Add missing purpur config options + } + + public boolean catRidable = false; +@@ -1764,12 +1780,22 @@ public class PurpurWorldConfig { + public boolean frogControllable = true; + public float frogRidableJumpHeight = 0.65F; + public int frogBreedingTicks = 6000; ++ // DivineMC start - Add missing purpur config options ++ public double frogMaxHealth = 10.0D; ++ public boolean frogTakeDamageFromWater = false; ++ public boolean frogAlwaysDropExp = false; ++ // DivineMC end - Add missing purpur config options + private void frogSettings() { + frogRidable = getBoolean("mobs.frog.ridable", frogRidable); + frogRidableInWater = getBoolean("mobs.frog.ridable-in-water", frogRidableInWater); + frogControllable = getBoolean("mobs.frog.controllable", frogControllable); + frogRidableJumpHeight = (float) getDouble("mobs.frog.ridable-jump-height", frogRidableJumpHeight); + frogBreedingTicks = getInt("mobs.frog.breeding-delay-ticks", frogBreedingTicks); ++ // DivineMC start - Add missing purpur config options ++ frogMaxHealth = getDouble("mobs.frog.attributes.max_health", frogMaxHealth); ++ frogTakeDamageFromWater = getBoolean("mobs.frog.takes-damage-from-water", frogTakeDamageFromWater); ++ frogAlwaysDropExp = getBoolean("mobs.frog.always-drop-exp", frogAlwaysDropExp); ++ // DivineMC end + } + + public boolean ghastRidable = false; +@@ -2776,6 +2802,10 @@ public class PurpurWorldConfig { + public double snifferMaxHealth = 14.0D; + public double snifferScale = 1.0D; + public int snifferBreedingTicks = 6000; ++ // DivineMC start - Add missing purpur config options ++ public boolean snifferTakeDamageFromWater = false; ++ public boolean snifferAlwaysDropExp = false; ++ // DivineMC end - Add missing purpur config options + private void snifferSettings() { + snifferRidable = getBoolean("mobs.sniffer.ridable", snifferRidable); + snifferRidableInWater = getBoolean("mobs.sniffer.ridable-in-water", snifferRidableInWater); +@@ -2783,6 +2813,10 @@ public class PurpurWorldConfig { + snifferMaxHealth = getDouble("mobs.sniffer.attributes.max_health", snifferMaxHealth); + snifferScale = Mth.clamp(getDouble("mobs.sniffer.attributes.scale", snifferScale), 0.0625D, 16.0D); + snifferBreedingTicks = getInt("mobs.sniffer.breeding-delay-ticks", snifferBreedingTicks); ++ // DivineMC start - Add missing purpur config options ++ snifferTakeDamageFromWater = getBoolean("mobs.sniffer.takes-damage-from-water", snifferTakeDamageFromWater); ++ snifferAlwaysDropExp = getBoolean("mobs.sniffer.always-drop-exp", snifferAlwaysDropExp); ++ // DivineMC end - Add missing purpur config options + } + + public boolean squidRidable = false; +@@ -2884,10 +2918,20 @@ public class PurpurWorldConfig { + public boolean tadpoleRidable = false; + public boolean tadpoleRidableInWater = true; + public boolean tadpoleControllable = true; ++ // DivineMC start - Add missing purpur config options ++ public double tadpoleMaxHealth = 10.0D; ++ public boolean tadpoleTakeDamageFromWater = false; ++ public boolean tadpoleAlwaysDropExp = false; ++ // DivineMC end - Add missing purpur config options + private void tadpoleSettings() { + tadpoleRidable = getBoolean("mobs.tadpole.ridable", tadpoleRidable); + tadpoleRidableInWater = getBoolean("mobs.tadpole.ridable-in-water", tadpoleRidableInWater); + tadpoleControllable = getBoolean("mobs.tadpole.controllable", tadpoleControllable); ++ // DivineMC start - Add missing purpur config options ++ tadpoleMaxHealth = getDouble("mobs.tadpole.attributes.max_health", tadpoleMaxHealth); ++ tadpoleTakeDamageFromWater = getBoolean("mobs.tadpole.takes-damage-from-water", tadpoleTakeDamageFromWater); ++ tadpoleAlwaysDropExp = getBoolean("mobs.tadpole.always-drop-exp", tadpoleAlwaysDropExp); ++ // DivineMC end - Add missing purpur config options + } + + public boolean traderLlamaRidable = false; +@@ -3116,10 +3160,20 @@ public class PurpurWorldConfig { + public boolean wardenRidable = false; + public boolean wardenRidableInWater = true; + public boolean wardenControllable = true; ++ // DivineMC start - Add missing purpur config options ++ public double wardenMaxHealth = 500.0D; ++ public boolean wardenTakeDamageFromWater = false; ++ public boolean wardenAlwaysDropExp = false; ++ // DivineMC end - Add missing purpur config options + private void wardenSettings() { + wardenRidable = getBoolean("mobs.warden.ridable", wardenRidable); + wardenRidableInWater = getBoolean("mobs.warden.ridable-in-water", wardenRidableInWater); + wardenControllable = getBoolean("mobs.warden.controllable", wardenControllable); ++ // DivineMC start - Add missing purpur config options ++ wardenMaxHealth = getDouble("mobs.warden.attributes.max_health", wardenMaxHealth); ++ wardenTakeDamageFromWater = getBoolean("mobs.warden.takes-damage-from-water", wardenTakeDamageFromWater); ++ wardenAlwaysDropExp = getBoolean("mobs.warden.always-drop-exp", wardenAlwaysDropExp); ++ // DivineMC end - Add missing purpur config options + } + + public boolean witchRidable = false; diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommand.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommand.java new file mode 100644 index 0000000..2c005a5 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommand.java @@ -0,0 +1,160 @@ +package space.bxteam.divinemc.command; + +import io.papermc.paper.command.CommandUtil; +import it.unimi.dsi.fastutil.Pair; +import net.minecraft.Util; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.Nullable; +import space.bxteam.divinemc.command.subcommands.*; + +import java.util.*; +import java.util.stream.Collectors; + +import static net.kyori.adventure.text.Component.newline; +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.format.NamedTextColor.GRAY; +import static net.kyori.adventure.text.format.NamedTextColor.RED; + +public final class DivineCommand extends Command { + public static final String COMMAND_LABEL = "divinemc"; + public static final String BASE_PERM = DivineCommands.COMMAND_BASE_PERM + "." + COMMAND_LABEL; + private static final Permission basePermission = new Permission(BASE_PERM, PermissionDefault.TRUE); + + private static final DivineSubCommand RELOAD_SUBCOMMAND = new ReloadCommand(); + private static final DivineSubCommand VERSION_SUBCOMMAND = new VersionCommand(); + private static final Map SUBCOMMANDS = Util.make(() -> { + final Map, DivineSubCommand> commands = new HashMap<>(); + + commands.put(Set.of(ReloadCommand.LITERAL_ARGUMENT), RELOAD_SUBCOMMAND); + commands.put(Set.of(VersionCommand.LITERAL_ARGUMENT), VERSION_SUBCOMMAND); + + return commands.entrySet().stream() + .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + }); + // alias -> subcommand label + private static final Map ALIASES = Util.make(() -> { + final Map> aliases = new HashMap<>(); + + aliases.put(VersionCommand.LITERAL_ARGUMENT, Set.of("ver")); + + return aliases.entrySet().stream() + .flatMap(entry -> entry.getValue().stream().map(s -> Map.entry(s, entry.getKey()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + }); + + private String createUsageMessage(Collection arguments) { + return "/" + COMMAND_LABEL + " [" + String.join(" | ", arguments) + "]"; + } + + public DivineCommand() { + super(COMMAND_LABEL); + this.description = "DivineMC related commands"; + this.usageMessage = this.createUsageMessage(SUBCOMMANDS.keySet()); + final List permissions = SUBCOMMANDS.values().stream().map(DivineSubCommand::getPermission).filter(Objects::nonNull).toList(); + this.setPermission(BASE_PERM); + final PluginManager pluginManager = Bukkit.getServer().getPluginManager(); + pluginManager.addPermission(basePermission); + for (final Permission permission : permissions) { + pluginManager.addPermission(permission); + } + } + + @Override + public List tabComplete( + final CommandSender sender, + final String alias, + final String[] args, + final @Nullable Location location + ) throws IllegalArgumentException { + if (args.length <= 1) { + List subCommandArguments = new ArrayList<>(SUBCOMMANDS.size()); + for (Map.Entry subCommandEntry : SUBCOMMANDS.entrySet()) { + if (subCommandEntry.getValue().testPermission(sender)) { + subCommandArguments.add(subCommandEntry.getKey()); + } + } + return CommandUtil.getListMatchingLast(sender, args, subCommandArguments); + } + + final @Nullable Pair subCommand = resolveCommand(args[0]); + if (subCommand != null && subCommand.second().testPermission(sender)) { + return subCommand.second().tabComplete(sender, subCommand.first(), Arrays.copyOfRange(args, 1, args.length)); + } + + return Collections.emptyList(); + } + + private boolean testHasOnePermission(CommandSender sender) { + for (Map.Entry subCommandEntry : SUBCOMMANDS.entrySet()) { + if (subCommandEntry.getValue().testPermission(sender)) { + return true; + } + } + return false; + } + + @Override + public boolean execute( + final CommandSender sender, + final String commandLabel, + final String[] args + ) { + + // Check if the sender has the base permission and at least one specific permission + if (!sender.hasPermission(basePermission) || !this.testHasOnePermission(sender)) { + sender.sendMessage(Bukkit.permissionMessage()); + return true; + } + + // Determine the usage message with the subcommands they can perform + List subCommandArguments = new ArrayList<>(SUBCOMMANDS.size()); + for (Map.Entry subCommandEntry : SUBCOMMANDS.entrySet()) { + if (subCommandEntry.getValue().testPermission(sender)) { + subCommandArguments.add(subCommandEntry.getKey()); + } + } + String specificUsageMessage = this.createUsageMessage(subCommandArguments); + + // If they did not give a subcommand + if (args.length == 0) { + sender.sendMessage(newline().append(text("Command usage: " + specificUsageMessage, GRAY))); + return false; + } + + // If they do not have permission for the subcommand they gave, or the argument is not a valid subcommand + final @Nullable Pair subCommand = resolveCommand(args[0]); + if (subCommand == null || !subCommand.second().testPermission(sender)) { + sender.sendMessage(text("Usage: " + specificUsageMessage, RED)); + return false; + } + + // Execute the subcommand + final String[] choppedArgs = Arrays.copyOfRange(args, 1, args.length); + return subCommand.second().execute(sender, subCommand.first(), choppedArgs); + } + + private static @Nullable Pair resolveCommand(String label) { + label = label.toLowerCase(Locale.ENGLISH); + @Nullable DivineSubCommand subCommand = SUBCOMMANDS.get(label); + if (subCommand == null) { + final @Nullable String command = ALIASES.get(label); + if (command != null) { + label = command; + subCommand = SUBCOMMANDS.get(command); + } + } + + if (subCommand != null) { + return Pair.of(label, subCommand); + } + + return null; + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommands.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommands.java new file mode 100644 index 0000000..4dd92fd --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineCommands.java @@ -0,0 +1,28 @@ +package space.bxteam.divinemc.command; + +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.bukkit.craftbukkit.util.permissions.CraftDefaultPermissions; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +import java.util.HashMap; +import java.util.Map; + +@DefaultQualifier(NonNull.class) +public final class DivineCommands { + public static final String COMMAND_BASE_PERM = CraftDefaultPermissions.DIVINEMC_ROOT + ".command"; + + private DivineCommands() {} + + private static final Map COMMANDS = new HashMap<>(); + static { + COMMANDS.put(DivineCommand.COMMAND_LABEL, new DivineCommand()); + } + + public static void registerCommands(final MinecraftServer server) { + COMMANDS.forEach((s, command) -> { + server.server.getCommandMap().register(s, "DivineMC", command); + }); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommand.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommand.java new file mode 100644 index 0000000..750fd89 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommand.java @@ -0,0 +1,23 @@ +package space.bxteam.divinemc.command; + +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +@DefaultQualifier(NonNull.class) +public interface DivineSubCommand { + boolean execute(CommandSender sender, String subCommand, String[] args); + + default List tabComplete(final CommandSender sender, final String subCommand, final String[] args) { + return Collections.emptyList(); + } + + boolean testPermission(CommandSender sender); + + @Nullable Permission getPermission(); +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommandPermission.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommandPermission.java new file mode 100644 index 0000000..152ae2b --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/DivineSubCommandPermission.java @@ -0,0 +1,28 @@ +package space.bxteam.divinemc.command; + +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.jetbrains.annotations.Nullable; + +public abstract class DivineSubCommandPermission implements DivineSubCommand { + public final Permission permission; + + protected DivineSubCommandPermission(Permission permission) { + this.permission = permission; + } + + protected DivineSubCommandPermission(String permission, PermissionDefault permissionDefault) { + this(new Permission(permission, permissionDefault)); + } + + @Override + public boolean testPermission(CommandSender sender) { + return sender.hasPermission(this.permission); + } + + @Override + public @Nullable Permission getPermission() { + return this.permission; + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/ReloadCommand.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/ReloadCommand.java new file mode 100644 index 0000000..d028307 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/ReloadCommand.java @@ -0,0 +1,47 @@ +package space.bxteam.divinemc.command.subcommands; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.permissions.PermissionDefault; +import space.bxteam.divinemc.command.DivineCommand; +import space.bxteam.divinemc.command.DivineSubCommandPermission; +import space.bxteam.divinemc.configuration.DivineConfig; + +import java.io.File; + +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.format.NamedTextColor.GREEN; +import static net.kyori.adventure.text.format.NamedTextColor.RED; + +public final class ReloadCommand extends DivineSubCommandPermission { + public final static String LITERAL_ARGUMENT = "reload"; + public static final String PERM = DivineCommand.BASE_PERM + "." + LITERAL_ARGUMENT; + + public ReloadCommand() { + super(PERM, PermissionDefault.OP); + } + + @Override + public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { + this.doReload(sender); + return true; + } + + private void doReload(final CommandSender sender) { + Command.broadcastCommandMessage(sender, text("Please note that this command is not supported and may cause issues.", RED)); + Command.broadcastCommandMessage(sender, text("If you encounter any issues please use the /stop command to restart your server.", RED)); + + MinecraftServer server = ((CraftServer) sender.getServer()).getServer(); + DivineConfig.init((File) server.options.valueOf("divinemc-settings")); + for (ServerLevel level : server.getAllLevels()) { + level.divinemcConfig.init(); + level.resetBreedingCooldowns(); + } + server.server.reloadCount++; + + Command.broadcastCommandMessage(sender, text("DivineMC config reload complete.", GREEN)); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/VersionCommand.java b/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/VersionCommand.java new file mode 100644 index 0000000..fe93249 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/command/subcommands/VersionCommand.java @@ -0,0 +1,35 @@ +package space.bxteam.divinemc.command.subcommands; + +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.PermissionDefault; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.jetbrains.annotations.Nullable; +import space.bxteam.divinemc.command.DivineCommand; +import space.bxteam.divinemc.command.DivineSubCommandPermission; + +@DefaultQualifier(NonNull.class) +public final class VersionCommand extends DivineSubCommandPermission { + public final static String LITERAL_ARGUMENT = "version"; + public static final String PERM = DivineCommand.BASE_PERM + "." + LITERAL_ARGUMENT; + + public VersionCommand() { + super(PERM, PermissionDefault.TRUE); + } + + @Override + public boolean execute(final CommandSender sender, final String subCommand, final String[] args) { + final @Nullable Command ver = MinecraftServer.getServer().server.getCommandMap().getCommand("version"); + if (ver != null) { + ver.execute(sender, DivineCommand.COMMAND_LABEL, new String[0]); + } + return true; + } + + @Override + public boolean testPermission(CommandSender sender) { + return super.testPermission(sender) && sender.hasPermission("bukkit.command.version"); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/divinemc-server/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java new file mode 100644 index 0000000..6e19675 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java @@ -0,0 +1,210 @@ +package space.bxteam.divinemc.configuration; + +import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableMap; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; + +@SuppressWarnings("unused") +public class DivineConfig { + private static final String HEADER = "This is the main configuration file for DivineMC.\n" + + "If you need help with the configuration or have any questions related to DivineMC,\n" + + "join us in our Discord server.\n" + + "\n" + + "Discord: https://discord.gg/p7cxhw7E2M \n" + + "Docs: https://docs.bx-team.space/documentation/divinemc/about \n" + + "New builds: https://github.com/DivineMC/DivineMC/releases/latest"; + private static File CONFIG_FILE; + public static YamlConfiguration config; + + private static Map commands; + + public static int version; + static boolean verbose; + + public static void init(File configFile) { + CONFIG_FILE = configFile; + config = new YamlConfiguration(); + try { + config.load(CONFIG_FILE); + } catch (IOException ignore) { + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Could not load divinemc.yml, please correct your syntax errors", ex); + throw Throwables.propagate(ex); + } + config.options().header(HEADER); + config.options().copyDefaults(true); + verbose = getBoolean("verbose", false); + + version = getInt("config-version", 4); + set("config-version", 4); + + readConfig(DivineConfig.class, null); + + Block.BLOCK_STATE_REGISTRY.forEach(BlockBehaviour.BlockStateBase::initCache); + } + + protected static void log(String s) { + if (verbose) { + log(Level.INFO, s); + } + } + + protected static void log(Level level, String s) { + Bukkit.getLogger().log(level, s); + } + + static void readConfig(Class clazz, Object instance) { + for (Method method : clazz.getDeclaredMethods()) { + if (Modifier.isPrivate(method.getModifiers())) { + if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { + try { + method.setAccessible(true); + method.invoke(instance); + } catch (InvocationTargetException ex) { + throw Throwables.propagate(ex.getCause()); + } catch (Exception ex) { + Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex); + } + } + } + } + + try { + config.save(CONFIG_FILE); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex); + } + } + + private static void set(String path, Object val) { + config.addDefault(path, val); + config.set(path, val); + } + + private static String getString(String path, String def) { + config.addDefault(path, def); + return config.getString(path, config.getString(path)); + } + + private static boolean getBoolean(String path, boolean def) { + config.addDefault(path, def); + return config.getBoolean(path, config.getBoolean(path)); + } + + private static double getDouble(String path, double def) { + config.addDefault(path, def); + return config.getDouble(path, config.getDouble(path)); + } + + private static int getInt(String path, int def) { + config.addDefault(path, def); + return config.getInt(path, config.getInt(path)); + } + + private static List getList(String path, T def) { + config.addDefault(path, def); + return config.getList(path, config.getList(path)); + } + + static Map getMap(String path, Map def) { + if (def != null && config.getConfigurationSection(path) == null) { + config.addDefault(path, def); + return def; + } + return toMap(config.getConfigurationSection(path)); + } + + private static Map toMap(ConfigurationSection section) { + ImmutableMap.Builder builder = ImmutableMap.builder(); + if (section != null) { + for (String key : section.getKeys(false)) { + Object obj = section.get(key); + if (obj != null) { + builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj); + } + } + } + return builder.build(); + } + + public static boolean noChatSign = true; + private static void chatMessageSignatures() { + noChatSign = getBoolean("settings.no-chat-sign", noChatSign); + } + + public static boolean optimizedDragonRespawn = true; + private static void optimizations() { + optimizedDragonRespawn = getBoolean("settings.optimizations.optimized-dragon-respawn", optimizedDragonRespawn); + } + + public static boolean disableNonEditableSignWarning = true; + public static boolean removeVanillaUsernameCheck = false; + public static boolean disableMovedWronglyThreshold = false; + public static boolean enableSecureSeed = false; + private static void miscSettings() { + disableNonEditableSignWarning = getBoolean("settings.misc.disable-non-editable-sign-warning", disableNonEditableSignWarning); + removeVanillaUsernameCheck = getBoolean("settings.misc.remove-vanilla-username-check", removeVanillaUsernameCheck); + disableMovedWronglyThreshold = getBoolean("settings.misc.disable-moved-wrongly-threshold", disableMovedWronglyThreshold); + enableSecureSeed = getBoolean("settings.misc.enable-secure-seed", enableSecureSeed); + } + + public static int linearFlushFrequency = 5; + public static boolean throwOnUnknownExtension = false; + private static void linearSettings() { + linearFlushFrequency = getInt("settings.region-format.linear.flush-frequency", linearFlushFrequency); + throwOnUnknownExtension = getBoolean("settings.region-format.linear.throw-on-unknown-extension", throwOnUnknownExtension); + } + + public static boolean asyncPathfinding = true; + public static int asyncPathfindingMaxThreads = 0; + public static int asyncPathfindingKeepalive = 60; + private static void asyncPathfinding() { + asyncPathfinding = getBoolean("settings.async-pathfinding.enable", asyncPathfinding); + asyncPathfindingMaxThreads = getInt("settings.async-pathfinding.max-threads", asyncPathfindingMaxThreads); + asyncPathfindingKeepalive = getInt("settings.async-pathfinding.keepalive", asyncPathfindingKeepalive); + if (asyncPathfindingMaxThreads < 0) + asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncPathfindingMaxThreads, 1); + else if (asyncPathfindingMaxThreads == 0) + asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1); + if (!asyncPathfinding) + asyncPathfindingMaxThreads = 0; + else + Bukkit.getLogger().log(Level.INFO, "Using " + asyncPathfindingMaxThreads + " threads for Async Pathfinding"); + } + + public static boolean multithreadedEnabled = false; + public static boolean multithreadedCompatModeEnabled = false; + public static int asyncEntityTrackerMaxThreads = 0; + public static int asyncEntityTrackerKeepalive = 60; + private static void multithreadedTracker() { + multithreadedEnabled = getBoolean("settings.multithreaded-tracker.enable", multithreadedEnabled); + multithreadedCompatModeEnabled = getBoolean("settings.multithreaded-tracker.compat-mode", multithreadedCompatModeEnabled); + asyncEntityTrackerMaxThreads = getInt("settings.multithreaded-tracker.max-threads", asyncEntityTrackerMaxThreads); + asyncEntityTrackerKeepalive = getInt("settings.multithreaded-tracker.keepalive", asyncEntityTrackerKeepalive); + + if (asyncEntityTrackerMaxThreads < 0) + asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncEntityTrackerMaxThreads, 1); + else if (asyncEntityTrackerMaxThreads == 0) + asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1); + + if (!multithreadedEnabled) + asyncEntityTrackerMaxThreads = 0; + else + Bukkit.getLogger().log(Level.INFO, "Using " + asyncEntityTrackerMaxThreads + " threads for Async Entity Tracker"); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java b/divinemc-server/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java new file mode 100644 index 0000000..db3f466 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java @@ -0,0 +1,121 @@ +package space.bxteam.divinemc.configuration; + +import org.apache.commons.lang.BooleanUtils; +import org.bukkit.World; +import org.bukkit.configuration.ConfigurationSection; +import space.bxteam.divinemc.region.RegionFileFormat; + +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; +import java.util.logging.Level; + +import static space.bxteam.divinemc.configuration.DivineConfig.log; + +@SuppressWarnings("unused") +public class DivineWorldConfig { + private final String worldName; + private final World.Environment environment; + + public DivineWorldConfig(String worldName, World.Environment environment) { + this.worldName = worldName; + this.environment = environment; + init(); + } + + public void init() { + log("-------- World Settings For [" + worldName + "] --------"); + DivineConfig.readConfig(DivineWorldConfig.class, this); + } + + private void set(String path, Object val) { + DivineConfig.config.addDefault("world-settings.default." + path, val); + DivineConfig.config.set("world-settings.default." + path, val); + if (DivineConfig.config.get("world-settings." + worldName + "." + path) != null) { + DivineConfig.config.addDefault("world-settings." + worldName + "." + path, val); + DivineConfig.config.set("world-settings." + worldName + "." + path, val); + } + } + + private ConfigurationSection getConfigurationSection(String path) { + ConfigurationSection section = DivineConfig.config.getConfigurationSection("world-settings." + worldName + "." + path); + return section != null ? section : DivineConfig.config.getConfigurationSection("world-settings.default." + path); + } + + private String getString(String path, String def) { + DivineConfig.config.addDefault("world-settings.default." + path, def); + return DivineConfig.config.getString("world-settings." + worldName + "." + path, DivineConfig.config.getString("world-settings.default." + path)); + } + + private boolean getBoolean(String path, boolean def) { + DivineConfig.config.addDefault("world-settings.default." + path, def); + return DivineConfig.config.getBoolean("world-settings." + worldName + "." + path, DivineConfig.config.getBoolean("world-settings.default." + path)); + } + + private boolean getBoolean(String path, Predicate predicate) { + String val = getString(path, "default").toLowerCase(); + Boolean bool = BooleanUtils.toBooleanObject(val, "true", "false", "default"); + return predicate.test(bool); + } + + private double getDouble(String path, double def) { + DivineConfig.config.addDefault("world-settings.default." + path, def); + return DivineConfig.config.getDouble("world-settings." + worldName + "." + path, DivineConfig.config.getDouble("world-settings.default." + path)); + } + + private int getInt(String path, int def) { + DivineConfig.config.addDefault("world-settings.default." + path, def); + return DivineConfig.config.getInt("world-settings." + worldName + "." + path, DivineConfig.config.getInt("world-settings.default." + path)); + } + + private List getList(String path, T def) { + DivineConfig.config.addDefault("world-settings.default." + path, def); + return DivineConfig.config.getList("world-settings." + worldName + "." + path, DivineConfig.config.getList("world-settings.default." + path)); + } + + private Map getMap(String path, Map def) { + final Map fallback = DivineConfig.getMap("world-settings.default." + path, def); + final Map value = DivineConfig.getMap("world-settings." + worldName + "." + path, null); + return value.isEmpty() ? fallback : value; + } + + public boolean despawnShulkerBulletsOnOwnerDeath = true; + private void despawnShulkerBulletsOnOwnerDeath() { + despawnShulkerBulletsOnOwnerDeath = getBoolean("gameplay-mechanics.mob.shulker.despawn-bullets-on-player-death", despawnShulkerBulletsOnOwnerDeath); + } + + public boolean saveFireworks = false; + private void projectiles() { + saveFireworks = getBoolean("gameplay-mechanics.should-save-fireworks", saveFireworks); + } + + public boolean suppressErrorsFromDirtyAttributes = true; + private void suppressErrorsFromDirtyAttributes() { + suppressErrorsFromDirtyAttributes = getBoolean("suppress-errors-from-dirty-attributes", suppressErrorsFromDirtyAttributes); + } + + public boolean snowballCanKnockback = true; + public boolean eggCanKnockback = true; + private void setSnowballAndEggKnockback() { + snowballCanKnockback = getBoolean("gameplay-mechanics.projectiles.snowball.knockback", snowballCanKnockback); + eggCanKnockback = getBoolean("gameplay-mechanics.projectiles.egg.knockback", eggCanKnockback); + } + + public RegionFileFormat regionFormatName = RegionFileFormat.MCA; + public int linearCompressionLevel = 1; + private void regionFormatSettings() { + regionFormatName = RegionFileFormat.fromExtension(getString("region-format.format", regionFormatName.name())); + if (regionFormatName.equals(RegionFileFormat.UNKNOWN)) { + log(Level.SEVERE, "Unknown region file type!"); + log(Level.SEVERE, "Falling back to ANVIL region file format."); + regionFormatName = RegionFileFormat.MCA; + } + + linearCompressionLevel = getInt("region-format.linear.compression-level", linearCompressionLevel); + if (linearCompressionLevel > 23 || linearCompressionLevel < 1) { + log(Level.SEVERE, "Linear region compression level should be between 1 and 22 in config: " + linearCompressionLevel); + log(Level.SEVERE, "Falling back to compression level 1."); + linearCompressionLevel = 1; + } + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPath.java b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPath.java new file mode 100644 index 0000000..2067234 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPath.java @@ -0,0 +1,284 @@ +package space.bxteam.divinemc.pathfinding; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.pathfinder.Node; +import net.minecraft.world.level.pathfinder.Path; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +public class AsyncPath extends Path { + /** + * marks whether this async path has been processed + */ + private volatile PathProcessState processState = PathProcessState.WAITING; + + /** + * runnables waiting for this to be processed + */ + private final List postProcessing = new ArrayList<>(0); + + /** + * a list of positions that this path could path towards + */ + private final Set positions; + + /** + * the supplier of the real processed path + */ + private final Supplier pathSupplier; + + /* + * Processed values + */ + + /** + * this is a reference to the nodes list in the parent `Path` object + */ + private final List nodes; + /** + * the block we're trying to path to + *

+ * while processing, we have no idea where this is so consumers of `Path` should check that the path is processed before checking the target block + */ + private @Nullable BlockPos target; + /** + * how far we are to the target + *

+ * while processing, the target could be anywhere but theoretically we're always "close" to a theoretical target so default is 0 + */ + private float distToTarget = 0; + /** + * whether we can reach the target + *

+ * while processing, we can always theoretically reach the target so default is true + */ + private boolean canReach = true; + + public AsyncPath(@NotNull List emptyNodeList, @NotNull Set positions, @NotNull Supplier pathSupplier) { + //noinspection ConstantConditions + super(emptyNodeList, null, false); + + this.nodes = emptyNodeList; + this.positions = positions; + this.pathSupplier = pathSupplier; + + AsyncPathProcessor.queue(this); + } + + @Override + public boolean isProcessed() { + return this.processState == PathProcessState.COMPLETED; + } + + /** + * returns the future representing the processing state of this path + */ + public synchronized void postProcessing(@NotNull Runnable runnable) { + if (isProcessed()) { + runnable.run(); + } else { + this.postProcessing.add(runnable); + } + } + + /** + * an easy way to check if this processing path is the same as an attempted new path + * + * @param positions - the positions to compare against + * @return true if we are processing the same positions + */ + public boolean hasSameProcessingPositions(final Set positions) { + if (this.positions.size() != positions.size()) { + return false; + } + + return this.positions.containsAll(positions); + } + + /** + * starts processing this path + */ + public synchronized void process() { + if (this.processState == PathProcessState.COMPLETED || + this.processState == PathProcessState.PROCESSING) { + return; + } + + processState = PathProcessState.PROCESSING; + + final Path bestPath = this.pathSupplier.get(); + + this.nodes.addAll(bestPath.nodes); // we mutate this list to reuse the logic in Path + this.target = bestPath.getTarget(); + this.distToTarget = bestPath.getDistToTarget(); + this.canReach = bestPath.canReach(); + + processState = PathProcessState.COMPLETED; + + for (Runnable runnable : this.postProcessing) { + runnable.run(); + } // Run tasks after processing + } + + /** + * if this path is accessed while it hasn't processed, just process it in-place + */ + private void checkProcessed() { + if (this.processState == PathProcessState.WAITING || + this.processState == PathProcessState.PROCESSING) { // Block if we are on processing + this.process(); + } + } + + /* + * overrides we need for final fields that we cannot modify after processing + */ + + @Override + public @NotNull BlockPos getTarget() { + this.checkProcessed(); + + return this.target; + } + + @Override + public float getDistToTarget() { + this.checkProcessed(); + + return this.distToTarget; + } + + @Override + public boolean canReach() { + this.checkProcessed(); + + return this.canReach; + } + + /* + * overrides to ensure we're processed first + */ + + @Override + public boolean isDone() { + return this.processState == PathProcessState.COMPLETED && super.isDone(); + } + + @Override + public void advance() { + this.checkProcessed(); + + super.advance(); + } + + @Override + public boolean notStarted() { + this.checkProcessed(); + + return super.notStarted(); + } + + @Nullable + @Override + public Node getEndNode() { + this.checkProcessed(); + + return super.getEndNode(); + } + + @Override + public Node getNode(int index) { + this.checkProcessed(); + + return super.getNode(index); + } + + @Override + public void truncateNodes(int length) { + this.checkProcessed(); + + super.truncateNodes(length); + } + + @Override + public void replaceNode(int index, Node node) { + this.checkProcessed(); + + super.replaceNode(index, node); + } + + @Override + public int getNodeCount() { + this.checkProcessed(); + + return super.getNodeCount(); + } + + @Override + public int getNextNodeIndex() { + this.checkProcessed(); + + return super.getNextNodeIndex(); + } + + @Override + public void setNextNodeIndex(int nodeIndex) { + this.checkProcessed(); + + super.setNextNodeIndex(nodeIndex); + } + + @Override + public Vec3 getEntityPosAtNode(Entity entity, int index) { + this.checkProcessed(); + + return super.getEntityPosAtNode(entity, index); + } + + @Override + public BlockPos getNodePos(int index) { + this.checkProcessed(); + + return super.getNodePos(index); + } + + @Override + public Vec3 getNextEntityPos(Entity entity) { + this.checkProcessed(); + + return super.getNextEntityPos(entity); + } + + @Override + public BlockPos getNextNodePos() { + this.checkProcessed(); + + return super.getNextNodePos(); + } + + @Override + public Node getNextNode() { + this.checkProcessed(); + + return super.getNextNode(); + } + + @Nullable + @Override + public Node getPreviousNode() { + this.checkProcessed(); + + return super.getPreviousNode(); + } + + public PathProcessState getProcessState() { + return processState; + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPathProcessor.java b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPathProcessor.java new file mode 100644 index 0000000..b3222ad --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPathProcessor.java @@ -0,0 +1,48 @@ +package space.bxteam.divinemc.pathfinding; + +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.pathfinder.Path; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.*; +import java.util.function.Consumer; + +/** + * used to handle the scheduling of async path processing + */ +public class AsyncPathProcessor { + private static final Executor pathProcessingExecutor = new ThreadPoolExecutor( + 1, + space.bxteam.divinemc.configuration.DivineConfig.asyncPathfindingMaxThreads, + space.bxteam.divinemc.configuration.DivineConfig.asyncPathfindingKeepalive, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + new ThreadFactoryBuilder() + .setNameFormat("DivineMC Async Pathfinding Thread - %d") + .setPriority(Thread.NORM_PRIORITY - 2) + .build() + ); + + protected static CompletableFuture queue(@NotNull AsyncPath path) { + return CompletableFuture.runAsync(path::process, pathProcessingExecutor); + } + + /** + * takes a possibly unprocessed path, and waits until it is completed + * the consumer will be immediately invoked if the path is already processed + * the consumer will always be called on the main thread + * + * @param path a path to wait on + * @param afterProcessing a consumer to be called + */ + public static void awaitProcessing(@Nullable Path path, Consumer<@Nullable Path> afterProcessing) { + if (path != null && !path.isProcessed() && path instanceof AsyncPath asyncPath) { + asyncPath.postProcessing(() -> + MinecraftServer.getServer().scheduleOnMain(() -> afterProcessing.accept(path)) + ); + } else { + afterProcessing.accept(path); + } + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorCache.java b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorCache.java new file mode 100644 index 0000000..11d4c50 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorCache.java @@ -0,0 +1,44 @@ +package space.bxteam.divinemc.pathfinding; + +import net.minecraft.world.level.pathfinder.NodeEvaluator; +import org.apache.commons.lang.Validate; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; + +public class NodeEvaluatorCache { + private static final Map> threadLocalNodeEvaluators = new ConcurrentHashMap<>(); + private static final Map nodeEvaluatorToGenerator = new ConcurrentHashMap<>(); + + private static @NotNull Queue getQueueForFeatures(@NotNull NodeEvaluatorFeatures nodeEvaluatorFeatures) { + return threadLocalNodeEvaluators.computeIfAbsent(nodeEvaluatorFeatures, key -> new ConcurrentLinkedQueue<>()); + } + + public static @NotNull NodeEvaluator takeNodeEvaluator(@NotNull NodeEvaluatorGenerator generator, @NotNull NodeEvaluator localNodeEvaluator) { + final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(localNodeEvaluator); + NodeEvaluator nodeEvaluator = getQueueForFeatures(nodeEvaluatorFeatures).poll(); + + if (nodeEvaluator == null) { + nodeEvaluator = generator.generate(nodeEvaluatorFeatures); + } + + nodeEvaluatorToGenerator.put(nodeEvaluator, generator); + + return nodeEvaluator; + } + + public static void returnNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) { + final NodeEvaluatorGenerator generator = nodeEvaluatorToGenerator.remove(nodeEvaluator); + Validate.notNull(generator, "NodeEvaluator already returned"); + + final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(nodeEvaluator); + getQueueForFeatures(nodeEvaluatorFeatures).offer(nodeEvaluator); + } + + public static void removeNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) { + nodeEvaluatorToGenerator.remove(nodeEvaluator); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorFeatures.java b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorFeatures.java new file mode 100644 index 0000000..e1aeb6b --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorFeatures.java @@ -0,0 +1,23 @@ +package space.bxteam.divinemc.pathfinding; + +import net.minecraft.world.level.pathfinder.NodeEvaluator; +import net.minecraft.world.level.pathfinder.SwimNodeEvaluator; + +public record NodeEvaluatorFeatures( + NodeEvaluatorType type, + boolean canPassDoors, + boolean canFloat, + boolean canWalkOverFences, + boolean canOpenDoors, + boolean allowBreaching +) { + public static NodeEvaluatorFeatures fromNodeEvaluator(NodeEvaluator nodeEvaluator) { + NodeEvaluatorType type = NodeEvaluatorType.fromNodeEvaluator(nodeEvaluator); + boolean canPassDoors = nodeEvaluator.canPassDoors(); + boolean canFloat = nodeEvaluator.canFloat(); + boolean canWalkOverFences = nodeEvaluator.canWalkOverFences(); + boolean canOpenDoors = nodeEvaluator.canOpenDoors(); + boolean allowBreaching = nodeEvaluator instanceof SwimNodeEvaluator swimNodeEvaluator && swimNodeEvaluator.allowBreaching; + return new NodeEvaluatorFeatures(type, canPassDoors, canFloat, canWalkOverFences, canOpenDoors, allowBreaching); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorGenerator.java b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorGenerator.java new file mode 100644 index 0000000..df5365c --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorGenerator.java @@ -0,0 +1,8 @@ +package space.bxteam.divinemc.pathfinding; + +import net.minecraft.world.level.pathfinder.NodeEvaluator; +import org.jetbrains.annotations.NotNull; + +public interface NodeEvaluatorGenerator { + @NotNull NodeEvaluator generate(NodeEvaluatorFeatures nodeEvaluatorFeatures); +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorType.java b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorType.java new file mode 100644 index 0000000..897e4f6 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorType.java @@ -0,0 +1,17 @@ +package space.bxteam.divinemc.pathfinding; + +import net.minecraft.world.level.pathfinder.*; + +public enum NodeEvaluatorType { + WALK, + SWIM, + AMPHIBIOUS, + FLY; + + public static NodeEvaluatorType fromNodeEvaluator(NodeEvaluator nodeEvaluator) { + if (nodeEvaluator instanceof SwimNodeEvaluator) return SWIM; + if (nodeEvaluator instanceof FlyNodeEvaluator) return FLY; + if (nodeEvaluator instanceof AmphibiousNodeEvaluator) return AMPHIBIOUS; + return WALK; + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/PathProcessState.java b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/PathProcessState.java new file mode 100644 index 0000000..47877a9 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/pathfinding/PathProcessState.java @@ -0,0 +1,7 @@ +package space.bxteam.divinemc.pathfinding; + +public enum PathProcessState { + WAITING, + PROCESSING, + COMPLETED +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/region/AbstractRegionFile.java b/divinemc-server/src/main/java/space/bxteam/divinemc/region/AbstractRegionFile.java new file mode 100644 index 0000000..02f352f --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/region/AbstractRegionFile.java @@ -0,0 +1,28 @@ +package space.bxteam.divinemc.region; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Path; +import ca.spottedleaf.moonrise.patches.chunk_system.storage.ChunkSystemRegionFile; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.ChunkPos; + +public interface AbstractRegionFile extends AutoCloseable, ChunkSystemRegionFile { + Path getPath(); + void flush() throws IOException; + void clear(ChunkPos pos) throws IOException; + void close() throws IOException; + void setOversized(int x, int z, boolean b) throws IOException; + void write(ChunkPos pos, ByteBuffer buffer) 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; +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/region/AbstractRegionFileFactory.java b/divinemc-server/src/main/java/space/bxteam/divinemc/region/AbstractRegionFileFactory.java new file mode 100644 index 0000000..c57b111 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/region/AbstractRegionFileFactory.java @@ -0,0 +1,51 @@ +package space.bxteam.divinemc.region; + +import java.io.IOException; +import java.nio.file.Path; +import net.minecraft.world.level.chunk.storage.RegionFile; +import net.minecraft.world.level.chunk.storage.RegionFileVersion; +import net.minecraft.world.level.chunk.storage.RegionStorageInfo; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import space.bxteam.divinemc.configuration.DivineConfig; + +public class AbstractRegionFileFactory { + @Contract("_, _, _, _ -> new") + public static @NotNull AbstractRegionFile getAbstractRegionFile(RegionStorageInfo storageKey, Path directory, Path path, boolean dsync) throws IOException { + return getAbstractRegionFile(storageKey, directory, path, RegionFileVersion.getCompressionFormat(), dsync); + } + + @Contract("_, _, _, _, _ -> new") + public static @NotNull AbstractRegionFile getAbstractRegionFile(RegionStorageInfo storageKey, Path directory, Path path, boolean dsync, boolean canRecalcHeader) throws IOException { + return getAbstractRegionFile(storageKey, directory, path, RegionFileVersion.getCompressionFormat(), dsync, canRecalcHeader); + } + + @Contract("_, _, _, _, _ -> new") + public static @NotNull AbstractRegionFile getAbstractRegionFile(RegionStorageInfo storageKey, Path path, Path directory, RegionFileVersion compressionFormat, boolean dsync) throws IOException { + return getAbstractRegionFile(storageKey, path, directory, compressionFormat, dsync, true); + } + + @Contract("_, _, _, _, _, _ -> new") + public static @NotNull AbstractRegionFile getAbstractRegionFile(RegionStorageInfo storageKey, @NotNull Path path, Path directory, RegionFileVersion compressionFormat, boolean dsync, boolean canRecalcHeader) throws IOException { + final String fullFileName = path.getFileName().toString(); + final String[] fullNameSplit = fullFileName.split("\\."); + final String extensionName = fullNameSplit[fullNameSplit.length - 1]; + switch (RegionFileFormat.fromExtension(extensionName)) { + case UNKNOWN -> { + if (DivineConfig.throwOnUnknownExtension) { + throw new IllegalArgumentException("Unknown region file extension for file: " + fullFileName + "!"); + } + + return new RegionFile(storageKey, path, directory, compressionFormat, dsync); + } + + case LINEAR -> { + return new LinearRegionFile(path, storageKey.linearCompressionLevel()); + } + + default -> { + return new RegionFile(storageKey, path, directory, compressionFormat, dsync); + } + } + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/region/LinearRegionFile.java b/divinemc-server/src/main/java/space/bxteam/divinemc/region/LinearRegionFile.java new file mode 100644 index 0000000..d1618e4 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/region/LinearRegionFile.java @@ -0,0 +1,309 @@ +package space.bxteam.divinemc.region; + +import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; +import com.github.luben.zstd.ZstdInputStream; +import com.github.luben.zstd.ZstdOutputStream; +import com.mojang.logging.LogUtils; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +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.TimeUnit; +import javax.annotation.Nullable; + +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 org.slf4j.Logger; +import space.bxteam.divinemc.configuration.DivineConfig; + +public class LinearRegionFile implements AbstractRegionFile { + 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 final byte[][] buffer = new byte[1024][]; + private final int[] bufferUncompressedSize = new int[1024]; + private final int[] chunkTimestamps = new int[1024]; + private final LZ4Compressor compressor; + private final LZ4FastDecompressor decompressor; + private final int compressionLevel; + public boolean closed = false; + public Path path; + private volatile long lastFlushed = System.nanoTime(); + + 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; + } + } + } + } + } + + private static int getChunkIndex(int x, int z) { + return (x & 31) + ((z & 31) << 5); + } + + private static int getTimestamp() { + return (int) (System.currentTimeMillis() / 1000L); + } + + public void flush() throws IOException { + flushWrapper(); // sync + } + + 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); + this.lastFlushed = System.nanoTime(); + } + + 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); + } + + if ((System.nanoTime() - this.lastFlushed) >= TimeUnit.NANOSECONDS.toSeconds(DivineConfig.linearFlushFrequency)) { + this.flushWrapper(); + } + } + + public DataOutputStream getChunkDataOutputStream(ChunkPos pos) { + return new DataOutputStream(new BufferedOutputStream(new ChunkBuffer(pos))); + } + + @Override + public MoonriseRegionFileIO.RegionDataController.WriteData moonrise$startWrite(CompoundTag data, ChunkPos pos) throws IOException { + final DataOutputStream out = this.getChunkDataOutputStream(pos); + + return new ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData( + data, ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController.WriteData.WriteResult.WRITE, + out, regionFile -> out.close() + ); + } + + 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 void clear(ChunkPos pos) { + int i = getChunkIndex(pos.x, pos.z); + this.buffer[i] = null; + this.bufferUncompressedSize[i] = 0; + this.chunkTimestamps[i] = getTimestamp(); + this.flushWrapper(); + } + + public Path getPath() { + return this.path; + } + + 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 + } + + 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; + } + + private class ChunkBuffer extends ByteArrayOutputStream { + private final ChunkPos pos; + + public ChunkBuffer(ChunkPos chunkcoordintpair) { + super(); + this.pos = chunkcoordintpair; + } + + public void close() { + ByteBuffer bytebuffer = ByteBuffer.wrap(this.buf, 0, this.count); + LinearRegionFile.this.write(this.pos, bytebuffer); + } + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/region/RegionFileFormat.java b/divinemc-server/src/main/java/space/bxteam/divinemc/region/RegionFileFormat.java new file mode 100644 index 0000000..2fe2ab6 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/region/RegionFileFormat.java @@ -0,0 +1,55 @@ +package space.bxteam.divinemc.region; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import java.util.Locale; + +public enum RegionFileFormat { + LINEAR(".linear"), + MCA(".mca"), + UNKNOWN(null); + + private final String extensionName; + + RegionFileFormat(String extensionName) { + this.extensionName = extensionName; + } + + public String getExtensionName() { + return this.extensionName; + } + + @Contract(pure = true) + public static RegionFileFormat fromName(@NotNull String name) { + switch (name.toUpperCase(Locale.ROOT)) { + default -> { + return UNKNOWN; + } + + case "MCA" -> { + return MCA; + } + + case "LINEAR" -> { + return LINEAR; + } + } + } + + @Contract(pure = true) + public static RegionFileFormat fromExtension(@NotNull String name) { + switch (name.toLowerCase()) { + case "mca" -> { + return MCA; + } + + case "linear" -> { + return LINEAR; + } + + default -> { + return UNKNOWN; + } + } + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Globals.java b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Globals.java new file mode 100644 index 0000000..39d2d74 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Globals.java @@ -0,0 +1,94 @@ +package space.bxteam.divinemc.seed; + +import com.google.common.collect.Iterables; +import net.minecraft.server.level.ServerLevel; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Optional; + +public class Globals { + public static final int WORLD_SEED_LONGS = 16; + public static final int WORLD_SEED_BITS = WORLD_SEED_LONGS * 64; + + public static final long[] worldSeed = new long[WORLD_SEED_LONGS]; + public static final ThreadLocal dimension = ThreadLocal.withInitial(() -> 0); + + public enum Salt { + UNDEFINED, + BASTION_FEATURE, + WOODLAND_MANSION_FEATURE, + MINESHAFT_FEATURE, + BURIED_TREASURE_FEATURE, + NETHER_FORTRESS_FEATURE, + PILLAGER_OUTPOST_FEATURE, + GEODE_FEATURE, + NETHER_FOSSIL_FEATURE, + OCEAN_MONUMENT_FEATURE, + RUINED_PORTAL_FEATURE, + POTENTIONAL_FEATURE, + GENERATE_FEATURE, + JIGSAW_PLACEMENT, + STRONGHOLDS, + POPULATION, + DECORATION, + SLIME_CHUNK + } + + public static void setupGlobals(ServerLevel world) { + if (!space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) return; + + long[] seed = world.getServer().getWorldData().worldGenOptions().featureSeed(); + System.arraycopy(seed, 0, worldSeed, 0, WORLD_SEED_LONGS); + int worldIndex = Iterables.indexOf(world.getServer().levelKeys(), it -> it == world.dimension()); + if (worldIndex == -1) + worldIndex = world.getServer().levelKeys().size(); // if we are in world construction it may not have been added to the map yet + dimension.set(worldIndex); + } + + public static long[] createRandomWorldSeed() { + long[] seed = new long[WORLD_SEED_LONGS]; + SecureRandom rand = new SecureRandom(); + for (int i = 0; i < WORLD_SEED_LONGS; i++) { + seed[i] = rand.nextLong(); + } + return seed; + } + + // 1024-bit string -> 16 * 64 long[] + public static Optional parseSeed(String seedStr) { + if (seedStr.isEmpty()) return Optional.empty(); + + if (seedStr.length() != WORLD_SEED_BITS) { + throw new IllegalArgumentException("Secure seed length must be " + WORLD_SEED_BITS + "-bit but found " + seedStr.length() + "-bit."); + } + + long[] seed = new long[WORLD_SEED_LONGS]; + + for (int i = 0; i < WORLD_SEED_LONGS; i++) { + int start = i * 64; + int end = start + 64; + String seedSection = seedStr.substring(start, end); + + BigInteger seedInDecimal = new BigInteger(seedSection, 2); + seed[i] = seedInDecimal.longValue(); + } + + return Optional.of(seed); + } + + // 16 * 64 long[] -> 1024-bit string + public static String seedToString(long[] seed) { + StringBuilder sb = new StringBuilder(); + + for (long longV : seed) { + // Convert to 64-bit binary string per long + // Use format to keep 64-bit length, and use 0 to complete space + String binaryStr = String.format("%64s", Long.toBinaryString(longV)).replace(' ', '0'); + + sb.append(binaryStr); + } + + return sb.toString(); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Hashing.java b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Hashing.java new file mode 100644 index 0000000..abedc9f --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/Hashing.java @@ -0,0 +1,73 @@ +package space.bxteam.divinemc.seed; + +public class Hashing { + // https://en.wikipedia.org/wiki/BLAKE_(hash_function) + // https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java + + private final static long[] blake2b_IV = { + 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL, + 0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL, + 0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L + }; + + private final static byte[][] blake2b_sigma = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3} + }; + + public static long[] hashWorldSeed(long[] worldSeed) { + long[] result = blake2b_IV.clone(); + result[0] ^= 0x01010040; + hash(worldSeed, result, new long[16], 0, false); + return result; + } + + public static void hash(long[] message, long[] chainValue, long[] internalState, long messageOffset, boolean isFinal) { + assert message.length == 16; + assert chainValue.length == 8; + assert internalState.length == 16; + + System.arraycopy(chainValue, 0, internalState, 0, chainValue.length); + System.arraycopy(blake2b_IV, 0, internalState, chainValue.length, 4); + internalState[12] = messageOffset ^ blake2b_IV[4]; + internalState[13] = blake2b_IV[5]; + if (isFinal) internalState[14] = ~blake2b_IV[6]; + internalState[15] = blake2b_IV[7]; + + for (int round = 0; round < 12; round++) { + G(message[blake2b_sigma[round][0]], message[blake2b_sigma[round][1]], 0, 4, 8, 12, internalState); + G(message[blake2b_sigma[round][2]], message[blake2b_sigma[round][3]], 1, 5, 9, 13, internalState); + G(message[blake2b_sigma[round][4]], message[blake2b_sigma[round][5]], 2, 6, 10, 14, internalState); + G(message[blake2b_sigma[round][6]], message[blake2b_sigma[round][7]], 3, 7, 11, 15, internalState); + G(message[blake2b_sigma[round][8]], message[blake2b_sigma[round][9]], 0, 5, 10, 15, internalState); + G(message[blake2b_sigma[round][10]], message[blake2b_sigma[round][11]], 1, 6, 11, 12, internalState); + G(message[blake2b_sigma[round][12]], message[blake2b_sigma[round][13]], 2, 7, 8, 13, internalState); + G(message[blake2b_sigma[round][14]], message[blake2b_sigma[round][15]], 3, 4, 9, 14, internalState); + } + + for (int i = 0; i < 8; i++) { + chainValue[i] ^= internalState[i] ^ internalState[i + 8]; + } + } + + private static void G(long m1, long m2, int posA, int posB, int posC, int posD, long[] internalState) { + internalState[posA] = internalState[posA] + internalState[posB] + m1; + internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 32); + internalState[posC] = internalState[posC] + internalState[posD]; + internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 24); // replaces 25 of BLAKE + internalState[posA] = internalState[posA] + internalState[posB] + m2; + internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 16); + internalState[posC] = internalState[posC] + internalState[posD]; + internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 63); // replaces 11 of BLAKE + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java new file mode 100644 index 0000000..f6e69a1 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java @@ -0,0 +1,159 @@ +package space.bxteam.divinemc.seed; + +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.levelgen.LegacyRandomSource; +import net.minecraft.world.level.levelgen.WorldgenRandom; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; + +public class WorldgenCryptoRandom extends WorldgenRandom { + // hash the world seed to guard against badly chosen world seeds + private static final long[] HASHED_ZERO_SEED = Hashing.hashWorldSeed(new long[Globals.WORLD_SEED_LONGS]); + private static final ThreadLocal LAST_SEEN_WORLD_SEED = ThreadLocal.withInitial(() -> new long[Globals.WORLD_SEED_LONGS]); + private static final ThreadLocal HASHED_WORLD_SEED = ThreadLocal.withInitial(() -> HASHED_ZERO_SEED); + + private final long[] worldSeed = new long[Globals.WORLD_SEED_LONGS]; + private final long[] randomBits = new long[8]; + private int randomBitIndex; + private static final int MAX_RANDOM_BIT_INDEX = 64 * 8; + private static final int LOG2_MAX_RANDOM_BIT_INDEX = 9; + private long counter; + private final long[] message = new long[16]; + private final long[] cachedInternalState = new long[16]; + + public WorldgenCryptoRandom(int x, int z, Globals.Salt typeSalt, long salt) { + super(new LegacyRandomSource(0L)); + if (typeSalt != null) { + this.setSecureSeed(x, z, typeSalt, salt); + } + } + + public void setSecureSeed(int x, int z, Globals.Salt typeSalt, long salt) { + System.arraycopy(Globals.worldSeed, 0, this.worldSeed, 0, Globals.WORLD_SEED_LONGS); + message[0] = ((long) x << 32) | ((long) z & 0xffffffffL); + message[1] = ((long) Globals.dimension.get() << 32) | ((long) salt & 0xffffffffL); + message[2] = typeSalt.ordinal(); + message[3] = counter = 0; + randomBitIndex = MAX_RANDOM_BIT_INDEX; + } + + private long[] getHashedWorldSeed() { + if (!Arrays.equals(worldSeed, LAST_SEEN_WORLD_SEED.get())) { + HASHED_WORLD_SEED.set(Hashing.hashWorldSeed(worldSeed)); + System.arraycopy(worldSeed, 0, LAST_SEEN_WORLD_SEED.get(), 0, Globals.WORLD_SEED_LONGS); + } + return HASHED_WORLD_SEED.get(); + } + + private void moreRandomBits() { + message[3] = counter++; + System.arraycopy(getHashedWorldSeed(), 0, randomBits, 0, 8); + Hashing.hash(message, randomBits, cachedInternalState, 64, true); + } + + private long getBits(int count) { + if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) { + moreRandomBits(); + randomBitIndex -= MAX_RANDOM_BIT_INDEX; + } + + int alignment = randomBitIndex & 63; + if ((randomBitIndex >>> 6) == ((randomBitIndex + count) >>> 6)) { + long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << count) - 1); + randomBitIndex += count; + return result; + } else { + long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << (64 - alignment)) - 1); + randomBitIndex += count; + if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) { + moreRandomBits(); + randomBitIndex -= MAX_RANDOM_BIT_INDEX; + } + alignment = randomBitIndex & 63; + result <<= alignment; + result |= (randomBits[randomBitIndex >>> 6] >>> (64 - alignment)) & ((1L << alignment) - 1); + + return result; + } + } + + @Override + public @NotNull RandomSource fork() { + WorldgenCryptoRandom fork = new WorldgenCryptoRandom(0, 0, null, 0); + + System.arraycopy(Globals.worldSeed, 0, fork.worldSeed, 0, Globals.WORLD_SEED_LONGS); + fork.message[0] = this.message[0]; + fork.message[1] = this.message[1]; + fork.message[2] = this.message[2]; + fork.message[3] = this.message[3]; + fork.randomBitIndex = this.randomBitIndex; + fork.counter = this.counter; + fork.nextLong(); + + return fork; + } + + @Override + public int next(int bits) { + return (int) getBits(bits); + } + + @Override + public void consumeCount(int count) { + randomBitIndex += count; + if (randomBitIndex >= MAX_RANDOM_BIT_INDEX * 2) { + randomBitIndex -= MAX_RANDOM_BIT_INDEX; + counter += randomBitIndex >>> LOG2_MAX_RANDOM_BIT_INDEX; + randomBitIndex &= MAX_RANDOM_BIT_INDEX - 1; + randomBitIndex += MAX_RANDOM_BIT_INDEX; + } + } + + @Override + public int nextInt(int bound) { + int bits = Mth.ceillog2(bound); + int result; + do { + result = (int) getBits(bits); + } while (result >= bound); + + return result; + } + + @Override + public long nextLong() { + return getBits(64); + } + + @Override + public double nextDouble() { + return getBits(53) * 0x1.0p-53; + } + + @Override + public long setDecorationSeed(long worldSeed, int blockX, int blockZ) { + setSecureSeed(blockX, blockZ, Globals.Salt.POPULATION, 0); + return ((long) blockX << 32) | ((long) blockZ & 0xffffffffL); + } + + @Override + public void setFeatureSeed(long populationSeed, int index, int step) { + setSecureSeed((int) (populationSeed >> 32), (int) populationSeed, Globals.Salt.DECORATION, index + 10000L * step); + } + + @Override + public void setLargeFeatureSeed(long worldSeed, int chunkX, int chunkZ) { + super.setLargeFeatureSeed(worldSeed, chunkX, chunkZ); + } + + @Override + public void setLargeFeatureWithSalt(long worldSeed, int regionX, int regionZ, int salt) { + super.setLargeFeatureWithSalt(worldSeed, regionX, regionZ, salt); + } + + public static RandomSource seedSlimeChunk(int chunkX, int chunkZ) { + return new WorldgenCryptoRandom(chunkX, chunkZ, Globals.Salt.SLIME_CHUNK, 0); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/tracker/MultithreadedTracker.java b/divinemc-server/src/main/java/space/bxteam/divinemc/tracker/MultithreadedTracker.java new file mode 100644 index 0000000..f5f686d --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/tracker/MultithreadedTracker.java @@ -0,0 +1,141 @@ +package space.bxteam.divinemc.tracker; + +import ca.spottedleaf.moonrise.common.list.ReferenceList; +import ca.spottedleaf.moonrise.common.misc.NearbyPlayers; +import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; +import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup; +import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity; +import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.FullChunkStatus; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.Entity; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class MultithreadedTracker { + private static final Logger LOGGER = LogManager.getLogger("MultithreadedTracker"); + + public static class MultithreadedTrackerThread extends Thread { + @Override + public void run() { + super.run(); + } + } + + private static final Executor trackerExecutor = new ThreadPoolExecutor( + 1, + space.bxteam.divinemc.configuration.DivineConfig.asyncEntityTrackerMaxThreads, + space.bxteam.divinemc.configuration.DivineConfig.asyncEntityTrackerKeepalive, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + new ThreadFactoryBuilder() + .setThreadFactory( + r -> new MultithreadedTrackerThread() { + @Override + public void run() { + r.run(); + } + } + ) + .setNameFormat("DivineMC Async Tracker Thread - %d") + .setPriority(Thread.NORM_PRIORITY - 2) + .build()); + + private MultithreadedTracker() { } + + public static Executor getTrackerExecutor() { + return trackerExecutor; + } + + public static void tick(ChunkSystemServerLevel level) { + try { + if (!space.bxteam.divinemc.configuration.DivineConfig.multithreadedCompatModeEnabled) { + tickAsync(level); + } else { + tickAsyncWithCompatMode(level); + } + } catch (Exception e) { + LOGGER.error("Error occurred while executing async task.", e); + } + } + + private static void tickAsync(ChunkSystemServerLevel level) { + final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers(); + final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup(); + + final ReferenceList trackerEntities = entityLookup.trackerEntities; + final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked(); + + // Move tracking to off-main + trackerExecutor.execute(() -> { + for (final Entity entity : trackerEntitiesRaw) { + if (entity == null) continue; + + final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity(); + + if (tracker == null) continue; + + ((EntityTrackerTrackedEntity) tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); + tracker.serverEntity.sendChanges(); + } + }); + } + + private static void tickAsyncWithCompatMode(ChunkSystemServerLevel level) { + final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers(); + final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup(); + + final ReferenceList trackerEntities = entityLookup.trackerEntities; + final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked(); + final Runnable[] sendChangesTasks = new Runnable[trackerEntitiesRaw.length]; + int index = 0; + + for (final Entity entity : trackerEntitiesRaw) { + if (entity == null) continue; + + final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity(); + + if (tracker == null) continue; + + ((EntityTrackerTrackedEntity) tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); + sendChangesTasks[index++] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array + } + + // batch submit tasks + trackerExecutor.execute(() -> { + for (final Runnable sendChanges : sendChangesTasks) { + if (sendChanges == null) continue; + + sendChanges.run(); + } + }); + } + + // Original ChunkMap#newTrackerTick of Paper + // Just for diff usage for future update + @SuppressWarnings("DuplicatedCode") + private static void tickOriginal(ServerLevel level) { + final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup entityLookup = (ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup) ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel) level).moonrise$getEntityLookup(); + + final ca.spottedleaf.moonrise.common.list.ReferenceList trackerEntities = entityLookup.trackerEntities; + final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked(); + for (int i = 0, len = trackerEntities.size(); i < len; ++i) { + final Entity entity = trackerEntitiesRaw[i]; + final ChunkMap.TrackedEntity tracker = ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity) entity).moonrise$getTrackedEntity(); + if (tracker == null) { + continue; + } + ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity) tracker).moonrise$tick(((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity) entity).moonrise$getChunkData().nearbyPlayers); + if (((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity) tracker).moonrise$hasPlayers() + || ((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity) entity).moonrise$getChunkStatus().isOrAfter(FullChunkStatus.ENTITY_TICKING)) { + tracker.serverEntity.sendChanges(); + } + } + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/util/c2me/ObjectCachingUtils.java b/divinemc-server/src/main/java/space/bxteam/divinemc/util/c2me/ObjectCachingUtils.java new file mode 100644 index 0000000..828b9cb --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/util/c2me/ObjectCachingUtils.java @@ -0,0 +1,20 @@ +package space.bxteam.divinemc.util.c2me; + +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +import java.util.BitSet; +import java.util.function.IntFunction; + +public class ObjectCachingUtils { + private static final IntFunction bitSetConstructor = BitSet::new; + + public static ThreadLocal> BITSETS = ThreadLocal.withInitial(Int2ObjectOpenHashMap::new); + + private ObjectCachingUtils() {} + + public static BitSet getCachedOrNewBitSet(int bits) { + final BitSet bitSet = BITSETS.get().computeIfAbsent(bits, bitSetConstructor); + bitSet.clear(); + return bitSet; + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/util/carpetams/BlockPatternHelper.java b/divinemc-server/src/main/java/space/bxteam/divinemc/util/carpetams/BlockPatternHelper.java new file mode 100644 index 0000000..1bf8f3d --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/util/carpetams/BlockPatternHelper.java @@ -0,0 +1,32 @@ +package space.bxteam.divinemc.util.carpetams; + +import com.google.common.cache.LoadingCache; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.pattern.BlockInWorld; +import net.minecraft.world.level.block.state.pattern.BlockPattern; + +/** + * Original here + * + * @author 1024-byteeeee + */ +public class BlockPatternHelper { + public static BlockPattern.BlockPatternMatch partialSearchAround(BlockPattern pattern, Level world, BlockPos pos) { + LoadingCache loadingCache = BlockPattern.createLevelCache(world, false); + int i = Math.max(Math.max(pattern.getWidth(), pattern.getHeight()), pattern.getDepth()); + + for (BlockPos blockPos : BlockPos.betweenClosed(pos, pos.offset(i - 1, 0, i - 1))) { + for (Direction direction : Direction.values()) { + for (Direction direction2 : Direction.values()) { + BlockPattern.BlockPatternMatch result; + if (direction2 == direction || direction2 == direction.getOpposite() || (result = pattern.matches(blockPos, direction, direction2, loadingCache)) == null) + continue; + return result; + } + } + } + return null; + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/util/lithium/CompactSineLUT.java b/divinemc-server/src/main/java/space/bxteam/divinemc/util/lithium/CompactSineLUT.java new file mode 100644 index 0000000..76225e1 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/util/lithium/CompactSineLUT.java @@ -0,0 +1,88 @@ +package space.bxteam.divinemc.util.lithium; + +import net.minecraft.util.Mth; + +/** + * A replacement for the sine angle lookup table used in {@link Mth}, both reducing the size of LUT and improving + * the access patterns for common paired sin/cos operations. + *

+ * sin(-x) = -sin(x) + * ... to eliminate negative angles from the LUT. + *

+ * sin(x) = sin(pi/2 - x) + * ... to eliminate supplementary angles from the LUT. + *

+ * Using these identities allows us to reduce the LUT from 64K entries (256 KB) to just 16K entries (64 KB), enabling + * it to better fit into the CPU's caches at the expense of some cycles on the fast path. The implementation has been + * tightly optimized to avoid branching where possible and to use very quick integer operations. + *

+ * Generally speaking, reducing the size of a lookup table is always a good optimization, but since we need to spend + * extra CPU cycles trying to maintain parity with vanilla, there is the potential risk that this implementation ends + * up being slower than vanilla when the lookup table is able to be kept in cache memory. + *

+ * Unlike other "fast math" implementations, the values returned by this class are *bit-for-bit identical* with those + * from {@link Mth}. Validation is performed during runtime to ensure that the table is correct. + * + * @author coderbot16 Author of the original (and very clever) implementation in Rust + * @author jellysquid3 Additional optimizations, port to Java + */ +public class CompactSineLUT { + private static final int[] SIN_INT = new int[16384 + 1]; + private static final float SIN_MIDPOINT; + + static { + // Copy the sine table, covering to raw int bits + for (int i = 0; i < SIN_INT.length; i++) { + SIN_INT[i] = Float.floatToRawIntBits(Mth.SIN[i]); + } + + SIN_MIDPOINT = Mth.SIN[Mth.SIN.length / 2]; + + // Test that the lookup table is correct during runtime + for (int i = 0; i < Mth.SIN.length; i++) { + float expected = Mth.SIN[i]; + float value = lookup(i); + + if (expected != value) { + throw new IllegalArgumentException(String.format("LUT error at index %d (expected: %s, found: %s)", i, expected, value)); + } + } + } + + // [VanillaCopy] Mth#sin(float) + public static float sin(float f) { + return lookup((int) (f * 10430.378f) & 0xFFFF); + } + + // [VanillaCopy] Mth#cos(float) + public static float cos(float f) { + return lookup((int) (f * 10430.378f + 16384.0f) & 0xFFFF); + } + + private static float lookup(int index) { + // A special case... Is there some way to eliminate this? + if (index == 32768) { + return SIN_MIDPOINT; + } + + // Trigonometric identity: sin(-x) = -sin(x) + // Given a domain of 0 <= x <= 2*pi, just negate the value if x > pi. + // This allows the sin table size to be halved. + int neg = (index & 0x8000) << 16; + + // All bits set if (pi/2 <= x), none set otherwise + // Extracts the 15th bit from 'half' + int mask = (index << 17) >> 31; + + // Trigonometric identity: sin(x) = sin(pi/2 - x) + int pos = (0x8001 & mask) + (index ^ mask); + + // Wrap the position in the table. Moving this down to immediately before the array access + // seems to help the Hotspot compiler optimize the bit math better. + pos &= 0x7fff; + + // Fetch the corresponding value from the LUT and invert the sign bit as needed + // This directly manipulate the sign bit on the float bits to simplify logic + return Float.intBitsToFloat(SIN_INT[pos] ^ neg); + } +} diff --git a/divinemc-server/src/main/java/space/bxteam/divinemc/util/lithium/HashedReferenceList.java b/divinemc-server/src/main/java/space/bxteam/divinemc/util/lithium/HashedReferenceList.java new file mode 100644 index 0000000..aafa3e5 --- /dev/null +++ b/divinemc-server/src/main/java/space/bxteam/divinemc/util/lithium/HashedReferenceList.java @@ -0,0 +1,281 @@ +package space.bxteam.divinemc.util.lithium; + +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +/** + * Wraps a {@link List} with a hash table which provides O(1) lookups for {@link Collection#contains(Object)}. The type + * contained by this list must use reference-equality semantics. + */ +@SuppressWarnings("SuspiciousMethodCalls") +public class HashedReferenceList implements List { + private final ReferenceArrayList list; + private final Reference2IntOpenHashMap counter; + + public HashedReferenceList(List list) { + this.list = new ReferenceArrayList<>(); + this.list.addAll(list); + + this.counter = new Reference2IntOpenHashMap<>(); + this.counter.defaultReturnValue(0); + + for (T obj : this.list) { + this.counter.addTo(obj, 1); + } + } + + @Override + public int size() { + return this.list.size(); + } + + @Override + public boolean isEmpty() { + return this.list.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return this.counter.containsKey(o); + } + + @Override + public Iterator iterator() { + return this.listIterator(); + } + + @Override + public Object[] toArray() { + return this.list.toArray(); + } + + @Override + public T1[] toArray(T1 @NotNull [] a) { + return this.list.toArray(a); + } + + @Override + public boolean add(T t) { + this.trackReferenceAdded(t); + + return this.list.add(t); + } + + @Override + public boolean remove(Object o) { + this.trackReferenceRemoved(o); + + return this.list.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + for (Object obj : c) { + if (!this.counter.containsKey(obj)) { + return false; + } + } + + return true; + } + + @Override + public boolean addAll(Collection c) { + for (T obj : c) { + this.trackReferenceAdded(obj); + } + + return this.list.addAll(c); + } + + @Override + public boolean addAll(int index, Collection c) { + for (T obj : c) { + this.trackReferenceAdded(obj); + } + + return this.list.addAll(index, c); + } + + @Override + public boolean removeAll(@NotNull Collection c) { + if (this.size() >= 2 && c.size() > 4 && c instanceof List) { + //HashReferenceList uses reference equality, so using ReferenceOpenHashSet is fine + c = new ReferenceOpenHashSet<>(c); + } + this.counter.keySet().removeAll(c); + return this.list.removeAll(c); + } + + @Override + public boolean retainAll(@NotNull Collection c) { + this.counter.keySet().retainAll(c); + return this.list.retainAll(c); + } + + @Override + public void clear() { + this.counter.clear(); + this.list.clear(); + } + + @Override + public T get(int index) { + return this.list.get(index); + } + + @Override + public T set(int index, T element) { + T prev = this.list.set(index, element); + + if (prev != element) { + if (prev != null) { + this.trackReferenceRemoved(prev); + } + + this.trackReferenceAdded(element); + } + + return prev; + } + + @Override + public void add(int index, T element) { + this.trackReferenceAdded(element); + + this.list.add(index, element); + } + + @Override + public T remove(int index) { + T prev = this.list.remove(index); + + if (prev != null) { + this.trackReferenceRemoved(prev); + } + + return prev; + } + + @Override + public int indexOf(Object o) { + return this.list.indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + return this.list.lastIndexOf(o); + } + + @Override + public ListIterator listIterator() { + return this.listIterator(0); + } + + @Override + public ListIterator listIterator(int index) { + return new ListIterator<>() { + private final ListIterator inner = HashedReferenceList.this.list.listIterator(index); + + @Override + public boolean hasNext() { + return this.inner.hasNext(); + } + + @Override + public T next() { + return this.inner.next(); + } + + @Override + public boolean hasPrevious() { + return this.inner.hasPrevious(); + } + + @Override + public T previous() { + return this.inner.previous(); + } + + @Override + public int nextIndex() { + return this.inner.nextIndex(); + } + + @Override + public int previousIndex() { + return this.inner.previousIndex(); + } + + @Override + public void remove() { + int last = this.previousIndex(); + + if (last == -1) { + throw new NoSuchElementException(); + } + + T prev = HashedReferenceList.this.get(last); + + if (prev != null) { + HashedReferenceList.this.trackReferenceRemoved(prev); + } + + this.inner.remove(); + } + + @Override + public void set(T t) { + int last = this.previousIndex(); + + if (last == -1) { + throw new NoSuchElementException(); + } + + T prev = HashedReferenceList.this.get(last); + + if (prev != t) { + if (prev != null) { + HashedReferenceList.this.trackReferenceRemoved(prev); + } + + HashedReferenceList.this.trackReferenceAdded(t); + } + + this.inner.remove(); + } + + @Override + public void add(T t) { + HashedReferenceList.this.trackReferenceAdded(t); + + this.inner.add(t); + } + }; + } + + @Override + public List subList(int fromIndex, int toIndex) { + return this.list.subList(fromIndex, toIndex); + } + + private void trackReferenceAdded(T t) { + this.counter.addTo(t, 1); + } + + @SuppressWarnings("unchecked") + private void trackReferenceRemoved(Object o) { + if (this.counter.addTo((T) o, -1) <= 1) { + this.counter.removeInt(o); + } + } + +} diff --git a/gradle.properties b/gradle.properties index fc80fa8..2dc2d31 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,8 +2,9 @@ group = space.bxteam.divinemc mcVersion = 1.21.4 version = 1.21.4-R0.1-SNAPSHOT -purpurRef = dd4143984219cea8440913b7918322b5ba59265a +purpurRef = a9bcd9f0c8b66ad84f9ca6005d425902c9cd0442 +org.gradle.configuration-cache=true org.gradle.caching = true org.gradle.parallel = true org.gradle.vfs.watch = false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df97d72..cea7a79 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6..f3b75f3 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 9b42019..9d21a21 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,94 +1,94 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/patches/api/0001-Divine-Branding.patch b/patches/api/0001-Divine-Branding.patch deleted file mode 100644 index 5924cea..0000000 --- a/patches/api/0001-Divine-Branding.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 18 May 2024 01:10:38 +0300 -Subject: [PATCH] Divine Branding - - -diff --git a/build.gradle.kts b/build.gradle.kts -index 49546dfbb4dd006b5a2419908890ba4c2a0e207a..db5b44861cabd70f7d9acb6a8a951efd68b97a18 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -257,3 +257,12 @@ tasks.check { - dependsOn(scanJarForOldGeneratedCode) - } - // Paper end -+ -+// DivineMC start - Hide unnecessary compilation warnings -+tasks.withType { -+ val compilerArgs = options.compilerArgs -+ compilerArgs.add("-Xlint:-module") -+ compilerArgs.add("-Xlint:-removal") -+ compilerArgs.add("-Xlint:-dep-ann") -+} -+// DivineMC end -diff --git a/src/main/java/io/papermc/paper/ServerBuildInfo.java b/src/main/java/io/papermc/paper/ServerBuildInfo.java -index 7196594e07af19a14c320d77df893978525fe386..6811f38a2eaf614c8237190800a48d101c39cd99 100644 ---- a/src/main/java/io/papermc/paper/ServerBuildInfo.java -+++ b/src/main/java/io/papermc/paper/ServerBuildInfo.java -@@ -33,6 +33,13 @@ public interface ServerBuildInfo { - Key BRAND_PURPUR_ID = Key.key("purpurmc", "purpur"); - // Purpur end - -+ // DivineMC start -+ /** -+ * The brand id for DivineMC. -+ */ -+ Key BRAND_DIVINEMC_ID = Key.key("divinemc", "divinemc"); -+ // DivineMC end -+ - /** - * Gets the {@code ServerBuildInfo}. - * -diff --git a/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/src/main/java/org/bukkit/command/defaults/VersionCommand.java -index c880d0010849ab733ad13bbd18fab3c864d0cf61..de6522adba933dca90338bfb19f833486e696b8d 100644 ---- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java -+++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java -@@ -259,7 +259,7 @@ public class VersionCommand extends BukkitCommand { - // Purpur start - int distance = getVersionFetcher().distance(); - final Component message = Component.join(net.kyori.adventure.text.JoinConfiguration.separator(Component.newline()), -- ChatColor.parseMM("Current Purpur Version: %s%s*", distance == 0 ? "" : distance > 0 ? "" : "", Bukkit.getVersion()), -+ ChatColor.parseMM("Current DivineMC Version: %s%s*", distance == 0 ? "" : distance > 0 ? "" : "", Bukkit.getVersion()), // DivineMC - // Purpur end - msg - ); diff --git a/patches/api/0002-Divine-Configuration.patch b/patches/api/0002-Divine-Configuration.patch deleted file mode 100644 index e0e4adc..0000000 --- a/patches/api/0002-Divine-Configuration.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 27 Mar 2023 17:50:05 +0300 -Subject: [PATCH] Divine Configuration - - -diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java -index ddb1ff894910761a78b91a343f32e129f03a03c4..ca097588b84912b6d8a763b7e36885fbdf3dffec 100644 ---- a/src/main/java/org/bukkit/Server.java -+++ b/src/main/java/org/bukkit/Server.java -@@ -2295,6 +2295,13 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi - } - // Purpur end - -+ // DivineMC start -+ @NotNull -+ public org.bukkit.configuration.file.YamlConfiguration getDivineConfig() { -+ throw new UnsupportedOperationException("Not supported yet"); -+ } -+ // DivineMC end -+ - /** - * Sends the component to the player - * diff --git a/patches/api/0005-DivineMC-API-Publish.patch b/patches/api/0005-DivineMC-API-Publish.patch deleted file mode 100644 index a459491..0000000 --- a/patches/api/0005-DivineMC-API-Publish.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Tue, 14 May 2024 17:22:39 +0300 -Subject: [PATCH] DivineMC API Publish - - -diff --git a/build.gradle.kts b/build.gradle.kts -index db5b44861cabd70f7d9acb6a8a951efd68b97a18..95af32719a74893b0c0c7f20f178f222f8d24777 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -266,3 +266,14 @@ tasks.withType { - compilerArgs.add("-Xlint:-dep-ann") - } - // DivineMC end -+ -+// DivineMC start - publish to GitHub Packages -+publishing { -+ repositories { -+ maven("https://repo.bx-team.space/snapshots") { -+ name = "divinemc" -+ credentials(PasswordCredentials::class) -+ } -+ } -+} -+// DivineMC end diff --git a/patches/api/0006-Bump-Dependencies.patch b/patches/api/0006-Bump-Dependencies.patch deleted file mode 100644 index 6f0af3f..0000000 --- a/patches/api/0006-Bump-Dependencies.patch +++ /dev/null @@ -1,81 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Thu, 11 Jul 2024 18:55:47 +0300 -Subject: [PATCH] Bump Dependencies - - -diff --git a/build.gradle.kts b/build.gradle.kts -index 95af32719a74893b0c0c7f20f178f222f8d24777..d6deb2136e314f9e9d66123ab4aaa60342156d3a 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -9,11 +9,17 @@ java { - withJavadocJar() - } - -+// DivineMC start - Bump Dependencies -+repositories { -+ mavenCentral() -+} -+// DivineMC end -+ - val annotationsVersion = "24.1.0" - val bungeeCordChatVersion = "1.20-R0.2" - val adventureVersion = "4.17.0" --val slf4jVersion = "2.0.9" --val log4jVersion = "2.17.1" -+val slf4jVersion = "2.0.13" // DivineMC - Bump Dependencies -+val log4jVersion = "2.23.1" // DivineMC - Bump Dependencies - val apiAndDocs: Configuration by configurations.creating { - attributes { - attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION)) -@@ -72,23 +78,23 @@ dependencies { - implementation("org.ow2.asm:asm-commons:9.7.1") - // Paper end - -- api("org.apache.maven:maven-resolver-provider:3.9.6") // Paper - make API dependency for Paper Plugins -- compileOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18") -- compileOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.18") -+ api("org.apache.maven:maven-resolver-provider:3.9.7") // Paper - make API dependency for Paper Plugins // DivineMC - Bump Dependencies -+ compileOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.20") // DivineMC - Bump Dependencies -+ compileOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.9.20") // DivineMC - Bump Dependencies - - val annotations = "org.jetbrains:annotations:$annotationsVersion" // Paper - we don't want Java 5 annotations... - compileOnly(annotations) - testCompileOnly(annotations) - - // Paper start - add checker -- val checkerQual = "org.checkerframework:checker-qual:3.33.0" -+ val checkerQual = "org.checkerframework:checker-qual:3.44.0" // DivineMC - Bump Dependencies - compileOnlyApi(checkerQual) - testCompileOnly(checkerQual) - // Paper end - api("org.jspecify:jspecify:1.0.0") // Paper - add jspecify - -- testImplementation("org.apache.commons:commons-lang3:3.12.0") -- testImplementation("org.junit.jupiter:junit-jupiter:5.10.2") -+ testImplementation("org.apache.commons:commons-lang3:3.14.0") // DivineMC - Bump Dependencies -+ testImplementation("org.junit.jupiter:junit-jupiter:5.11.0-M2") // DivineMC - Bump Dependencies - testImplementation("org.hamcrest:hamcrest:2.2") - testImplementation("org.mockito:mockito-core:5.14.1") - testImplementation("org.ow2.asm:asm-tree:9.7.1") -@@ -182,7 +188,7 @@ tasks.withType { - "https://javadoc.io/doc/org.jetbrains/annotations/$annotationsVersion/", // Paper - we don't want Java 5 annotations - // "https://javadoc.io/doc/net.md-5/bungeecord-chat/$bungeeCordChatVersion/", // Paper - don't link to bungee chat - // Paper start - add missing javadoc links -- "https://javadoc.io/doc/org.joml/joml/1.10.8/index.html", -+ "https://javadoc.io/doc/org.joml/joml/latest/index.html", // DivineMC - Bump Dependencies - "https://www.javadoc.io/doc/com.google.code.gson/gson/2.11.0", - "https://jspecify.dev/docs/api/", - // Paper end -@@ -195,9 +201,9 @@ tasks.withType { - "https://jd.advntr.dev/text-serializer-plain/$adventureVersion/", - "https://jd.advntr.dev/text-logger-slf4j/$adventureVersion/", - "https://javadoc.io/doc/org.slf4j/slf4j-api/$slf4jVersion/", -- "https://javadoc.io/doc/org.apache.logging.log4j/log4j-api/$log4jVersion/", -+ "https://javadoc.io/doc/org.apache.logging.log4j/log4j-api/2.20.0", // DivineMC - Bump Dependencies - // Paper end -- "https://javadoc.io/doc/org.apache.maven.resolver/maven-resolver-api/1.7.3", // Paper -+ "https://javadoc.io/doc/org.apache.maven.resolver/maven-resolver-api/1.9.19", // Paper // DivineMC - Bump Dependencies - ) - options.tags("apiNote:a:API Note:") - diff --git a/patches/server/0002-Divine-Configuration.patch b/patches/server/0002-Divine-Configuration.patch deleted file mode 100644 index 3e61b31..0000000 --- a/patches/server/0002-Divine-Configuration.patch +++ /dev/null @@ -1,437 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 27 Mar 2023 13:16:41 +0300 -Subject: [PATCH] Divine Configuration - - -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index 7b312fa229bc03df45c9d2fa340396f62570356a..c9fc57d6b00bf5ca57805c9ad2829646de347475 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -57,6 +57,7 @@ import net.minecraft.world.level.GameType; - import net.minecraft.world.level.block.entity.SkullBlockEntity; - import net.minecraft.world.level.storage.LevelStorageSource; - import org.slf4j.Logger; -+import space.bxteam.divinemc.configuration.DivineConfig; // DivineMC - - // CraftBukkit start - import net.minecraft.server.WorldLoader; -@@ -259,6 +260,16 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - gg.pufferfish.pufferfish.PufferfishConfig.load(); // Pufferfish - gg.pufferfish.pufferfish.PufferfishCommand.init(); // Pufferfish - -+ // DivineMC start -+ try { -+ DivineConfig.init((java.io.File) options.valueOf("divinemc-settings")); -+ } catch (Exception e) { -+ DedicatedServer.LOGGER.error("Unable to load server configuration", e); -+ return false; -+ } -+ DivineConfig.registerCommands(); -+ // DivineMC end -+ - this.setPvpAllowed(dedicatedserverproperties.pvp); - this.setFlightAllowed(dedicatedserverproperties.allowFlight); - this.setMotd(dedicatedserverproperties.motd); -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 72c9b9d8f520aa29106dd28a48dcdaf9bf8ea9e6..d152815ed2b12334458eba1eb88171d563169e84 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -176,6 +176,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - - public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray - public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur - Purpur config files -+ public final space.bxteam.divinemc.configuration.DivineWorldConfig divinemcConfig; // DivineMC - public static BlockPos lastPhysicsProblem; // Spigot - private org.spigotmc.TickLimiter entityLimiter; - private org.spigotmc.TickLimiter tileLimiter; -@@ -888,6 +889,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot - this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config - this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), env); // Purpur - Purpur config files -+ this.divinemcConfig = new space.bxteam.divinemc.configuration.DivineWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), env); // DivineMC - this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); // Purpur - this.generator = gen; - this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index badf280a6b01b06e8148c552330872d64e6256b7..c86dd96f7abfd9fe73f9a044689f1de7374d6a48 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1101,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); - org.purpurmc.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur - Purpur config files -+ space.bxteam.divinemc.configuration.DivineConfig.init((File) console.options.valueOf("divinemc-settings")); // DivineMC - for (ServerLevel world : this.console.getAllLevels()) { - // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty - world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) -@@ -1117,6 +1118,7 @@ public final class CraftServer implements Server { - } - world.spigotConfig.init(); // Spigot - world.purpurConfig.init(); // Purpur - Purpur config files -+ world.divinemcConfig.init(); // DivineMC - } - - Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper -@@ -1135,6 +1137,7 @@ public final class CraftServer implements Server { - io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper - this.spark.registerCommandBeforePlugins(this); // Paper - spark - org.purpurmc.purpur.PurpurConfig.registerCommands(); // Purpur - Purpur config files -+ space.bxteam.divinemc.configuration.DivineConfig.registerCommands(); // DivineMC - this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); - this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); - -@@ -3113,6 +3116,13 @@ public final class CraftServer implements Server { - } - // Purpur end - Purpur config files - -+ // DivineMC start -+ @Override -+ public YamlConfiguration getDivineConfig() { -+ return space.bxteam.divinemc.configuration.DivineConfig.config; -+ } -+ // DivineMC end -+ - @Override - public void restart() { - org.spigotmc.RestartCommand.restart(); -diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java -index 22380501e8cdc09ff785c188d68601a0030fd921..b4a4bc186086e8008e0e3279364d71a1ca6f097c 100644 ---- a/src/main/java/org/bukkit/craftbukkit/Main.java -+++ b/src/main/java/org/bukkit/craftbukkit/Main.java -@@ -190,6 +190,14 @@ public class Main { - .describedAs("Yml file"); - // Purpur end - Purpur config files - -+ // DivineMC start -+ acceptsAll(asList("divinemc", "divinemc-settings"), "File for divinemc settings") -+ .withRequiredArg() -+ .ofType(File.class) -+ .defaultsTo(new File("divinemc.yml")) -+ .describedAs("Yml file"); -+ // DivineMC end -+ - // Paper start - acceptsAll(asList("server-name"), "Name of the server") - .withRequiredArg() -diff --git a/src/main/java/space/bxteam/divinemc/commands/DivineCommand.java b/src/main/java/space/bxteam/divinemc/commands/DivineCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..06af4af987859081bcf23e91da02b295ec31eba4 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/commands/DivineCommand.java -@@ -0,0 +1,66 @@ -+package space.bxteam.divinemc.commands; -+ -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.level.ServerLevel; -+import space.bxteam.divinemc.configuration.DivineConfig; -+import org.bukkit.ChatColor; -+import org.bukkit.Location; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandSender; -+ -+import java.io.File; -+import java.util.Collections; -+import java.util.List; -+import java.util.stream.Collectors; -+import java.util.stream.Stream; -+ -+public class DivineCommand extends Command { -+ public DivineCommand(String name) { -+ super(name); -+ this.description = "DivineMC related commands"; -+ this.usageMessage = "/divinemc [reload | version]"; -+ this.setPermission("bukkit.command.divinemc"); -+ } -+ -+ @Override -+ public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { -+ if (args.length == 1) { -+ return Stream.of("reload", "version") -+ .filter(arg -> arg.startsWith(args[0].toLowerCase())) -+ .collect(Collectors.toList()); -+ } -+ return Collections.emptyList(); -+ } -+ -+ @Override -+ public boolean execute(CommandSender sender, String commandLabel, String[] args) { -+ if (!testPermission(sender)) return true; -+ -+ if (args.length != 1) { -+ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); -+ return false; -+ } -+ -+ if (args[0].equalsIgnoreCase("reload")) { -+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues."); -+ Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server."); -+ -+ MinecraftServer console = MinecraftServer.getServer(); -+ DivineConfig.init((File) console.options.valueOf("divinemc-settings")); -+ for (ServerLevel level : console.getAllLevels()) { -+ level.divinemcConfig.init(); -+ level.resetBreedingCooldowns(); -+ } -+ console.server.reloadCount++; -+ -+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "DivineMC config reload complete."); -+ } else if (args[0].equalsIgnoreCase("version")) { -+ Command verCmd = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version"); -+ if (verCmd != null) { -+ return verCmd.execute(sender, commandLabel, new String[0]); -+ } -+ } -+ -+ return true; -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -new file mode 100644 -index 0000000000000000000000000000000000000000..27c45545e1e9ad6ea88e65c884c5a4955fc1c80c ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -0,0 +1,157 @@ -+package space.bxteam.divinemc.configuration; -+ -+import com.google.common.base.Throwables; -+import com.google.common.collect.ImmutableMap; -+import net.minecraft.world.level.block.Block; -+import net.minecraft.world.level.block.state.BlockBehaviour; -+import space.bxteam.divinemc.commands.*; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.world.level.block.Blocks; -+import org.bukkit.Bukkit; -+import org.bukkit.command.Command; -+import org.bukkit.configuration.ConfigurationSection; -+import org.bukkit.configuration.InvalidConfigurationException; -+import org.bukkit.configuration.file.YamlConfiguration; -+ -+import java.io.File; -+import java.io.IOException; -+import java.lang.reflect.InvocationTargetException; -+import java.lang.reflect.Method; -+import java.lang.reflect.Modifier; -+import java.util.HashMap; -+import java.util.List; -+import java.util.Map; -+import java.util.logging.Level; -+ -+@SuppressWarnings("unused") -+public class DivineConfig { -+ private static final String HEADER = "This is the main configuration file for DivineMC.\n" -+ + "If you need help with the configuration or have any questions related to DivineMC,\n" -+ + "join us in our Discord server.\n" -+ + "\n" -+ + "Discord: https://discord.gg/p7cxhw7E2M \n" -+ + "Docs: https://docs.bx-team.space/divinemc \n" -+ + "New builds: https://github.com/DivineMC/DivineMC/releases/latest"; -+ private static File CONFIG_FILE; -+ public static YamlConfiguration config; -+ -+ private static Map commands; -+ -+ public static int version; -+ static boolean verbose; -+ -+ public static void init(File configFile) { -+ CONFIG_FILE = configFile; -+ config = new YamlConfiguration(); -+ try { -+ config.load(CONFIG_FILE); -+ } catch (IOException ignore) { -+ } catch (InvalidConfigurationException ex) { -+ Bukkit.getLogger().log(Level.SEVERE, "Could not load divinemc.yml, please correct your syntax errors", ex); -+ throw Throwables.propagate(ex); -+ } -+ config.options().header(HEADER); -+ config.options().copyDefaults(true); -+ verbose = getBoolean("verbose", false); -+ -+ commands = new HashMap<>(); -+ commands.put("divinemc", new DivineCommand("divinemc")); -+ -+ version = getInt("config-version", 3); -+ set("config-version", 3); -+ -+ readConfig(DivineConfig.class, null); -+ -+ Block.BLOCK_STATE_REGISTRY.forEach(BlockBehaviour.BlockStateBase::initCache); -+ } -+ -+ protected static void log(String s) { -+ if (verbose) { -+ log(Level.INFO, s); -+ } -+ } -+ -+ protected static void log(Level level, String s) { -+ Bukkit.getLogger().log(level, s); -+ } -+ -+ public static void registerCommands() { -+ for (Map.Entry entry : commands.entrySet()) { -+ MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "DivineMC", entry.getValue()); -+ } -+ } -+ -+ static void readConfig(Class clazz, Object instance) { -+ for (Method method : clazz.getDeclaredMethods()) { -+ if (Modifier.isPrivate(method.getModifiers())) { -+ if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { -+ try { -+ method.setAccessible(true); -+ method.invoke(instance); -+ } catch (InvocationTargetException ex) { -+ throw Throwables.propagate(ex.getCause()); -+ } catch (Exception ex) { -+ Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex); -+ } -+ } -+ } -+ } -+ -+ try { -+ config.save(CONFIG_FILE); -+ } catch (IOException ex) { -+ Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex); -+ } -+ } -+ -+ private static void set(String path, Object val) { -+ config.addDefault(path, val); -+ config.set(path, val); -+ } -+ -+ private static String getString(String path, String def) { -+ config.addDefault(path, def); -+ return config.getString(path, config.getString(path)); -+ } -+ -+ private static boolean getBoolean(String path, boolean def) { -+ config.addDefault(path, def); -+ return config.getBoolean(path, config.getBoolean(path)); -+ } -+ -+ private static double getDouble(String path, double def) { -+ config.addDefault(path, def); -+ return config.getDouble(path, config.getDouble(path)); -+ } -+ -+ private static int getInt(String path, int def) { -+ config.addDefault(path, def); -+ return config.getInt(path, config.getInt(path)); -+ } -+ -+ private static List getList(String path, T def) { -+ config.addDefault(path, def); -+ return config.getList(path, config.getList(path)); -+ } -+ -+ static Map getMap(String path, Map def) { -+ if (def != null && config.getConfigurationSection(path) == null) { -+ config.addDefault(path, def); -+ return def; -+ } -+ return toMap(config.getConfigurationSection(path)); -+ } -+ -+ private static Map toMap(ConfigurationSection section) { -+ ImmutableMap.Builder builder = ImmutableMap.builder(); -+ if (section != null) { -+ for (String key : section.getKeys(false)) { -+ Object obj = section.get(key); -+ if (obj != null) { -+ builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj); -+ } -+ } -+ } -+ return builder.build(); -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3e64e0d2d925f9fca1eb38cc07c20cae785ef39f ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -@@ -0,0 +1,80 @@ -+package space.bxteam.divinemc.configuration; -+ -+import org.apache.commons.lang.BooleanUtils; -+import org.bukkit.World; -+import org.bukkit.configuration.ConfigurationSection; -+ -+import java.util.List; -+import java.util.Map; -+import java.util.function.Predicate; -+ -+import static space.bxteam.divinemc.configuration.DivineConfig.log; -+ -+@SuppressWarnings("unused") -+public class DivineWorldConfig { -+ -+ private final String worldName; -+ private final World.Environment environment; -+ -+ public DivineWorldConfig(String worldName, World.Environment environment) { -+ this.worldName = worldName; -+ this.environment = environment; -+ init(); -+ } -+ -+ public void init() { -+ log("-------- World Settings For [" + worldName + "] --------"); -+ DivineConfig.readConfig(DivineWorldConfig.class, this); -+ } -+ -+ private void set(String path, Object val) { -+ DivineConfig.config.addDefault("world-settings.default." + path, val); -+ DivineConfig.config.set("world-settings.default." + path, val); -+ if (DivineConfig.config.get("world-settings." + worldName + "." + path) != null) { -+ DivineConfig.config.addDefault("world-settings." + worldName + "." + path, val); -+ DivineConfig.config.set("world-settings." + worldName + "." + path, val); -+ } -+ } -+ -+ private ConfigurationSection getConfigurationSection(String path) { -+ ConfigurationSection section = DivineConfig.config.getConfigurationSection("world-settings." + worldName + "." + path); -+ return section != null ? section : DivineConfig.config.getConfigurationSection("world-settings.default." + path); -+ } -+ -+ private String getString(String path, String def) { -+ DivineConfig.config.addDefault("world-settings.default." + path, def); -+ return DivineConfig.config.getString("world-settings." + worldName + "." + path, DivineConfig.config.getString("world-settings.default." + path)); -+ } -+ -+ private boolean getBoolean(String path, boolean def) { -+ DivineConfig.config.addDefault("world-settings.default." + path, def); -+ return DivineConfig.config.getBoolean("world-settings." + worldName + "." + path, DivineConfig.config.getBoolean("world-settings.default." + path)); -+ } -+ -+ private boolean getBoolean(String path, Predicate predicate) { -+ String val = getString(path, "default").toLowerCase(); -+ Boolean bool = BooleanUtils.toBooleanObject(val, "true", "false", "default"); -+ return predicate.test(bool); -+ } -+ -+ private double getDouble(String path, double def) { -+ DivineConfig.config.addDefault("world-settings.default." + path, def); -+ return DivineConfig.config.getDouble("world-settings." + worldName + "." + path, DivineConfig.config.getDouble("world-settings.default." + path)); -+ } -+ -+ private int getInt(String path, int def) { -+ DivineConfig.config.addDefault("world-settings.default." + path, def); -+ return DivineConfig.config.getInt("world-settings." + worldName + "." + path, DivineConfig.config.getInt("world-settings.default." + path)); -+ } -+ -+ private List getList(String path, T def) { -+ DivineConfig.config.addDefault("world-settings.default." + path, def); -+ return DivineConfig.config.getList("world-settings." + worldName + "." + path, DivineConfig.config.getList("world-settings.default." + path)); -+ } -+ -+ private Map getMap(String path, Map def) { -+ final Map fallback = DivineConfig.getMap("world-settings.default." + path, def); -+ final Map value = DivineConfig.getMap("world-settings." + worldName + "." + path, null); -+ return value.isEmpty() ? fallback : value; -+ } -+} diff --git a/patches/server/0004-Add-missing-purpur-config-options.patch b/patches/server/0004-Add-missing-purpur-config-options.patch deleted file mode 100644 index 8d58d0e..0000000 --- a/patches/server/0004-Add-missing-purpur-config-options.patch +++ /dev/null @@ -1,328 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 12 Jun 2023 15:31:21 +0300 -Subject: [PATCH] Add missing purpur config options - - -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 67ff60909dee395d42619c310bafc381b9bfce5d..d1aabc4183023136de6bd12f14742b0aeafb8f60 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 -@@ -163,6 +163,19 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS - this.getAttribute(Attributes.SCALE).setBaseValue(this.level().purpurConfig.allayScale); - } - // Purpur end - Configurable entity base attributes -+ -+ // DivineMC start - Add missing purpur config options -+ @Override -+ public boolean isSensitiveToWater() { -+ return level().purpurConfig.allayTakeDamageFromWater; -+ } -+ -+ @Override -+ public boolean isAlwaysExperienceDropper() { -+ return level().purpurConfig.allayAlwaysDropExp; -+ } -+ // DivineMC end -+ - @Override - protected Brain.Provider brainProvider() { - return Brain.provider(Allay.MEMORY_TYPES, Allay.SENSOR_TYPES); -diff --git a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -index d0023e3734bb3c625fa53077f47039dcb82d9606..1e62c6249df7864cc836b7f791fb11c65d6bdc9b 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -+++ b/src/main/java/net/minecraft/world/entity/animal/camel/Camel.java -@@ -100,6 +100,19 @@ public class Camel extends AbstractHorse { - return this.level().purpurConfig.camelBreedingTicks; - } - // Purpur end - Make entity breeding times configurable -+ -+ // DivineMC start - Add missing purpur config options -+ @Override -+ public boolean isSensitiveToWater() { -+ return level().purpurConfig.camelTakeDamageFromWater; -+ } -+ -+ @Override -+ public boolean isAlwaysExperienceDropper() { -+ return level().purpurConfig.camelAlwaysDropExp; -+ } -+ // DivineMC end -+ - @Override - public void addAdditionalSaveData(CompoundTag nbt) { - super.addAdditionalSaveData(nbt); -diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -index d09aa48e20c9a6e0d465b93e3759556638041394..2ce47ff58e5db6fc5387667bc8b02fdef73f9f7c 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -@@ -165,6 +165,23 @@ public class Frog extends Animal implements VariantHolder> { - } - // Purpur end - Ridables - -+ // DivineMC start - Add missing purpur config options -+ @Override -+ public boolean isSensitiveToWater() { -+ return level().purpurConfig.frogTakeDamageFromWater; -+ } -+ -+ @Override -+ public boolean isAlwaysExperienceDropper() { -+ return level().purpurConfig.frogAlwaysDropExp; -+ } -+ -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(level().purpurConfig.frogMaxHealth); -+ } -+ // DivineMC end -+ - // Purpur start - Make entity breeding times configurable - @Override - public int getPurpurBreedTime() { -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 33429a9afeefce9238969b2894d0a9c033baca51..b504df1a0004f09eb51ee64461b136934a60c2bc 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 -@@ -95,6 +95,23 @@ public class Tadpole extends AbstractFish { - } - // Purpur end - Ridables - -+ // DivineMC start - Add missing purpur config options -+ @Override -+ public boolean isSensitiveToWater() { -+ return level().purpurConfig.tadpoleTakeDamageFromWater; -+ } -+ -+ @Override -+ public boolean isAlwaysExperienceDropper() { -+ return level().purpurConfig.tadpoleAlwaysDropExp; -+ } -+ -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(level().purpurConfig.tadpoleMaxHealth); -+ } -+ // DivineMC end -+ - @Override - protected PathNavigation createNavigation(Level world) { - return new WaterBoundPathNavigation(this, world); -diff --git a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java -index 489854333e71ad4e7635870eedbdd07a84297a13..161ca711bcc4c6e9d5ac25b61154cf6b8a437d52 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java -+++ b/src/main/java/net/minecraft/world/entity/animal/sniffer/Sniffer.java -@@ -120,6 +120,19 @@ public class Sniffer extends Animal { - return this.level().purpurConfig.snifferBreedingTicks; - } - // Purpur end - Make entity breeding times configurable -+ -+ // DivineMC start - Add missing purpur config options -+ @Override -+ public boolean isSensitiveToWater() { -+ return level().purpurConfig.snifferTakeDamageFromWater; -+ } -+ -+ @Override -+ public boolean isAlwaysExperienceDropper() { -+ return level().purpurConfig.snifferAlwaysDropExp; -+ } -+ // DivineMC end -+ - @Override - protected void defineSynchedData(SynchedEntityData.Builder builder) { - super.defineSynchedData(builder); -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 74011f1ab7e48490109ad93d658bba216eef9e80..6d8c7639c6a264f40b2c402684c54cb694c05c63 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 -@@ -153,6 +153,23 @@ public class Warden extends Monster implements VibrationSystem { - } - // Purpur end - Ridables - -+ // DivineMC start - Add missing purpur config options -+ @Override -+ public boolean isSensitiveToWater() { -+ return level().purpurConfig.wardenTakeDamageFromWater; -+ } -+ -+ @Override -+ public boolean isAlwaysExperienceDropper() { -+ return level().purpurConfig.wardenAlwaysDropExp; -+ } -+ -+ @Override -+ public void initAttributes() { -+ this.getAttribute(Attributes.MAX_HEALTH).setBaseValue(level().purpurConfig.wardenMaxHealth); -+ } -+ // DivineMC end -+ - @Override - public Packet getAddEntityPacket(ServerEntity entityTrackerEntry) { - return new ClientboundAddEntityPacket(this, entityTrackerEntry, this.hasPose(Pose.EMERGING) ? 1 : 0); -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractChestBoat.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractChestBoat.java -index 1f4cc08e84a23213bb9786ea09ad77caeec2d336..a9941d8b9dbe80f77f27c2166f8c2ec9f34077c4 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractChestBoat.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractChestBoat.java -@@ -35,10 +35,10 @@ import org.bukkit.entity.HumanEntity; - import org.bukkit.event.entity.EntityRemoveEvent; - import org.bukkit.inventory.InventoryHolder; - // CraftBukkit end -+import org.purpurmc.purpur.PurpurConfig; // DivineMC - Add missing purpur config options - - public abstract class AbstractChestBoat extends AbstractBoat implements HasCustomInventoryScreen, ContainerEntity { -- -- private static final int CONTAINER_SIZE = 27; -+ private static final int CONTAINER_SIZE = PurpurConfig.chestBoatRows * 9; // DivineMC - Add missing purpur config options - private NonNullList itemStacks; - @Nullable - private ResourceKey lootTable; -@@ -46,7 +46,7 @@ public abstract class AbstractChestBoat extends AbstractBoat implements HasCusto - - public AbstractChestBoat(EntityType type, Level world, Supplier itemSupplier) { - super(type, world, itemSupplier); -- this.itemStacks = NonNullList.withSize(27, ItemStack.EMPTY); -+ this.itemStacks = NonNullList.withSize(PurpurConfig.chestBoatRows * 9, ItemStack.EMPTY); // DivineMC - Add missing purpur config options - } - - @Override -@@ -142,7 +142,7 @@ public abstract class AbstractChestBoat extends AbstractBoat implements HasCusto - - @Override - public int getContainerSize() { -- return 27; -+ return PurpurConfig.chestBoatRows * 9; // DivineMC - Add missing purpur config options - } - - @Override -diff --git a/src/main/java/org/purpurmc/purpur/PurpurConfig.java b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -index 6a161bb00b4665cf0b148bc7eabd8b07c6b07e99..316718a16f1e81d6706f1374ce27075acfa5707f 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurConfig.java -@@ -331,6 +331,7 @@ public class PurpurConfig { - } - - public static int barrelRows = 3; -+ public static int chestBoatRows = 3; // DivineMC - Add missing purpur config options - public static boolean enderChestSixRows = false; - public static boolean enderChestPermissionRows = false; - public static boolean cryingObsidianValidForPortalFrame = false; -@@ -373,6 +374,7 @@ public class PurpurConfig { - case 1 -> 9; - default -> 27; - }); -+ chestBoatRows = getInt("settings.blocks.chest_boat.rows", chestBoatRows); // DivineMC - Add missing purpur config options - enderChestSixRows = getBoolean("settings.blocks.ender_chest.six-rows", enderChestSixRows); - org.bukkit.event.inventory.InventoryType.ENDER_CHEST.setDefaultSize(enderChestSixRows ? 54 : 27); - enderChestPermissionRows = getBoolean("settings.blocks.ender_chest.use-permissions-for-rows", enderChestPermissionRows); -diff --git a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -index 0d5a0e14cbaacc63eeced78a6c28cc64ad918522..7425854b0230d6c3f8d58a5d049a883dc9f0c7e7 100644 ---- a/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -+++ b/src/main/java/org/purpurmc/purpur/PurpurWorldConfig.java -@@ -1144,12 +1144,20 @@ public class PurpurWorldConfig { - public boolean allayControllable = true; - public double allayMaxHealth = 20.0D; - public double allayScale = 1.0D; -+ // DivineMC start - Add missing purpur config options -+ public boolean allayTakeDamageFromWater = false; -+ public boolean allayAlwaysDropExp = false; -+ // DivineMC end - private void allaySettings() { - allayRidable = getBoolean("mobs.allay.ridable", allayRidable); - allayRidableInWater = getBoolean("mobs.allay.ridable-in-water", allayRidableInWater); - allayControllable = getBoolean("mobs.allay.controllable", allayControllable); - allayMaxHealth = getDouble("mobs.allay.attributes.max_health", allayMaxHealth); - allayScale = Mth.clamp(getDouble("mobs.allay.attributes.scale", allayScale), 0.0625D, 16.0D); -+ // DivineMC start - Add missing purpur config options -+ allayTakeDamageFromWater = getBoolean("mobs.allay.take-damage-from-water", allayTakeDamageFromWater); -+ allayAlwaysDropExp = getBoolean("mobs.allay.always-drop-exp", allayAlwaysDropExp); -+ // DivineMC end - } - - public boolean armadilloRidable = false; -@@ -1299,7 +1307,15 @@ public class PurpurWorldConfig { - public double camelMovementSpeedMin = 0.09D; - public double camelMovementSpeedMax = 0.09D; - public int camelBreedingTicks = 6000; -+ // DivineMC start - Add missing purpur config options -+ public boolean camelTakeDamageFromWater = false; -+ public boolean camelAlwaysDropExp = false; -+ // DivineMC end - private void camelSettings() { -+ // DivineMC start - Add missing purpur config options -+ camelTakeDamageFromWater = getBoolean("mobs.camel.takes-damage-from-water", camelTakeDamageFromWater); -+ camelAlwaysDropExp = getBoolean("mobs.camel.always-drop-exp", camelAlwaysDropExp); -+ // DivineMC end - camelRidableInWater = getBoolean("mobs.camel.ridable-in-water", camelRidableInWater); - camelMaxHealthMin = getDouble("mobs.camel.attributes.max_health.min", camelMaxHealthMin); - camelMaxHealthMax = getDouble("mobs.camel.attributes.max_health.max", camelMaxHealthMax); -@@ -1764,7 +1780,17 @@ public class PurpurWorldConfig { - public boolean frogControllable = true; - public float frogRidableJumpHeight = 0.65F; - public int frogBreedingTicks = 6000; -+ // DivineMC start - Add missing purpur config options -+ public double frogMaxHealth = 10.0D; -+ public boolean frogTakeDamageFromWater = false; -+ public boolean frogAlwaysDropExp = false; -+ // DivineMC end - private void frogSettings() { -+ // DivineMC start - Add missing purpur config options -+ frogMaxHealth = getDouble("mobs.frog.attributes.max_health", frogMaxHealth); -+ frogTakeDamageFromWater = getBoolean("mobs.frog.takes-damage-from-water", frogTakeDamageFromWater); -+ frogAlwaysDropExp = getBoolean("mobs.frog.always-drop-exp", frogAlwaysDropExp); -+ // DivineMC end - frogRidable = getBoolean("mobs.frog.ridable", frogRidable); - frogRidableInWater = getBoolean("mobs.frog.ridable-in-water", frogRidableInWater); - frogControllable = getBoolean("mobs.frog.controllable", frogControllable); -@@ -2776,7 +2802,15 @@ public class PurpurWorldConfig { - public double snifferMaxHealth = 14.0D; - public double snifferScale = 1.0D; - public int snifferBreedingTicks = 6000; -+ // DivineMC start - Add missing purpur config options -+ public boolean snifferTakeDamageFromWater = false; -+ public boolean snifferAlwaysDropExp = false; -+ // DivineMC end - private void snifferSettings() { -+ // DivineMC start - Add missing purpur config options -+ snifferTakeDamageFromWater = getBoolean("mobs.sniffer.takes-damage-from-water", snifferTakeDamageFromWater); -+ snifferAlwaysDropExp = getBoolean("mobs.sniffer.always-drop-exp", snifferAlwaysDropExp); -+ // DivineMC end - snifferRidable = getBoolean("mobs.sniffer.ridable", snifferRidable); - snifferRidableInWater = getBoolean("mobs.sniffer.ridable-in-water", snifferRidableInWater); - snifferControllable = getBoolean("mobs.sniffer.controllable", snifferControllable); -@@ -2884,7 +2918,17 @@ public class PurpurWorldConfig { - public boolean tadpoleRidable = false; - public boolean tadpoleRidableInWater = true; - public boolean tadpoleControllable = true; -+ // DivineMC start - Add missing purpur config options -+ public double tadpoleMaxHealth = 10.0D; -+ public boolean tadpoleTakeDamageFromWater = false; -+ public boolean tadpoleAlwaysDropExp = false; -+ // DivineMC end - private void tadpoleSettings() { -+ // DivineMC start - Add missing purpur config options -+ tadpoleMaxHealth = getDouble("mobs.tadpole.attributes.max_health", tadpoleMaxHealth); -+ tadpoleTakeDamageFromWater = getBoolean("mobs.tadpole.takes-damage-from-water", tadpoleTakeDamageFromWater); -+ tadpoleAlwaysDropExp = getBoolean("mobs.tadpole.always-drop-exp", tadpoleAlwaysDropExp); -+ // DivineMC end - tadpoleRidable = getBoolean("mobs.tadpole.ridable", tadpoleRidable); - tadpoleRidableInWater = getBoolean("mobs.tadpole.ridable-in-water", tadpoleRidableInWater); - tadpoleControllable = getBoolean("mobs.tadpole.controllable", tadpoleControllable); -@@ -3116,7 +3160,17 @@ public class PurpurWorldConfig { - public boolean wardenRidable = false; - public boolean wardenRidableInWater = true; - public boolean wardenControllable = true; -+ // DivineMC start - Add missing purpur config options -+ public double wardenMaxHealth = 500.0D; -+ public boolean wardenTakeDamageFromWater = false; -+ public boolean wardenAlwaysDropExp = false; -+ // DivineMC end - private void wardenSettings() { -+ // DivineMC start - Add missing purpur config options -+ wardenMaxHealth = getDouble("mobs.warden.attributes.max_health", wardenMaxHealth); -+ wardenTakeDamageFromWater = getBoolean("mobs.warden.takes-damage-from-water", wardenTakeDamageFromWater); -+ wardenAlwaysDropExp = getBoolean("mobs.warden.always-drop-exp", wardenAlwaysDropExp); -+ // DivineMC end - wardenRidable = getBoolean("mobs.warden.ridable", wardenRidable); - wardenRidableInWater = getBoolean("mobs.warden.ridable-in-water", wardenRidableInWater); - wardenControllable = getBoolean("mobs.warden.controllable", wardenControllable); diff --git a/patches/server/0005-Fix-MC-93826.patch b/patches/server/0005-Fix-MC-93826.patch deleted file mode 100644 index 0382740..0000000 --- a/patches/server/0005-Fix-MC-93826.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 10 Jun 2023 12:44:58 +0300 -Subject: [PATCH] Fix MC-93826 - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Animal.java b/src/main/java/net/minecraft/world/entity/animal/Animal.java -index 8df1682dca61b0f771a1c08f5bcd797506badf19..389950c97eb94fbbc730fbff51fedcc3217be22a 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Animal.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Animal.java -@@ -51,6 +51,8 @@ public abstract class Animal extends AgeableMob { - public ItemStack breedItem; // CraftBukkit - Add breedItem variable - public abstract int getPurpurBreedTime(); // Purpur - Make entity breeding times configurable - -+ private Object level; -+ - protected Animal(EntityType type, Level world) { - super(type, world); - this.setPathfindingMalus(PathType.DANGER_FIRE, 16.0F); -@@ -84,7 +86,11 @@ public abstract class Animal extends AgeableMob { - double d1 = this.random.nextGaussian() * 0.02D; - double d2 = this.random.nextGaussian() * 0.02D; - -- this.level().addParticle(ParticleTypes.HEART, this.getRandomX(1.0D), this.getRandomY() + 0.5D, this.getRandomZ(1.0D), d0, d1, d2); -+ // DivineMC start - Fix MC-93826 -+ if (this.level instanceof ServerLevel serverLevel) { -+ serverLevel.sendParticles(ParticleTypes.HEART, this.getRandomX(1.0D), this.getRandomY() + 0.5D, this.getRandomZ(1.0D), 1, d0, d1, d2, 0); -+ } -+ // DivineMC end - } - } - diff --git a/patches/server/0006-Fix-MC-110386.patch b/patches/server/0006-Fix-MC-110386.patch deleted file mode 100644 index 71168ed..0000000 --- a/patches/server/0006-Fix-MC-110386.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 1 Apr 2023 00:41:04 +0300 -Subject: [PATCH] Fix MC-110386 - -See https://bugs.mojang.com/browse/MC-110386 - -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java -index edfc77ba9c5c7dc4cfe997faff7a34e9b9e9365e..f1dc334ce16f92b1f4c165b931bb322e88646bf9 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/target/HurtByTargetGoal.java -@@ -114,6 +114,7 @@ public class HurtByTargetGoal extends TargetGoal { - } - - protected void alertOther(Mob mob, LivingEntity target) { -+ if (mob == target) return; // DivineMC - Fix MC-110386 - mob.setTarget(target, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY, true); // CraftBukkit - reason - } - } diff --git a/patches/server/0007-Fix-MC-31819.patch b/patches/server/0007-Fix-MC-31819.patch deleted file mode 100644 index 7539969..0000000 --- a/patches/server/0007-Fix-MC-31819.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 10 Jun 2023 13:17:01 +0300 -Subject: [PATCH] Fix MC-31819 - - -diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java -index d19b1e127a1018a798eed181d8060a781b589f90..a861e84b959e6f07a51c72b25ea4df0b5df7fd34 100644 ---- a/src/main/java/net/minecraft/world/entity/player/Player.java -+++ b/src/main/java/net/minecraft/world/entity/player/Player.java -@@ -1896,6 +1896,11 @@ public abstract class Player extends LivingEntity { - } - - public void causeFoodExhaustion(float f, EntityExhaustionEvent.ExhaustionReason reason) { -+ // DivineMC start - Fix MC-31819 -+ if (this.level().getDifficulty() == Difficulty.PEACEFUL) { -+ return; -+ } -+ // DivineMC end - // CraftBukkit end - if (!this.abilities.invulnerable) { - if (!this.level().isClientSide) { diff --git a/patches/server/0008-Fix-MC-172801.patch b/patches/server/0008-Fix-MC-172801.patch deleted file mode 100644 index 9ab3d67..0000000 --- a/patches/server/0008-Fix-MC-172801.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 27 Jan 2024 16:56:27 +0300 -Subject: [PATCH] Fix MC-172801 - -Original post on Mojira: https://bugs.mojang.com/browse/MC-172801 - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 33fbe0f7a356dd847e546feffb468b2803c6744b..84ac9ee926a9a4b4d2dcd107cb089162637a2576 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -3350,7 +3350,13 @@ public abstract class LivingEntity extends Entity implements Attackable { - } - - protected float getFlyingSpeed() { -- return this.getControllingPassenger() instanceof net.minecraft.world.entity.player.Player ? this.getSpeed() * 0.1F : 0.02F; -+ // DivineMC start - Fix MC-172801 -+ float flyingSpeed = 0.02F; -+ if (this.getAttributes().hasAttribute(Attributes.FLYING_SPEED)) { -+ flyingSpeed = (float) (this.getAttribute(Attributes.FLYING_SPEED).getValue() * 0.049999999254942D); -+ } -+ return this.getControllingPassenger() instanceof net.minecraft.world.entity.player.Player ? this.getSpeed() * 0.1F : flyingSpeed; -+ // DivineMC end - } - - public float getSpeed() { diff --git a/patches/server/0010-Fix-MC-7569.patch b/patches/server/0010-Fix-MC-7569.patch deleted file mode 100644 index 8820936..0000000 --- a/patches/server/0010-Fix-MC-7569.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sun, 16 Jul 2023 12:21:09 +0300 -Subject: [PATCH] Fix MC-7569 - -Original post on Mojira: https://bugs.mojang.com/browse/MC-7569 - -diff --git a/src/main/java/net/minecraft/server/rcon/RconConsoleSource.java b/src/main/java/net/minecraft/server/rcon/RconConsoleSource.java -index a04cf04cb694237ec77b38fc576f0dfc9264a25a..47a00e408d96fd9ade864ec4e278e1eb006722ef 100644 ---- a/src/main/java/net/minecraft/server/rcon/RconConsoleSource.java -+++ b/src/main/java/net/minecraft/server/rcon/RconConsoleSource.java -@@ -55,7 +55,7 @@ public class RconConsoleSource implements CommandSource { - - @Override - public void sendSystemMessage(Component message) { -- this.buffer.append(message.getString()); -+ this.buffer.append(message.getString()).append(System.lineSeparator()); - } - - @Override diff --git a/patches/server/0012-vmp-skip-entity-move-if-movement-is-zero.patch b/patches/server/0012-vmp-skip-entity-move-if-movement-is-zero.patch deleted file mode 100644 index 02c28ad..0000000 --- a/patches/server/0012-vmp-skip-entity-move-if-movement-is-zero.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Fri, 26 Jan 2024 17:42:42 +0300 -Subject: [PATCH] vmp: skip entity move if movement is zero - -Original code by RelativityMC, licensed under MIT -You can find the original code on https://github.com/RelativityMC/VMP-fabric (Yarn mappings) - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index 7fba17c4802159c50a57f23820a153dcd85a2470..5c3c38c6c5d1ac06848f1709a195c28fb7a3d2a2 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -282,6 +282,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - public float yRotO; - public float xRotO; - private AABB bb; -+ private boolean boundingBoxChanged = false; // DivineMC - vmp: skip entity move if movement is zero - public boolean onGround; - public boolean horizontalCollision; - public boolean verticalCollision; -@@ -1197,6 +1198,12 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - // Paper end - detailed watchdog information - - public void move(MoverType type, Vec3 movement) { -+ // DivineMC start - vmp: skip entity move if movement is zero -+ if (!boundingBoxChanged && movement.equals(Vec3.ZERO)) { -+ boundingBoxChanged = false; -+ return; -+ } -+ // DivineMC end - final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity - // Paper start - detailed watchdog information - ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main"); -@@ -4502,6 +4509,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - } - - public final void setBoundingBox(AABB boundingBox) { -+ if (!this.bb.equals(boundingBox)) boundingBoxChanged = true; // DivineMC - vmp: skip entity move if movement is zero - // CraftBukkit start - block invalid bounding boxes - double minX = boundingBox.minX, - minY = boundingBox.minY, diff --git a/patches/server/0013-vmp-use-linked-map-for-entity-trackers-for-faster-it.patch b/patches/server/0013-vmp-use-linked-map-for-entity-trackers-for-faster-it.patch deleted file mode 100644 index 0de152e..0000000 --- a/patches/server/0013-vmp-use-linked-map-for-entity-trackers-for-faster-it.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sun, 14 Jan 2024 14:50:10 +0300 -Subject: [PATCH] vmp: use linked map for entity trackers for faster iteration - -Original code by RelativityMC, licensed under MIT -You can find the original code on https://github.com/RelativityMC/VMP-fabric (Yarn mappings) - -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 5b3a886c624b36557cbfaccdc3fb05a46a4ba36a..b3f5d66ec02953c7ed8c2c05af6f77bc6bb83d1d 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -8,6 +8,7 @@ import com.google.common.collect.Queues; - import com.google.common.collect.Sets; - import com.mojang.datafixers.DataFixer; - import com.mojang.logging.LogUtils; -+import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; // DivineMC - vmp: use linked map for entity trackers for faster iteration - import it.unimi.dsi.fastutil.ints.Int2ObjectMap; - import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; - import it.unimi.dsi.fastutil.longs.Long2ByteMap; -@@ -186,7 +187,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.toDrop = new LongOpenHashSet(); - this.tickingGenerated = new AtomicInteger(); - this.playerMap = new PlayerMap(); -- this.entityMap = new Int2ObjectOpenHashMap(); -+ this.entityMap = new Int2ObjectLinkedOpenHashMap<>(); // DivineMC - vmp: use linked map for entity trackers for faster iteration - this.chunkTypeCache = new Long2ByteOpenHashMap(); - // Paper - rewrite chunk system - Path path = session.getDimensionPath(world.dimension()); diff --git a/patches/server/0015-lithium-collections.gamerules.patch b/patches/server/0015-lithium-collections.gamerules.patch deleted file mode 100644 index 698e68b..0000000 --- a/patches/server/0015-lithium-collections.gamerules.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 8 Apr 2023 01:28:01 +0300 -Subject: [PATCH] lithium: collections.gamerules - -Original code by CaffeineMC, licensed under LGPL v3 -You can find the original code on https://github.com/CaffeineMC/lithium-fabric (Yarn mappings) - -diff --git a/src/main/java/net/minecraft/world/level/GameRules.java b/src/main/java/net/minecraft/world/level/GameRules.java -index 467696a78791d65a66eb380e97d33123a5a12e61..ea7b3b2ad418e046e7f3fae3b09a0b76c4a6152f 100644 ---- a/src/main/java/net/minecraft/world/level/GameRules.java -+++ b/src/main/java/net/minecraft/world/level/GameRules.java -@@ -33,6 +33,7 @@ import net.minecraft.server.level.ServerPlayer; - import net.minecraft.world.flag.FeatureFlagSet; - import net.minecraft.world.flag.FeatureFlags; - import org.slf4j.Logger; -+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; - - public class GameRules { - -@@ -168,7 +169,7 @@ public class GameRules { - } - - private GameRules(Map, GameRules.Value> rules, FeatureFlagSet enabledFeatures) { -- this.rules = rules; -+ this.rules = new Object2ObjectOpenHashMap<>(rules); // DivineMC - lithium: collections.gamerules - this.enabledFeatures = enabledFeatures; - - // Paper start - Perf: Use array for gamerule storage diff --git a/patches/server/0016-lithium-collections.entity_by_type.patch b/patches/server/0016-lithium-collections.entity_by_type.patch deleted file mode 100644 index 2888f25..0000000 --- a/patches/server/0016-lithium-collections.entity_by_type.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 13 Jan 2024 20:12:23 +0300 -Subject: [PATCH] lithium: collections.entity_by_type - -Original code by CaffeineMC, licensed under LGPL v3 -You can find the original code on https://github.com/CaffeineMC/lithium-fabric (Yarn mappings) - -diff --git a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java -index 038710ba934a9a57815dfe9f414b98223b848385..f631aa8bc724d0fc899783967417c3e01aac3c9c 100644 ---- a/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java -+++ b/src/main/java/net/minecraft/util/ClassInstanceMultiMap.java -@@ -3,7 +3,6 @@ package net.minecraft.util; - import com.google.common.collect.ImmutableList; - import com.google.common.collect.Iterators; - import com.google.common.collect.Lists; --import com.google.common.collect.Maps; - import java.util.AbstractCollection; - import java.util.Collection; - import java.util.Collections; -@@ -12,9 +11,10 @@ import java.util.List; - import java.util.Map; - import java.util.Map.Entry; - import net.minecraft.Util; -+import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; // DivineMC - - public class ClassInstanceMultiMap extends AbstractCollection { -- private final Map, List> byClass = Maps.newHashMap(); -+ private final Map, List> byClass = new Reference2ReferenceOpenHashMap<>(); // DivineMC - private final Class baseClass; - private final List allInstances = Lists.newArrayList(); - diff --git a/patches/server/0017-lithium-entity.fast_elytra_check-entity.fast_hand_sw.patch b/patches/server/0017-lithium-entity.fast_elytra_check-entity.fast_hand_sw.patch deleted file mode 100644 index 110e5b9..0000000 --- a/patches/server/0017-lithium-entity.fast_elytra_check-entity.fast_hand_sw.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 13 Jan 2024 20:37:54 +0300 -Subject: [PATCH] lithium: entity.fast_elytra_check + entity.fast_hand_swing - -Original code by CaffeineMC, licensed under LGPL v3 -You can find the original code on https://github.com/CaffeineMC/lithium-fabric (Yarn mappings) - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index 84ac9ee926a9a4b4d2dcd107cb089162637a2576..012ab9befd112e26d5f593cac1dba8592c0b0021 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -2832,6 +2832,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - } - - protected void updateSwingTime() { -+ if (!this.swinging && this.swingTime == 0) return; // DivineMC - lithium: entity.fast_hand_swing - int i = this.getCurrentSwingDuration(); - - if (this.swinging) { -@@ -3878,6 +3879,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - protected void updateFallFlying() { - this.checkSlowFallDistance(); - if (!this.level().isClientSide) { -+ if (!this.isFallFlying() && this.fallFlyTicks == 0) return; // DivineMC - lithium: entity.fast_elytra_check - if (!this.canGlide()) { - if (this.getSharedFlag(7) != false && !CraftEventFactory.callToggleGlideEvent(this, false).isCancelled()) // CraftBukkit - this.setSharedFlag(7, false); diff --git a/patches/server/0018-vmp-spawn_density_cap.patch b/patches/server/0018-vmp-spawn_density_cap.patch deleted file mode 100644 index d72792b..0000000 --- a/patches/server/0018-vmp-spawn_density_cap.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Tue, 16 Jan 2024 20:35:06 +0300 -Subject: [PATCH] vmp: spawn_density_cap - -Original code by RelativityMC, licensed under MIT -You can find the original code on https://github.com/RelativityMC/VMP-fabric (Yarn mappings) - -diff --git a/src/main/java/net/minecraft/world/level/LocalMobCapCalculator.java b/src/main/java/net/minecraft/world/level/LocalMobCapCalculator.java -index 2039b16e5e9bc0797b3f31081d221bb8b34a4dc7..4239fa40788fb92211cc52ed5e06236621195018 100644 ---- a/src/main/java/net/minecraft/world/level/LocalMobCapCalculator.java -+++ b/src/main/java/net/minecraft/world/level/LocalMobCapCalculator.java -@@ -3,8 +3,6 @@ package net.minecraft.world.level; - import com.google.common.collect.Maps; - import it.unimi.dsi.fastutil.longs.Long2ObjectMap; - import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; --import it.unimi.dsi.fastutil.objects.Object2IntMap; --import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; - import java.util.List; - import java.util.Map; - import net.minecraft.server.level.ChunkMap; -@@ -42,14 +40,14 @@ public class LocalMobCapCalculator { - } - - static class MobCounts { -- private final Object2IntMap counts = new Object2IntOpenHashMap<>(MobCategory.values().length); -+ private final int[] spawnGroupDensities = new int[MobCategory.values().length]; // DivineMC - vmp: spawn_density_cap - - public void add(MobCategory spawnGroup) { -- this.counts.computeInt(spawnGroup, (group, density) -> density == null ? 1 : density + 1); -+ this.spawnGroupDensities[spawnGroup.ordinal()]++; // DivineMC - vmp: spawn_density_cap - } - - public boolean canSpawn(MobCategory spawnGroup) { -- return this.counts.getOrDefault(spawnGroup, 0) < spawnGroup.getMaxInstancesPerChunk(); -+ return this.spawnGroupDensities[spawnGroup.ordinal()] < spawnGroup.getMaxInstancesPerChunk(); // DivineMC - vmp: spawn_density_cap - } - } - } diff --git a/patches/server/0019-lithium-math.sine_lut.patch b/patches/server/0019-lithium-math.sine_lut.patch deleted file mode 100644 index ccaa794..0000000 --- a/patches/server/0019-lithium-math.sine_lut.patch +++ /dev/null @@ -1,127 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 25 May 2024 18:39:17 +0300 -Subject: [PATCH] lithium: math.sine_lut - - -diff --git a/src/main/java/net/minecraft/util/Mth.java b/src/main/java/net/minecraft/util/Mth.java -index ef93fc37759e0b54387c8a59adc2b19258cfeb7d..f6fd9804c6d8e3c492d3d7ebef2e15538820decc 100644 ---- a/src/main/java/net/minecraft/util/Mth.java -+++ b/src/main/java/net/minecraft/util/Mth.java -@@ -29,7 +29,7 @@ public class Mth { - public static final Vector3f Y_AXIS = new Vector3f(0.0F, 1.0F, 0.0F); - public static final Vector3f X_AXIS = new Vector3f(1.0F, 0.0F, 0.0F); - public static final Vector3f Z_AXIS = new Vector3f(0.0F, 0.0F, 1.0F); -- private static final float[] SIN = Util.make(new float[65536], sineTable -> { -+ public static final float[] SIN = Util.make(new float[65536], sineTable -> { // DivineMC - lithium: math.sine_lut - for (int ix = 0; ix < sineTable.length; ix++) { - sineTable[ix] = (float)Math.sin((double)ix * Math.PI * 2.0 / 65536.0); - } -@@ -46,11 +46,11 @@ public class Mth { - private static final double[] COS_TAB = new double[257]; - - public static float sin(float value) { -- return SIN[(int)(value * 10430.378F) & 65535]; -+ return space.bxteam.divinemc.util.lithium.CompactSineLUT.sin(value); // DivineMC - lithium: math.sine_lut - } - - public static float cos(float value) { -- return SIN[(int)(value * 10430.378F + 16384.0F) & 65535]; -+ return space.bxteam.divinemc.util.lithium.CompactSineLUT.cos(value); // DivineMC - lithium: math.sine_lut - } - - public static float sqrt(float value) { -diff --git a/src/main/java/space/bxteam/divinemc/util/lithium/CompactSineLUT.java b/src/main/java/space/bxteam/divinemc/util/lithium/CompactSineLUT.java -new file mode 100644 -index 0000000000000000000000000000000000000000..79a49c3e99ab069172f2fd85a942474f0c872fc9 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/util/lithium/CompactSineLUT.java -@@ -0,0 +1,88 @@ -+package space.bxteam.divinemc.util.lithium; -+ -+import net.minecraft.util.Mth; -+ -+/** -+ * A replacement for the sine angle lookup table used in {@link Mth}, both reducing the size of LUT and improving -+ * the access patterns for common paired sin/cos operations. -+ *

-+ * sin(-x) = -sin(x) -+ * ... to eliminate negative angles from the LUT. -+ *

-+ * sin(x) = sin(pi/2 - x) -+ * ... to eliminate supplementary angles from the LUT. -+ *

-+ * Using these identities allows us to reduce the LUT from 64K entries (256 KB) to just 16K entries (64 KB), enabling -+ * it to better fit into the CPU's caches at the expense of some cycles on the fast path. The implementation has been -+ * tightly optimized to avoid branching where possible and to use very quick integer operations. -+ *

-+ * Generally speaking, reducing the size of a lookup table is always a good optimization, but since we need to spend -+ * extra CPU cycles trying to maintain parity with vanilla, there is the potential risk that this implementation ends -+ * up being slower than vanilla when the lookup table is able to be kept in cache memory. -+ *

-+ * Unlike other "fast math" implementations, the values returned by this class are *bit-for-bit identical* with those -+ * from {@link Mth}. Validation is performed during runtime to ensure that the table is correct. -+ * -+ * @author coderbot16 Author of the original (and very clever) implementation in Rust -+ * @author jellysquid3 Additional optimizations, port to Java -+ */ -+public class CompactSineLUT { -+ private static final int[] SIN_INT = new int[16384 + 1]; -+ private static final float SIN_MIDPOINT; -+ -+ static { -+ // Copy the sine table, covering to raw int bits -+ for (int i = 0; i < SIN_INT.length; i++) { -+ SIN_INT[i] = Float.floatToRawIntBits(Mth.SIN[i]); -+ } -+ -+ SIN_MIDPOINT = Mth.SIN[Mth.SIN.length / 2]; -+ -+ // Test that the lookup table is correct during runtime -+ for (int i = 0; i < Mth.SIN.length; i++) { -+ float expected = Mth.SIN[i]; -+ float value = lookup(i); -+ -+ if (expected != value) { -+ throw new IllegalArgumentException(String.format("LUT error at index %d (expected: %s, found: %s)", i, expected, value)); -+ } -+ } -+ } -+ -+ // [VanillaCopy] Mth#sin(float) -+ public static float sin(float f) { -+ return lookup((int) (f * 10430.378f) & 0xFFFF); -+ } -+ -+ // [VanillaCopy] Mth#cos(float) -+ public static float cos(float f) { -+ return lookup((int) (f * 10430.378f + 16384.0f) & 0xFFFF); -+ } -+ -+ private static float lookup(int index) { -+ // A special case... Is there some way to eliminate this? -+ if (index == 32768) { -+ return SIN_MIDPOINT; -+ } -+ -+ // Trigonometric identity: sin(-x) = -sin(x) -+ // Given a domain of 0 <= x <= 2*pi, just negate the value if x > pi. -+ // This allows the sin table size to be halved. -+ int neg = (index & 0x8000) << 16; -+ -+ // All bits set if (pi/2 <= x), none set otherwise -+ // Extracts the 15th bit from 'half' -+ int mask = (index << 17) >> 31; -+ -+ // Trigonometric identity: sin(x) = sin(pi/2 - x) -+ int pos = (0x8001 & mask) + (index ^ mask); -+ -+ // Wrap the position in the table. Moving this down to immediately before the array access -+ // seems to help the Hotspot compiler optimize the bit math better. -+ pos &= 0x7fff; -+ -+ // Fetch the corresponding value from the LUT and invert the sign bit as needed -+ // This directly manipulate the sign bit on the float bits to simplify logic -+ return Float.intBitsToFloat(SIN_INT[pos] ^ neg); -+ } -+} diff --git a/patches/server/0020-Don-t-save-Fireworks.patch b/patches/server/0020-Don-t-save-Fireworks.patch deleted file mode 100644 index fb9d7ee..0000000 --- a/patches/server/0020-Don-t-save-Fireworks.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Fri, 31 Mar 2023 22:47:12 +0300 -Subject: [PATCH] Don't save Fireworks - - -diff --git a/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java b/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java -index 8a4e7e1c0c4919d2ee34121c14f9665b9ad95273..70c8fe6c225a1e90a144f9b00e6ce2d97d5d82a7 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/FireworkRocketEntity.java -@@ -370,6 +370,13 @@ public class FireworkRocketEntity extends Projectile implements ItemSupplier { - return false; - } - -+ // DivineMC start - Don't save Fireworks -+ @Override -+ public boolean shouldBeSaved() { -+ return this.level().divinemcConfig.saveFireworks; -+ } -+ // DivineMC end -+ - public static ItemStack getDefaultItem() { - return new ItemStack(Items.FIREWORK_ROCKET); - } -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -index 3e64e0d2d925f9fca1eb38cc07c20cae785ef39f..243b3d4379f4f6f273222a3611d8a463053d3e70 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -@@ -77,4 +77,9 @@ public class DivineWorldConfig { - final Map value = DivineConfig.getMap("world-settings." + worldName + "." + path, null); - return value.isEmpty() ? fallback : value; - } -+ -+ public boolean saveFireworks = false; -+ private void saveFireworks() { -+ saveFireworks = getBoolean("gameplay-mechanics.should-save-fireworks", saveFireworks); -+ } - } diff --git a/patches/server/0021-Remove-Spigot-tick-limiter.patch b/patches/server/0021-Remove-Spigot-tick-limiter.patch deleted file mode 100644 index 9f1eb7e..0000000 --- a/patches/server/0021-Remove-Spigot-tick-limiter.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Fri, 31 Mar 2023 23:05:12 +0300 -Subject: [PATCH] Remove Spigot tick limiter - -Original code by Titaniumtown, modified by NONPLAYT -You can find the original code on https://gitlab.com/Titaniumtown/JettPack - -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index d152815ed2b12334458eba1eb88171d563169e84..4ce5cf4f5e2974e3d87514aee920d296a973938e 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -178,8 +178,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur - Purpur config files - public final space.bxteam.divinemc.configuration.DivineWorldConfig divinemcConfig; // DivineMC - public static BlockPos lastPhysicsProblem; // Spigot -- private org.spigotmc.TickLimiter entityLimiter; -- private org.spigotmc.TickLimiter tileLimiter; - private int tileTickPosition; - public final Map explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions - public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Faster redstone torch rapid clock removal; Move from Map in BlockRedstoneTorch to here -@@ -966,8 +964,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - public void onBorderSetDamageSafeZOne(WorldBorder border, double safeZoneRadius) {} - }); - // CraftBukkit end -- this.entityLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.entityMaxTickTime); -- this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime); - this.chunkPacketBlockController = this.paperConfig().anticheat.antiXray.enabled ? new com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray(this, executor) : com.destroystokyo.paper.antixray.ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray - this.entityLookup = new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEntityLookup(this); // Paper - rewrite chunk system - } -diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java -index 0e6b697c6e8503beeed9412abddad2b512246b37..c8c0b7f092886bf50fc9d07df974ec32e95b27e2 100644 ---- a/src/main/java/org/spigotmc/SpigotWorldConfig.java -+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java -@@ -454,6 +454,7 @@ public class SpigotWorldConfig - this.hangingTickFrequency = this.getInt( "hanging-tick-frequency", 200 ); // DivineMC - optimize default values for configs - } - -+ /* DivineMC - remove tick limiter - public int tileMaxTickTime; - public int entityMaxTickTime; - private void maxTickTimes() -@@ -462,6 +463,7 @@ public class SpigotWorldConfig - this.entityMaxTickTime = this.getInt("max-tick-time.entity", 50); - this.log("Tile Max Tick Time: " + this.tileMaxTickTime + "ms Entity max Tick Time: " + this.entityMaxTickTime + "ms"); - } -+ */ - - public int thunderChance; - private void thunderChance() -diff --git a/src/main/java/org/spigotmc/TickLimiter.java b/src/main/java/org/spigotmc/TickLimiter.java -deleted file mode 100644 -index 4074538ea6090bf99d8ab04b1e98c2832a0e9a98..0000000000000000000000000000000000000000 ---- a/src/main/java/org/spigotmc/TickLimiter.java -+++ /dev/null -@@ -1,20 +0,0 @@ --package org.spigotmc; -- --public class TickLimiter { -- -- private final int maxTime; -- private long startTime; -- -- public TickLimiter(int maxtime) { -- this.maxTime = maxtime; -- } -- -- public void initTick() { -- this.startTime = System.currentTimeMillis(); -- } -- -- public boolean shouldContinue() { -- long remaining = System.currentTimeMillis() - this.startTime; -- return remaining < this.maxTime; -- } --} diff --git a/patches/server/0022-Make-entity-goals-public.patch b/patches/server/0022-Make-entity-goals-public.patch deleted file mode 100644 index 006e3d4..0000000 --- a/patches/server/0022-Make-entity-goals-public.patch +++ /dev/null @@ -1,139 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sun, 16 Jul 2023 11:37:32 +0300 -Subject: [PATCH] Make entity goals public - - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java -index d2ac2c3a2481ee216a491333b173625da3881737..98ae165bba22390b098f8a96d1e513f72ef71224 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -813,7 +813,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - return state.is(BlockTags.BEE_ATTRACTIVE) ? ((Boolean) state.getValueOrElse(BlockStateProperties.WATERLOGGED, false) ? false : (state.is(Blocks.SUNFLOWER) ? state.getValue(DoublePlantBlock.HALF) == DoubleBlockHalf.UPPER : true)) : false; - } - -- private class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables -+ public class BeeLookControl extends org.purpurmc.purpur.controller.LookControllerWASD { // Purpur - Ridables // DivineMC - private -> public - - BeeLookControl(final Mob entity) { - super(entity); -@@ -849,7 +849,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- private class BeeEnterHiveGoal extends Bee.BaseBeeGoal { -+ public class BeeEnterHiveGoal extends Bee.BaseBeeGoal { // DivineMC - private -> public - - BeeEnterHiveGoal() { - super(); -@@ -954,7 +954,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- private class BeePollinateGoal extends Bee.BaseBeeGoal { -+ public class BeePollinateGoal extends Bee.BaseBeeGoal { // DivineMC - private -> public - - private static final int MIN_POLLINATION_TICKS = 400; - private static final double ARRIVAL_THRESHOLD = 0.1D; -@@ -1133,7 +1133,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- private class BeeLocateHiveGoal extends Bee.BaseBeeGoal { -+ public class BeeLocateHiveGoal extends Bee.BaseBeeGoal { // DivineMC - private -> public - - BeeLocateHiveGoal() { - super(); -@@ -1362,7 +1362,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- private class BeeGrowCropGoal extends Bee.BaseBeeGoal { -+ public class BeeGrowCropGoal extends Bee.BaseBeeGoal { // DivineMC - private -> public - - static final int GROW_CHANCE = 30; - -@@ -1431,7 +1431,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- private class BeeWanderGoal extends Goal { -+ public class BeeWanderGoal extends Goal { // DivineMC - private -> public - - BeeWanderGoal() { - this.setFlags(EnumSet.of(Goal.Flag.MOVE)); -@@ -1482,7 +1482,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- private class BeeHurtByOtherGoal extends HurtByTargetGoal { -+ public class BeeHurtByOtherGoal extends HurtByTargetGoal { // DivineMC - private -> public - - BeeHurtByOtherGoal(final Bee entitybee) { - super(entitybee); -@@ -1502,7 +1502,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- private static class BeeBecomeAngryTargetGoal extends NearestAttackableTargetGoal { -+ public static class BeeBecomeAngryTargetGoal extends NearestAttackableTargetGoal { // DivineMC - private -> public - - BeeBecomeAngryTargetGoal(Bee bee) { - // Objects.requireNonNull(entitybee); // CraftBukkit - decompile error -@@ -1533,7 +1533,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } - } - -- private abstract class BaseBeeGoal extends Goal { -+ public abstract class BaseBeeGoal extends Goal { // DivineMC - private -> public - - BaseBeeGoal() {} - -diff --git a/src/main/java/net/minecraft/world/entity/animal/Cat.java b/src/main/java/net/minecraft/world/entity/animal/Cat.java -index 5a3348ca39b86cfea941fdfb98ca90a7a0ef908d..e70adf4228d6be6f10d9cfea3762f292a2bc31ce 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Cat.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Cat.java -@@ -578,7 +578,7 @@ public class Cat extends TamableAnimal implements VariantHolder public - - private final Cat cat; - @Nullable -@@ -716,7 +716,7 @@ public class Cat extends TamableAnimal implements VariantHolder extends AvoidEntityGoal { -+ public static class CatAvoidEntityGoal extends AvoidEntityGoal { // DivineMC - private -> public - - private final Cat cat; - -diff --git a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -index 3d1cb875edfe6bf5c9e3f4b7dade7868b7dbfa93..05326f8618626e953c2799089a69803e73b11da7 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Vindicator.java -@@ -99,7 +99,7 @@ public class Vindicator extends AbstractIllager { - this.goalSelector.addGoal(1, new AvoidEntityGoal<>(this, Creaking.class, 8.0F, 1.0, 1.2)); - this.goalSelector.addGoal(2, new Vindicator.VindicatorBreakDoorGoal(this)); - this.goalSelector.addGoal(3, new AbstractIllager.RaiderOpenDoorGoal(this)); -- this.goalSelector.addGoal(4, new Raider.HoldGroundAttackGoal(this, 10.0F)); -+ this.goalSelector.addGoal(4, new HoldGroundAttackGoal(this, 10.0F)); - this.goalSelector.addGoal(5, new MeleeAttackGoal(this, 1.0, false)); - this.targetSelector.addGoal(0, new org.purpurmc.purpur.entity.ai.HasRider(this)); // Purpur - Ridables - this.targetSelector.addGoal(1, new HurtByTargetGoal(this, Raider.class).setAlertOthers()); -diff --git a/src/main/java/net/minecraft/world/entity/raid/Raider.java b/src/main/java/net/minecraft/world/entity/raid/Raider.java -index 65206bc0c3276fda449936cae88cc819a346e299..2121ef972ce586b627c8adbfbcac0393a76b11cb 100644 ---- a/src/main/java/net/minecraft/world/entity/raid/Raider.java -+++ b/src/main/java/net/minecraft/world/entity/raid/Raider.java -@@ -381,7 +381,7 @@ public abstract class Raider extends PatrollingMonster { - } - } - -- private static class RaiderMoveThroughVillageGoal extends Goal { -+ public class RaiderMoveThroughVillageGoal extends Goal { // DivineMC - private -> public - - private final Raider raider; - private final double speedModifier; diff --git a/patches/server/0023-Fix-sprint-glitch.patch b/patches/server/0023-Fix-sprint-glitch.patch deleted file mode 100644 index dd3ec96..0000000 --- a/patches/server/0023-Fix-sprint-glitch.patch +++ /dev/null @@ -1,19 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sun, 12 May 2024 21:26:38 +0300 -Subject: [PATCH] Fix sprint glitch - - -diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java -index a850484f2c6f37a39a4cf8cc61683fed4f6f273e..8096e094381062f26172eb1758ee18fda61ca961 100644 ---- a/src/main/java/net/minecraft/world/entity/LivingEntity.java -+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java -@@ -1474,7 +1474,7 @@ public abstract class LivingEntity extends Entity implements Attackable { - player.setRealHealth(health); - } - -- player.updateScaledHealth(false); -+ this.entityData.set(LivingEntity.DATA_HEALTH_ID, player.getScaledHealth()); // DivineMC - Fix sprint glitch - return; - } - // CraftBukkit end diff --git a/patches/server/0025-Despawn-shulker-bullets-on-owner-death.patch b/patches/server/0025-Despawn-shulker-bullets-on-owner-death.patch deleted file mode 100644 index 7ccfe0d..0000000 --- a/patches/server/0025-Despawn-shulker-bullets-on-owner-death.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 10 Jun 2023 13:12:59 +0300 -Subject: [PATCH] Despawn shulker bullets on owner death - - -diff --git a/src/main/java/net/minecraft/world/entity/projectile/ShulkerBullet.java b/src/main/java/net/minecraft/world/entity/projectile/ShulkerBullet.java -index 347b1cab11529cb5399e607626c84bc985d5c513..9ab72bfeafed21c47f6ce27cf5df921d4b768f97 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/ShulkerBullet.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/ShulkerBullet.java -@@ -227,6 +227,17 @@ public class ShulkerBullet extends Projectile { - Vec3 vec3d; - - if (!this.level().isClientSide) { -+ // DivineMC start - despawn shulker bullets on owner death -+ if (this.level().divinemcConfig.despawnShulkerBulletsOnOwnerDeath) { -+ if (!isInvulnerable()) { -+ var owner = getOwner(); -+ if (owner == null || !owner.isAlive()) { -+ discard(); -+ return; -+ } -+ } -+ } -+ // DivineMC end - if (this.finalTarget == null && this.targetId != null) { - this.finalTarget = ((ServerLevel) this.level()).getEntity(this.targetId); - if (this.finalTarget == null) { -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -index e1274fe3b0ff369d1f6f229026ced2e03ea335ca..7e62ee9418d5add5b0b4ddb885d3a1745ce799b2 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -@@ -89,4 +89,9 @@ public class DivineWorldConfig { - dontEjectPlayerFromBoatUnderwater = getBoolean("gameplay-mechanics.boat.dont-eject-players-from-boat-underwater", dontEjectPlayerFromBoatUnderwater); - alwaysAllowToEnterTheBoat = getBoolean("gameplay-mechanics.boat.always-allow-to-enter-the-boat", alwaysAllowToEnterTheBoat); - } -+ -+ public boolean despawnShulkerBulletsOnOwnerDeath = true; -+ private void despawnShulkerBulletsOnOwnerDeath() { -+ despawnShulkerBulletsOnOwnerDeath = getBoolean("gameplay-mechanics.mob.shulker.despawn-bullets-on-player-death", despawnShulkerBulletsOnOwnerDeath); -+ } - } diff --git a/patches/server/0027-Snowball-and-Egg-knockback.patch b/patches/server/0027-Snowball-and-Egg-knockback.patch deleted file mode 100644 index 9daa495..0000000 --- a/patches/server/0027-Snowball-and-Egg-knockback.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Wed, 17 Apr 2024 02:02:02 +0300 -Subject: [PATCH] Snowball and Egg knockback - - -diff --git a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -index 6fdacf2f6934521a0dd4b25aea35a6a14123da0a..477a6326f19a15c9ea7e0329515ec0007ac2cd20 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/Snowball.java -@@ -3,6 +3,7 @@ package net.minecraft.world.entity.projectile; - import net.minecraft.core.particles.ItemParticleOption; - import net.minecraft.core.particles.ParticleOptions; - import net.minecraft.core.particles.ParticleTypes; -+import net.minecraft.server.level.ServerPlayer; - import net.minecraft.world.entity.Entity; - import net.minecraft.world.entity.EntityType; - import net.minecraft.world.entity.LivingEntity; -@@ -61,6 +62,13 @@ public class Snowball extends ThrowableItemProjectile { - int i = entity.level().purpurConfig.snowballDamage >= 0 ? entity.level().purpurConfig.snowballDamage : entity instanceof Blaze ? 3 : 0; // Purpur - - entity.hurt(this.damageSources().thrown(this, this.getOwner()), (float) i); -+ -+ // DivineMC start - Snowball and Egg knockback -+ if (this.level().divinemcConfig.snowballCanKnockback && entity instanceof ServerPlayer) { -+ entity.hurt(this.damageSources().thrown(this, this.getOwner()), 0.0000001F); -+ ((ServerPlayer) entity).knockback(0.4000000059604645D, this.getX() - entity.getX(), this.getZ() - entity.getZ(), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE); -+ } -+ // DivineMC end - } - - // Purpur start - borrowed and modified code from ThrownPotion#onHitBlock and ThrownPotion#dowseFire -diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownEgg.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownEgg.java -index 155c2bbd35adacb7c3668fbe81a7c454e5102c8b..8c48ad7b52bc7914531abe0319b30aebac38c6e6 100644 ---- a/src/main/java/net/minecraft/world/entity/projectile/ThrownEgg.java -+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownEgg.java -@@ -51,7 +51,15 @@ public class ThrownEgg extends ThrowableItemProjectile { - @Override - protected void onHitEntity(EntityHitResult entityHitResult) { - super.onHitEntity(entityHitResult); -+ Entity entity = entityHitResult.getEntity(); // DivineMC - entityHitResult.getEntity().hurt(this.damageSources().thrown(this, this.getOwner()), 0.0F); -+ -+ // DivineMC start - Snowball and Egg knockback -+ if (this.level().divinemcConfig.eggCanKnockback && entity instanceof ServerPlayer) { -+ entity.hurt(this.damageSources().thrown(this, this.getOwner()), 0.0000001F); -+ ((ServerPlayer) entity).knockback(0.4000000059604645D, this.getX() - entity.getX(), this.getZ() - entity.getZ(), this, io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.DAMAGE); -+ } -+ // DivineMC end - } - - @Override -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -index 7e62ee9418d5add5b0b4ddb885d3a1745ce799b2..242da697c957508c8e75bfd232c44ea34ba3a62a 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -@@ -94,4 +94,11 @@ public class DivineWorldConfig { - private void despawnShulkerBulletsOnOwnerDeath() { - despawnShulkerBulletsOnOwnerDeath = getBoolean("gameplay-mechanics.mob.shulker.despawn-bullets-on-player-death", despawnShulkerBulletsOnOwnerDeath); - } -+ -+ public boolean snowballCanKnockback = true; -+ public boolean eggCanKnockback = true; -+ private void setSnowballAndEggKnockback() { -+ snowballCanKnockback = getBoolean("gameplay-mechanics.projectiles.snowball.knockback", snowballCanKnockback); -+ eggCanKnockback = getBoolean("gameplay-mechanics.projectiles.egg.knockback", eggCanKnockback); -+ } - } diff --git a/patches/server/0028-Make-minecart-hopper-work-without-players.patch b/patches/server/0028-Make-minecart-hopper-work-without-players.patch deleted file mode 100644 index f6c467d..0000000 --- a/patches/server/0028-Make-minecart-hopper-work-without-players.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Wed, 22 May 2024 23:26:51 +0300 -Subject: [PATCH] Make minecart hopper work without players - - -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java -index d81a6874e8b25f098df619f84c359e146c7f64de..1d2794b77ca71b1dd9abd1200860467be2c346d9 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/MinecartHopper.java -@@ -99,6 +99,13 @@ public class MinecartHopper extends AbstractMinecartContainer implements Hopper - } - } - -+ // DivineMC start - tick minecart -+ @Override -+ public void inactiveTick() { -+ this.tick(); -+ } -+ // DivineMC end -+ - public boolean suckInItems() { - if (HopperBlockEntity.suckInItems(this.level(), this)) { - this.immunize(); // Paper diff --git a/patches/server/0030-Block-Log4Shell-exploit.patch b/patches/server/0030-Block-Log4Shell-exploit.patch deleted file mode 100644 index 566c8e4..0000000 --- a/patches/server/0030-Block-Log4Shell-exploit.patch +++ /dev/null @@ -1,47 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Thu, 23 May 2024 02:06:30 +0300 -Subject: [PATCH] Block Log4Shell exploit - - -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index c783c17d45beda8297171d0834350197808a7335..0fef16501c1a971dc8bd2616c7476dffabb6ea08 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2518,6 +2518,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - } - - private void tryHandleChat(String s, Runnable runnable, boolean sync) { // CraftBukkit -+ if (ServerGamePacketListenerImpl.isLog4ShellExploit(s)) return; // DivineMC - Block Log4Shell exploit - if (ServerGamePacketListenerImpl.isChatMessageIllegal(s)) { - this.disconnectAsync((Component) Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper // Paper - add proper async disconnect - } else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales -@@ -2549,6 +2550,15 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - } - } - -+ // DivineMC start - Block Log4Shell exploit -+ public static boolean isLog4ShellExploit(String message) { -+ java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(".*\\$\\{[^}]*}.*"); -+ java.util.regex.Matcher matcher = pattern.matcher(message); -+ -+ return matcher.find(); -+ } -+ // DivineMC end -+ - public static boolean isChatMessageIllegal(String message) { - for (int i = 0; i < message.length(); ++i) { - if (!StringUtil.isAllowedChatCharacter(message.charAt(i))) { -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index c2df3c38f58d8dcb5e3d62077655af56a3bffd65..e77d8db58e1be82f64aa3a3abfc1c85a6e382967 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -767,6 +767,8 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - Preconditions.checkArgument(msg != null, "msg cannot be null"); - - if (this.getHandle().connection == null) return; -+ -+ if (ServerGamePacketListenerImpl.isLog4ShellExploit(msg)) return; // DivineMC - Block Log4Shell exploit - - // Paper start - Improve chat handling - if (ServerGamePacketListenerImpl.isChatMessageIllegal(msg)) { diff --git a/patches/server/0031-Option-to-disable-non-editable-sign-warning.patch b/patches/server/0031-Option-to-disable-non-editable-sign-warning.patch deleted file mode 100644 index 1a25660..0000000 --- a/patches/server/0031-Option-to-disable-non-editable-sign-warning.patch +++ /dev/null @@ -1,33 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 17 Jun 2024 16:21:15 +0300 -Subject: [PATCH] Option to disable non editable sign warning - - -diff --git a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java -index b51b0b0f48b1da6187387d6ec025681e10ed584d..507b94fe816596e381434cf2e160bdee8023d337 100644 ---- a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java -+++ b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java -@@ -189,7 +189,7 @@ public class SignBlockEntity extends BlockEntity { - this.setAllowedPlayerEditor((UUID) null); - this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3); - } else { -- SignBlockEntity.LOGGER.warn("Player {} just tried to change non-editable sign", player.getName().getString()); -+ if (!space.bxteam.divinemc.configuration.DivineConfig.disableNonEditableSignWarning) SignBlockEntity.LOGGER.warn("Player {} just tried to change non-editable sign", player.getName().getString()); // DivineMC - Option to disable warning - if (player.distanceToSqr(this.getBlockPos().getX(), this.getBlockPos().getY(), this.getBlockPos().getZ()) < 32 * 32) // Paper - Dont send far away sign update - ((ServerPlayer) player).connection.send(this.getUpdatePacket()); // CraftBukkit - } -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -index 27c45545e1e9ad6ea88e65c884c5a4955fc1c80c..882e08b1eef89dd01129c89aa5fa1094d35788c5 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -154,4 +154,9 @@ public class DivineConfig { - } - return builder.build(); - } -+ -+ public static boolean disableNonEditableSignWarning = true; -+ private static void miscSettings() { -+ disableNonEditableSignWarning = getBoolean("settings.misc.disable-non-editable-sign-warning", disableNonEditableSignWarning); -+ } - } diff --git a/patches/server/0032-Suppress-errors-from-dirty-attributes.patch b/patches/server/0032-Suppress-errors-from-dirty-attributes.patch deleted file mode 100644 index ba62918..0000000 --- a/patches/server/0032-Suppress-errors-from-dirty-attributes.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sun, 14 Jan 2024 22:58:43 +0300 -Subject: [PATCH] Suppress errors from dirty attributes - - -diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index 2bec5ed591e658765602379f9065b8089f5df6ea..944de5ff109b15b76b69326e69b6b1c2afb63dd1 100644 ---- a/src/main/java/net/minecraft/server/level/ServerEntity.java -+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -455,7 +455,10 @@ public class ServerEntity { - } - - if (this.entity instanceof LivingEntity) { -- Set set = ((LivingEntity) this.entity).getAttributes().getAttributesToSync(); -+ // DivineMC start - Suppress errors from dirty attributes -+ Set attributes = ((LivingEntity) this.entity).getAttributes().getAttributesToSync(); -+ final Set set = this.level.divinemcConfig.suppressErrorsFromDirtyAttributes ? Collections.synchronizedSet(attributes) : attributes; -+ // DivineMC end - - if (!set.isEmpty()) { - // CraftBukkit start - Send scaled max health -@@ -466,7 +469,7 @@ public class ServerEntity { - this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), set)); - } - -- set.clear(); -+ attributes.clear(); // DivineMC - } - - } -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -index 242da697c957508c8e75bfd232c44ea34ba3a62a..d94c51ea18d299dd52b9a8521a9cdc0d95b79356 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -@@ -101,4 +101,9 @@ public class DivineWorldConfig { - snowballCanKnockback = getBoolean("gameplay-mechanics.projectiles.snowball.knockback", snowballCanKnockback); - eggCanKnockback = getBoolean("gameplay-mechanics.projectiles.egg.knockback", eggCanKnockback); - } -+ -+ public boolean suppressErrorsFromDirtyAttributes = true; -+ private void suppressErrorsFromDirtyAttributes() { -+ suppressErrorsFromDirtyAttributes = getBoolean("suppress-errors-from-dirty-attributes", suppressErrorsFromDirtyAttributes); -+ } - } diff --git a/patches/server/0033-Carpet-Fixes-getBiome-Optimize.patch b/patches/server/0033-Carpet-Fixes-getBiome-Optimize.patch deleted file mode 100644 index 287b68a..0000000 --- a/patches/server/0033-Carpet-Fixes-getBiome-Optimize.patch +++ /dev/null @@ -1,167 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 13 Jan 2024 19:19:16 +0300 -Subject: [PATCH] Carpet-Fixes: getBiome Optimize - - -diff --git a/src/main/java/net/minecraft/world/level/biome/BiomeManager.java b/src/main/java/net/minecraft/world/level/biome/BiomeManager.java -index 90f8360f547ce709fd13ee34f8e67d8bfa94b498..e71cfe169d8113b1e38fb1f3f12dc9b5177c83de 100644 ---- a/src/main/java/net/minecraft/world/level/biome/BiomeManager.java -+++ b/src/main/java/net/minecraft/world/level/biome/BiomeManager.java -@@ -14,6 +14,7 @@ public class BiomeManager { - private static final int ZOOM_MASK = 3; - private final BiomeManager.NoiseBiomeSource noiseBiomeSource; - private final long biomeZoomSeed; -+ private static final double maxOffset = 0.4500000001D; // DivineMC - Carpet-Fixes: getBiome Optimize - - public BiomeManager(BiomeManager.NoiseBiomeSource storage, long seed) { - this.noiseBiomeSource = storage; -@@ -29,39 +30,103 @@ public class BiomeManager { - } - - public Holder getBiome(BlockPos pos) { -- int i = pos.getX() - 2; -- int j = pos.getY() - 2; -- int k = pos.getZ() - 2; -- int l = i >> 2; -- int m = j >> 2; -- int n = k >> 2; -- double d = (double)(i & 3) / 4.0; -- double e = (double)(j & 3) / 4.0; -- double f = (double)(k & 3) / 4.0; -- int o = 0; -- double g = Double.POSITIVE_INFINITY; -- -- for (int p = 0; p < 8; p++) { -- boolean bl = (p & 4) == 0; -- boolean bl2 = (p & 2) == 0; -- boolean bl3 = (p & 1) == 0; -- int q = bl ? l : l + 1; -- int r = bl2 ? m : m + 1; -- int s = bl3 ? n : n + 1; -- double h = bl ? d : d - 1.0; -- double t = bl2 ? e : e - 1.0; -- double u = bl3 ? f : f - 1.0; -- double v = getFiddledDistance(this.biomeZoomSeed, q, r, s, h, t, u); -- if (g > v) { -- o = p; -- g = v; -+ // DivineMC start - Carpet-Fixes: getBiome Optimize -+ if (space.bxteam.divinemc.configuration.DivineConfig.biomeManagerOptimization) { -+ int xMinus2 = pos.getX() - 2; -+ int yMinus2 = pos.getY() - 2; -+ int zMinus2 = pos.getZ() - 2; -+ int x = xMinus2 >> 2; // BlockPos to BiomePos -+ int y = yMinus2 >> 2; -+ int z = zMinus2 >> 2; -+ double quartX = (double) (xMinus2 & 3) / 4.0D; // quartLocal divided by 4 -+ double quartY = (double) (yMinus2 & 3) / 4.0D; // 0/4, 1/4, 2/4, 3/4 -+ double quartZ = (double) (zMinus2 & 3) / 4.0D; // [0, 0.25, 0.5, 0.75] -+ int smallestX = 0; -+ double smallestDist = Double.POSITIVE_INFINITY; -+ for (int biomeX = 0; biomeX < 8; ++biomeX) { -+ boolean everyOtherQuad = (biomeX & 4) == 0; // 1 1 1 1 0 0 0 0 -+ boolean everyOtherPair = (biomeX & 2) == 0; // 1 1 0 0 1 1 0 0 -+ boolean everyOther = (biomeX & 1) == 0; // 1 0 1 0 1 0 1 0 -+ double quartXX = everyOtherQuad ? quartX : quartX - 1.0D; //[-1.0,-0.75,-0.5,-0.25,0.0,0.25,0.5,0.75] -+ double quartYY = everyOtherPair ? quartY : quartY - 1.0D; -+ double quartZZ = everyOther ? quartZ : quartZ - 1.0D; -+ -+ double maxQuartYY = 0.0D, maxQuartZZ = 0.0D; -+ if (biomeX != 0) { -+ maxQuartYY = Mth.square(Math.max(quartYY + maxOffset, Math.abs(quartYY - maxOffset))); -+ maxQuartZZ = Mth.square(Math.max(quartZZ + maxOffset, Math.abs(quartZZ - maxOffset))); -+ double maxQuartXX = Mth.square(Math.max(quartXX + maxOffset, Math.abs(quartXX - maxOffset))); -+ if (smallestDist < maxQuartXX + maxQuartYY + maxQuartZZ) continue; -+ } -+ -+ int xx = everyOtherQuad ? x : x + 1; -+ int yy = everyOtherPair ? y : y + 1; -+ int zz = everyOther ? z : z + 1; -+ -+ //I transferred the code from method_38106 to here, so I could call continue halfway through -+ long seed = LinearCongruentialGenerator.next(this.biomeZoomSeed, xx); -+ seed = LinearCongruentialGenerator.next(seed, yy); -+ seed = LinearCongruentialGenerator.next(seed, zz); -+ seed = LinearCongruentialGenerator.next(seed, xx); -+ seed = LinearCongruentialGenerator.next(seed, yy); -+ seed = LinearCongruentialGenerator.next(seed, zz); -+ double offsetX = getFiddle(seed); -+ double sqrX = Mth.square(quartXX + offsetX); -+ if (biomeX != 0 && smallestDist < sqrX + maxQuartYY + maxQuartZZ) continue; //skip the rest of the loop -+ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); -+ double offsetY = getFiddle(seed); -+ double sqrY = Mth.square(quartYY + offsetY); -+ if (biomeX != 0 && smallestDist < sqrX + sqrY + maxQuartZZ) continue; // skip the rest of the loop -+ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); -+ double offsetZ = getFiddle(seed); -+ double biomeDist = sqrX + sqrY + Mth.square(quartZZ + offsetZ); -+ -+ if (smallestDist > biomeDist) { -+ smallestX = biomeX; -+ smallestDist = biomeDist; -+ } -+ } -+ return this.noiseBiomeSource.getNoiseBiome( -+ (smallestX & 4) == 0 ? x : x + 1, -+ (smallestX & 2) == 0 ? y : y + 1, -+ (smallestX & 1) == 0 ? z : z + 1 -+ ); -+ } else { -+ int i = pos.getX() - 2; -+ int j = pos.getY() - 2; -+ int k = pos.getZ() - 2; -+ int l = i >> 2; -+ int m = j >> 2; -+ int n = k >> 2; -+ double d = (double)(i & 3) / 4.0; -+ double e = (double)(j & 3) / 4.0; -+ double f = (double)(k & 3) / 4.0; -+ int o = 0; -+ double g = Double.POSITIVE_INFINITY; -+ -+ for (int p = 0; p < 8; p++) { -+ boolean bl = (p & 4) == 0; -+ boolean bl2 = (p & 2) == 0; -+ boolean bl3 = (p & 1) == 0; -+ int q = bl ? l : l + 1; -+ int r = bl2 ? m : m + 1; -+ int s = bl3 ? n : n + 1; -+ double h = bl ? d : d - 1.0; -+ double t = bl2 ? e : e - 1.0; -+ double u = bl3 ? f : f - 1.0; -+ double v = getFiddledDistance(this.biomeZoomSeed, q, r, s, h, t, u); -+ if (g > v) { -+ o = p; -+ g = v; -+ } - } -- } - -- int w = (o & 4) == 0 ? l : l + 1; -- int x = (o & 2) == 0 ? m : m + 1; -- int y = (o & 1) == 0 ? n : n + 1; -- return this.noiseBiomeSource.getNoiseBiome(w, x, y); -+ int w = (o & 4) == 0 ? l : l + 1; -+ int x = (o & 2) == 0 ? m : m + 1; -+ int y = (o & 1) == 0 ? n : n + 1; -+ return this.noiseBiomeSource.getNoiseBiome(w, x, y); -+ } -+ // DivineMC end - } - - public Holder getNoiseBiomeAtPosition(double x, double y, double z) { -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -index 882e08b1eef89dd01129c89aa5fa1094d35788c5..911c9144cdf15930995fbd3c2b8ff01c74138c74 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -159,4 +159,9 @@ public class DivineConfig { - private static void miscSettings() { - disableNonEditableSignWarning = getBoolean("settings.misc.disable-non-editable-sign-warning", disableNonEditableSignWarning); - } -+ -+ public static boolean biomeManagerOptimization = true; -+ private static void optimizations() { -+ biomeManagerOptimization = getBoolean("settings.optimizations.biome-manager-optimization", biomeManagerOptimization); -+ } - } diff --git a/patches/server/0034-C2ME-reduce_allocs.patch b/patches/server/0034-C2ME-reduce_allocs.patch deleted file mode 100644 index e756c1b..0000000 --- a/patches/server/0034-C2ME-reduce_allocs.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 13 May 2024 17:40:02 +0300 -Subject: [PATCH] C2ME: reduce_allocs - - -diff --git a/src/main/java/net/minecraft/world/level/levelgen/feature/OreFeature.java b/src/main/java/net/minecraft/world/level/levelgen/feature/OreFeature.java -index 506b2afd099c9b7e9ac3f6f2fcea8e523fae396b..2394f6f2c031b25a6cbeb79918a04b5710548d98 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/feature/OreFeature.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/feature/OreFeature.java -@@ -69,7 +69,7 @@ public class OreFeature extends Feature { - int verticalSize - ) { - int i = 0; -- BitSet bitSet = new BitSet(horizontalSize * verticalSize * horizontalSize); -+ BitSet bitSet = space.bxteam.divinemc.util.c2me.ObjectCachingUtils.getCachedOrNewBitSet(horizontalSize * verticalSize * horizontalSize); // DivineMC - C2ME: reduce_allocs - BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos(); - int j = config.size; - double[] ds = new double[j * 4]; -diff --git a/src/main/java/space/bxteam/divinemc/util/c2me/ObjectCachingUtils.java b/src/main/java/space/bxteam/divinemc/util/c2me/ObjectCachingUtils.java -new file mode 100644 -index 0000000000000000000000000000000000000000..828b9cb2f6506b9775dfccc934e9a64d5ddfaeff ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/util/c2me/ObjectCachingUtils.java -@@ -0,0 +1,20 @@ -+package space.bxteam.divinemc.util.c2me; -+ -+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -+ -+import java.util.BitSet; -+import java.util.function.IntFunction; -+ -+public class ObjectCachingUtils { -+ private static final IntFunction bitSetConstructor = BitSet::new; -+ -+ public static ThreadLocal> BITSETS = ThreadLocal.withInitial(Int2ObjectOpenHashMap::new); -+ -+ private ObjectCachingUtils() {} -+ -+ public static BitSet getCachedOrNewBitSet(int bits) { -+ final BitSet bitSet = BITSETS.get().computeIfAbsent(bits, bitSetConstructor); -+ bitSet.clear(); -+ return bitSet; -+ } -+} diff --git a/patches/server/0035-Carpet-AMS-Addition-Optimized-dragon-respawn.patch b/patches/server/0035-Carpet-AMS-Addition-Optimized-dragon-respawn.patch deleted file mode 100644 index 83598cc..0000000 --- a/patches/server/0035-Carpet-AMS-Addition-Optimized-dragon-respawn.patch +++ /dev/null @@ -1,163 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Wed, 22 May 2024 22:44:05 +0300 -Subject: [PATCH] Carpet-AMS-Addition: Optimized dragon respawn - - -diff --git a/src/main/java/net/minecraft/world/level/block/state/pattern/BlockPattern.java b/src/main/java/net/minecraft/world/level/block/state/pattern/BlockPattern.java -index ee99519ebd46b1db3e76e7eb86e5cc121c867dc4..43174e6b5bd306273736d1c2971f976c149d3517 100644 ---- a/src/main/java/net/minecraft/world/level/block/state/pattern/BlockPattern.java -+++ b/src/main/java/net/minecraft/world/level/block/state/pattern/BlockPattern.java -@@ -59,7 +59,7 @@ public class BlockPattern { - } - - @Nullable -- private BlockPattern.BlockPatternMatch matches(BlockPos frontTopLeft, Direction forwards, Direction up, LoadingCache cache) { -+ public BlockPattern.BlockPatternMatch matches(BlockPos frontTopLeft, Direction forwards, Direction up, LoadingCache cache) { // DivineMC - private -> public - for (int i = 0; i < this.width; i++) { - for (int j = 0; j < this.height; j++) { - for (int k = 0; k < this.depth; k++) { -diff --git a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java -index b331c93c82c27f9456fec208a0c008c5bedfa8c4..ec3142ae55686e13c2984eaf8cf0bcc8db0e39a3 100644 ---- a/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java -+++ b/src/main/java/net/minecraft/world/level/dimension/end/EndDragonFight.java -@@ -46,6 +46,7 @@ import net.minecraft.world.entity.boss.enderdragon.phases.EnderDragonPhase; - import net.minecraft.world.level.ChunkPos; - import net.minecraft.world.level.block.Blocks; - import net.minecraft.world.level.block.entity.BlockEntity; -+import net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity; - import net.minecraft.world.level.block.entity.TheEndPortalBlockEntity; - import net.minecraft.world.level.block.state.pattern.BlockInWorld; - import net.minecraft.world.level.block.state.pattern.BlockPattern; -@@ -292,8 +293,67 @@ public class EndDragonFight { - return false; - } - -+ // DivineMC start - Optimized dragon respawn -+ private int cachePortalChunkIteratorX = -8; -+ private int cachePortalChunkIteratorZ = -8; -+ private int cachePortalOriginIteratorY = -1; -+ - @Nullable - public BlockPattern.BlockPatternMatch findExitPortal() { -+ if (space.bxteam.divinemc.configuration.DivineConfig.optimizedDragonRespawn) { -+ int i, j; -+ for (i = cachePortalChunkIteratorX; i <= 8; ++i) { -+ for (j = cachePortalChunkIteratorZ; j <= 8; ++j) { -+ LevelChunk worldChunk = this.level.getChunk(i, j); -+ for (BlockEntity blockEntity : worldChunk.getBlockEntities().values()) { -+ if (blockEntity instanceof TheEndGatewayBlockEntity) { -+ continue; -+ } -+ if (blockEntity instanceof TheEndPortalBlockEntity) { -+ BlockPattern.BlockPatternMatch blockPatternMatch = this.exitPortalPattern.find(this.level, blockEntity.getBlockPos()); -+ if (blockPatternMatch != null) { -+ BlockPos blockPos = blockPatternMatch.getBlock(3, 3, 3).getPos(); -+ if (this.portalLocation == null) { -+ this.portalLocation = blockPos; -+ } -+ //No need to judge whether optimizing option is open -+ cachePortalChunkIteratorX = i; -+ cachePortalChunkIteratorZ = j; -+ return blockPatternMatch; -+ } -+ } -+ } -+ } -+ } -+ -+ if (this.needsStateScanning || this.portalLocation == null) { -+ if (cachePortalOriginIteratorY != -1) { -+ i = cachePortalOriginIteratorY; -+ } else { -+ i = this.level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, EndPodiumFeature.getLocation(BlockPos.ZERO)).getY(); -+ } -+ boolean notFirstSearch = false; -+ for (j = i; j >= 0; --j) { -+ BlockPattern.BlockPatternMatch result2 = null; -+ if (notFirstSearch) { -+ result2 = space.bxteam.divinemc.util.carpetams.BlockPatternHelper.partialSearchAround(this.exitPortalPattern, this.level, new BlockPos(EndPodiumFeature.getLocation(BlockPos.ZERO).getY(), j, EndPodiumFeature.getLocation(BlockPos.ZERO).getZ())); -+ } else { -+ result2 = this.exitPortalPattern.find(this.level, new BlockPos(EndPodiumFeature.getLocation(BlockPos.ZERO).getX(), j, EndPodiumFeature.getLocation(BlockPos.ZERO).getZ())); -+ } -+ if (result2 != null) { -+ if (this.portalLocation == null) { -+ this.portalLocation = result2.getBlock(3, 3, 3).getPos(); -+ } -+ cachePortalOriginIteratorY = j; -+ return result2; -+ } -+ notFirstSearch = true; -+ } -+ } -+ -+ return null; -+ } -+ // DivineMC end - ChunkPos chunkcoordintpair = new ChunkPos(this.origin); - - int i; -@@ -624,6 +684,11 @@ public class EndDragonFight { - } - - public boolean respawnDragon(List list) { // CraftBukkit - return boolean -+ // DivineMC start - Optimized dragon respawn -+ cachePortalChunkIteratorX = -8; -+ cachePortalChunkIteratorZ = -8; -+ cachePortalOriginIteratorY = -1; -+ // DivineMC end - if (this.dragonKilled && this.respawnStage == null) { - for (BlockPattern.BlockPatternMatch shapedetector_shapedetectorcollection = this.findExitPortal(); shapedetector_shapedetectorcollection != null; shapedetector_shapedetectorcollection = this.findExitPortal()) { - for (int i = 0; i < this.exitPortalPattern.getWidth(); ++i) { -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -index 911c9144cdf15930995fbd3c2b8ff01c74138c74..c88157b27b344b2ed1153a44daee751f5b53b45d 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -161,7 +161,9 @@ public class DivineConfig { - } - - public static boolean biomeManagerOptimization = true; -+ public static boolean optimizedDragonRespawn = true; - private static void optimizations() { - biomeManagerOptimization = getBoolean("settings.optimizations.biome-manager-optimization", biomeManagerOptimization); -+ optimizedDragonRespawn = getBoolean("settings.optimizations.optimized-dragon-respawn", optimizedDragonRespawn); - } - } -diff --git a/src/main/java/space/bxteam/divinemc/util/carpetams/BlockPatternHelper.java b/src/main/java/space/bxteam/divinemc/util/carpetams/BlockPatternHelper.java -new file mode 100644 -index 0000000000000000000000000000000000000000..1bf8f3d814d513ea2806bba9c26d207d14533cbd ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/util/carpetams/BlockPatternHelper.java -@@ -0,0 +1,32 @@ -+package space.bxteam.divinemc.util.carpetams; -+ -+import com.google.common.cache.LoadingCache; -+import net.minecraft.core.BlockPos; -+import net.minecraft.core.Direction; -+import net.minecraft.world.level.Level; -+import net.minecraft.world.level.block.state.pattern.BlockInWorld; -+import net.minecraft.world.level.block.state.pattern.BlockPattern; -+ -+/** -+ * Original here -+ * -+ * @author 1024-byteeeee -+ */ -+public class BlockPatternHelper { -+ public static BlockPattern.BlockPatternMatch partialSearchAround(BlockPattern pattern, Level world, BlockPos pos) { -+ LoadingCache loadingCache = BlockPattern.createLevelCache(world, false); -+ int i = Math.max(Math.max(pattern.getWidth(), pattern.getHeight()), pattern.getDepth()); -+ -+ for (BlockPos blockPos : BlockPos.betweenClosed(pos, pos.offset(i - 1, 0, i - 1))) { -+ for (Direction direction : Direction.values()) { -+ for (Direction direction2 : Direction.values()) { -+ BlockPattern.BlockPatternMatch result; -+ if (direction2 == direction || direction2 == direction.getOpposite() || (result = pattern.matches(blockPos, direction, direction2, loadingCache)) == null) -+ continue; -+ return result; -+ } -+ } -+ } -+ return null; -+ } -+} diff --git a/patches/server/0039-No-chat-sign.patch b/patches/server/0039-No-chat-sign.patch deleted file mode 100644 index 1e3299a..0000000 --- a/patches/server/0039-No-chat-sign.patch +++ /dev/null @@ -1,175 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sun, 12 May 2024 19:49:57 +0300 -Subject: [PATCH] No chat sign - - -diff --git a/src/main/java/io/papermc/paper/adventure/ChatProcessor.java b/src/main/java/io/papermc/paper/adventure/ChatProcessor.java -index 14e412ebf75b0e06ab53a1c8f9dd1be6ad1e2680..a643f3fb7bf6ae8394fa43adf5a2149137d46c4b 100644 ---- a/src/main/java/io/papermc/paper/adventure/ChatProcessor.java -+++ b/src/main/java/io/papermc/paper/adventure/ChatProcessor.java -@@ -317,7 +317,7 @@ public final class ChatProcessor { - - private void sendToServer(final ChatType.Bound chatType, final @Nullable Function msgFunction) { - final PlayerChatMessage toConsoleMessage = msgFunction == null ? ChatProcessor.this.message : ChatProcessor.this.message.withUnsignedContent(msgFunction.apply(ChatProcessor.this.server.console)); -- ChatProcessor.this.server.logChatMessage(toConsoleMessage.decoratedContent(), chatType, ChatProcessor.this.server.getPlayerList().verifyChatTrusted(toConsoleMessage) ? null : "Not Secure"); -+ ChatProcessor.this.server.logChatMessage(toConsoleMessage.decoratedContent(), chatType, ChatProcessor.this.server.getPlayerList().verifyChatTrusted(toConsoleMessage) || space.bxteam.divinemc.configuration.DivineConfig.noChatSign ? null : "Not Secure"); // DivineMC - No chat sign - } - } - -diff --git a/src/main/java/net/minecraft/commands/arguments/ArgumentSignatures.java b/src/main/java/net/minecraft/commands/arguments/ArgumentSignatures.java -index 479e6e2aa88a22ef7f8fccb06add6806f5b71d9d..370543f7f710971a37a0d05da8ef484a91d79f01 100644 ---- a/src/main/java/net/minecraft/commands/arguments/ArgumentSignatures.java -+++ b/src/main/java/net/minecraft/commands/arguments/ArgumentSignatures.java -@@ -14,9 +14,16 @@ public record ArgumentSignatures(List entries) { - private static final int MAX_ARGUMENT_NAME_LENGTH = 16; - - public ArgumentSignatures(FriendlyByteBuf buf) { -- this(buf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 8), ArgumentSignatures.Entry::new)); -+ this(readSign(buf)); // DivineMC - No chat sign - } - -+ // DivineMC start - No chat sign -+ private static List readSign(FriendlyByteBuf buf) { -+ var entries = buf.readCollection(FriendlyByteBuf.limitValue(ArrayList::new, 8), Entry::new); -+ return space.bxteam.divinemc.configuration.DivineConfig.noChatSign ? List.of() : entries; -+ } -+ // DivineMC end -+ - public void write(FriendlyByteBuf buf) { - buf.writeCollection(this.entries, (buf2, entry) -> entry.write(buf2)); - } -diff --git a/src/main/java/net/minecraft/network/FriendlyByteBuf.java b/src/main/java/net/minecraft/network/FriendlyByteBuf.java -index a523a83aec3a6ecbec4d60a187edc0c0167d15b4..b7c6b3b31860d372bead4cd2d3ea00fa4ffe1be3 100644 ---- a/src/main/java/net/minecraft/network/FriendlyByteBuf.java -+++ b/src/main/java/net/minecraft/network/FriendlyByteBuf.java -@@ -129,6 +129,16 @@ public class FriendlyByteBuf extends ByteBuf { - // Paper end - Adventure; add max length parameter - DataResult dataresult = codec.encodeStart(JsonOps.INSTANCE, value); - -+ // DivineMC start - No chat sign -+ if (codec == net.minecraft.network.protocol.status.ServerStatus.CODEC) { -+ JsonElement element = dataresult.getOrThrow(string -> new EncoderException("Failed to encode: " + string + " " + value)); -+ element.getAsJsonObject().addProperty("preventsChatReports", space.bxteam.divinemc.configuration.DivineConfig.noChatSign); -+ -+ this.writeUtf(GSON.toJson(element)); -+ return; -+ } -+ // DivineMC end -+ - this.writeUtf(FriendlyByteBuf.GSON.toJson((JsonElement) dataresult.getOrThrow((s) -> { - return new EncoderException("Failed to encode: " + s + " " + String.valueOf(value)); - })), maxLength); // Paper - Adventure; add max length parameter -diff --git a/src/main/java/net/minecraft/network/protocol/game/ServerboundChatPacket.java b/src/main/java/net/minecraft/network/protocol/game/ServerboundChatPacket.java -index 07df3299f1d1aa5506e1f6f146347d53e0278d9c..02fafc64d6178f2ae0be36b38ed5de7e0c31448c 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ServerboundChatPacket.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ServerboundChatPacket.java -@@ -16,7 +16,7 @@ public record ServerboundChatPacket(String message, Instant timeStamp, long salt - ); - - private ServerboundChatPacket(FriendlyByteBuf buf) { -- this(buf.readUtf(256), buf.readInstant(), buf.readLong(), buf.readNullable(MessageSignature::read), new LastSeenMessages.Update(buf)); -+ this(buf.readUtf(256), buf.readInstant(), buf.readLong(), buf.readNullable(ServerboundChatPacket::readSign), new LastSeenMessages.Update(buf)); // DivineMC - No chat sign - } - - private void write(FriendlyByteBuf buf) { -@@ -27,6 +27,14 @@ public record ServerboundChatPacket(String message, Instant timeStamp, long salt - this.lastSeenMessages.write(buf); - } - -+ // DivineMC start - No chat sign -+ private static MessageSignature readSign(FriendlyByteBuf buf) { -+ byte[] bs = new byte[256]; -+ buf.readBytes(bs); -+ return space.bxteam.divinemc.configuration.DivineConfig.noChatSign ? null : new MessageSignature(bs); -+ } -+ // DivineMC end -+ - @Override - public PacketType type() { - return GamePacketTypes.SERVERBOUND_CHAT; -diff --git a/src/main/java/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java b/src/main/java/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java -index 5705cb920084b775cce4b361683b32c6b6e003ed..9c031dd0fa279a23405e5b7d77e4c11e5a762684 100644 ---- a/src/main/java/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java -+++ b/src/main/java/net/minecraft/network/protocol/game/ServerboundChatSessionUpdatePacket.java -@@ -26,6 +26,11 @@ public record ServerboundChatSessionUpdatePacket(RemoteChatSession.Data chatSess - - @Override - public void handle(ServerGamePacketListener listener) { -+ // DivineMC start - No chat sign -+ if (space.bxteam.divinemc.configuration.DivineConfig.noChatSign) { -+ return; -+ } -+ // DivineMC end - listener.handleChatSessionUpdate(this); - } - } -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index 75919064593167b51c4325ba6d500366ba8408f1..23b3bfaebb8bfbb3bed77535526235c3db9e750f 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -705,7 +705,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - // Paper start - Add setting for proxy online mode status - return dedicatedserverproperties.enforceSecureProfile - && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() -- && this.services.canValidateProfileKeys(); -+ && this.services.canValidateProfileKeys() && !DivineConfig.noChatSign; // DivineMC - No chat sign - // Paper end - Add setting for proxy online mode status - } - -diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -index df48c2754dc1ebd52addd8ae768cba5916ce3969..955cfbe0bd97d65fb757d6e9e76ec60440bc5b84 100644 ---- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java -@@ -332,10 +332,24 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack - } - - public void send(Packet packet) { -+ // DivineMC start - No chat sign -+ if (space.bxteam.divinemc.configuration.DivineConfig.noChatSign) { -+ if (this instanceof ServerGamePacketListenerImpl && packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket chat) { -+ packet = new net.minecraft.network.protocol.game.ClientboundSystemChatPacket(chat.chatType().decorate(chat.unsignedContent() != null ? chat.unsignedContent() : Component.literal(chat.body().content())), false); -+ } -+ } -+ // DivineMC end - this.send(packet, (PacketSendListener) null); - } - - public void send(Packet packet, @Nullable PacketSendListener callbacks) { -+ // DivineMC start - No chat sign -+ if (space.bxteam.divinemc.configuration.DivineConfig.noChatSign) { -+ if (packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket chat && callbacks != null) { -+ callbacks = null; -+ } -+ } -+ // DivineMC end - // CraftBukkit start - if (packet == null || this.processedDisconnect) { // Spigot - return; -diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java -index 5b1705794a8c3914cb11fdd35f75c8e0c128ecd0..10f6b7e9fdc64760a8bcce34f5f453074a305a94 100644 ---- a/src/main/java/net/minecraft/server/players/PlayerList.java -+++ b/src/main/java/net/minecraft/server/players/PlayerList.java -@@ -1426,7 +1426,7 @@ public abstract class PlayerList { - } - - public boolean verifyChatTrusted(PlayerChatMessage message) { // Paper - private -> public -- return message.hasSignature() && !message.hasExpiredServer(Instant.now()); -+ return space.bxteam.divinemc.configuration.DivineConfig.noChatSign || (message.hasSignature() && !message.hasExpiredServer(Instant.now())); // DivineMC - No chat sign - } - - // CraftBukkit start -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -index aaf3afd0fe1f13ed7375d272e2690e9db0410417..261a55a4a5171f4ceb57fda8532bc1f795dc30ed 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -168,4 +168,9 @@ public class DivineConfig { - optimizedDragonRespawn = getBoolean("settings.optimizations.optimized-dragon-respawn", optimizedDragonRespawn); - optimizeNoiseGeneration = getBoolean("settings.optimizations.optimize-noise-generation", optimizeNoiseGeneration); - } -+ -+ public static boolean noChatSign = true; -+ private static void chatMessageSignatures() { -+ noChatSign = getBoolean("settings.no-chat-sign", noChatSign); -+ } - } diff --git a/patches/server/0040-Remove-vanilla-username-check.patch b/patches/server/0040-Remove-vanilla-username-check.patch deleted file mode 100644 index 615c2c4..0000000 --- a/patches/server/0040-Remove-vanilla-username-check.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 24 Jun 2024 14:05:44 +0300 -Subject: [PATCH] Remove vanilla username check - - -diff --git a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java -index 8849862b45ccbbc635a1c316e9870bca81e55c04..85b7c7742a2cc678aec317487fc811b9680e87d6 100644 ---- a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java -+++ b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java -@@ -295,7 +295,7 @@ public class CraftPlayerProfile implements PlayerProfile, SharedPlayerProfile { - - private static GameProfile createAuthLibProfile(UUID uniqueId, String name) { - Preconditions.checkArgument(name == null || name.length() <= 16, "Name cannot be longer than 16 characters"); -- Preconditions.checkArgument(name == null || StringUtil.isValidPlayerName(name), "The name of the profile contains invalid characters: %s", name); -+ Preconditions.checkArgument(name == null || space.bxteam.divinemc.configuration.DivineConfig.removeVanillaUsernameCheck || StringUtil.isValidPlayerName(name), "The name of the profile contains invalid characters: %s", name); // DivineMC - Remove vanilla username check - return new GameProfile( - uniqueId != null ? uniqueId : Util.NIL_UUID, - name != null ? name : "" -diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -index 16069b9cbf6c7679c28a2e9a54e77d23cd10e541..1b33f08b1d4730b28f61435488a1e1c04051c8dc 100644 ---- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java -@@ -174,7 +174,8 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, - public void handleHello(ServerboundHelloPacket packet) { - Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet", new Object[0]); - // Paper start - Validate usernames -- if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() -+ if (!space.bxteam.divinemc.configuration.DivineConfig.removeVanillaUsernameCheck // DivineMC - Remove vanilla username check -+ && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() - && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.performUsernameValidation - && !this.iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation) { - Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username", new Object[0]); -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -index 261a55a4a5171f4ceb57fda8532bc1f795dc30ed..028589d94e04828686ff09e6a6b1a8fc71f054a0 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -156,8 +156,10 @@ public class DivineConfig { - } - - public static boolean disableNonEditableSignWarning = true; -+ public static boolean removeVanillaUsernameCheck = false; - private static void miscSettings() { - disableNonEditableSignWarning = getBoolean("settings.misc.disable-non-editable-sign-warning", disableNonEditableSignWarning); -+ removeVanillaUsernameCheck = getBoolean("settings.misc.remove-vanilla-username-check", removeVanillaUsernameCheck); - } - - public static boolean biomeManagerOptimization = true; diff --git a/patches/server/0041-Akarin-Save-Json-list-asynchronously.patch b/patches/server/0041-Akarin-Save-Json-list-asynchronously.patch deleted file mode 100644 index c17c2d8..0000000 --- a/patches/server/0041-Akarin-Save-Json-list-asynchronously.patch +++ /dev/null @@ -1,83 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 8 Jul 2024 09:03:56 +0300 -Subject: [PATCH] Akarin: Save Json list asynchronously - -Original project: https://github.com/Akarin-project/Akarin - -diff --git a/src/main/java/net/minecraft/server/players/StoredUserList.java b/src/main/java/net/minecraft/server/players/StoredUserList.java -index c038da20b76c0b7b1c18471b20be01e849d29f3a..e961374a6ba23c0e46b01e8fa01656c047989483 100644 ---- a/src/main/java/net/minecraft/server/players/StoredUserList.java -+++ b/src/main/java/net/minecraft/server/players/StoredUserList.java -@@ -103,37 +103,47 @@ public abstract class StoredUserList> { - } - - public void save() throws IOException { -- this.removeExpired(); // Paper - remove expired values before saving -- JsonArray jsonarray = new JsonArray(); -- Stream stream = this.map.values().stream().map((jsonlistentry) -> { // CraftBukkit - decompile error -- JsonObject jsonobject = new JsonObject(); -+ Runnable saveTask = () -> { // DivineMC - Save json list async -+ this.removeExpired(); // Paper - remove expired values before saving -+ JsonArray jsonarray = new JsonArray(); -+ Stream stream = this.map.values().stream().map((jsonlistentry) -> { // CraftBukkit - decompile error -+ JsonObject jsonobject = new JsonObject(); - -- Objects.requireNonNull(jsonlistentry); -- return (JsonObject) Util.make(jsonobject, jsonlistentry::serialize); -- }); -+ Objects.requireNonNull(jsonlistentry); -+ return (JsonObject) Util.make(jsonobject, jsonlistentry::serialize); -+ }); - -- Objects.requireNonNull(jsonarray); -- stream.forEach(jsonarray::add); -- BufferedWriter bufferedwriter = Files.newWriter(this.file, StandardCharsets.UTF_8); -+ Objects.requireNonNull(jsonarray); -+ stream.forEach(jsonarray::add); -+ -+ try { // DivineMC - Save json list async -+ BufferedWriter bufferedwriter = Files.newWriter(this.file, StandardCharsets.UTF_8); - -- try { -- StoredUserList.GSON.toJson(jsonarray, StoredUserList.GSON.newJsonWriter(bufferedwriter)); -- } catch (Throwable throwable) { -- if (bufferedwriter != null) { - try { -- bufferedwriter.close(); -- } catch (Throwable throwable1) { -- throwable.addSuppressed(throwable1); -- } -- } -+ StoredUserList.GSON.toJson(jsonarray, StoredUserList.GSON.newJsonWriter(bufferedwriter)); -+ } catch (Throwable throwable) { -+ if (bufferedwriter != null) { -+ try { -+ bufferedwriter.close(); -+ } catch (Throwable throwable1) { -+ throwable.addSuppressed(throwable1); -+ } -+ } - -- throw throwable; -- } -+ throw throwable; -+ } - -- if (bufferedwriter != null) { -- bufferedwriter.close(); -- } -+ if (bufferedwriter != null) { -+ bufferedwriter.close(); -+ } - -+ // DivineMC start -+ } catch (Exception e) { -+ StoredUserList.LOGGER.warn("Failed to async save " + this.file, e); -+ } -+ }; -+ io.papermc.paper.util.MCUtil.scheduleAsyncTask(saveTask); -+ // DivineMC end - } - - public void load() throws IOException { diff --git a/patches/server/0042-Fix-MC-223153.patch b/patches/server/0042-Fix-MC-223153.patch deleted file mode 100644 index 0d4277f..0000000 --- a/patches/server/0042-Fix-MC-223153.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Thu, 11 Jul 2024 19:11:08 +0300 -Subject: [PATCH] Fix MC-223153 - -Original bug on Mojira: https://bugs.mojang.com/browse/MC-223153 -Took here: https://github.com/Winds-Studio/Leaf/blob/ver/1.21/patches/server/0083-Fix-MC-223153.patch - -diff --git a/src/main/java/net/minecraft/world/level/block/Blocks.java b/src/main/java/net/minecraft/world/level/block/Blocks.java -index c5f8227cd9631d98cc8404e3f6d6109a55c617aa..041013600b2b76c146d7f73e9429376ac09faf23 100644 ---- a/src/main/java/net/minecraft/world/level/block/Blocks.java -+++ b/src/main/java/net/minecraft/world/level/block/Blocks.java -@@ -6600,6 +6600,7 @@ public class Blocks { - .mapColor(MapColor.COLOR_ORANGE) - .instrument(NoteBlockInstrument.BASEDRUM) - .requiresCorrectToolForDrops() -+ .sound(SoundType.COPPER) // DivineMC - Fix MC-223153 - .strength(5.0F, 6.0F) - ); - public static final Block RAW_GOLD_BLOCK = register( diff --git a/patches/server/0043-Fix-MC-119417.patch b/patches/server/0043-Fix-MC-119417.patch deleted file mode 100644 index b6fc183..0000000 --- a/patches/server/0043-Fix-MC-119417.patch +++ /dev/null @@ -1,20 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Thu, 11 Jul 2024 19:12:14 +0300 -Subject: [PATCH] Fix MC-119417 - -Original bug on Mojira: https://bugs.mojang.com/browse/MC-119417 -Took here: https://github.com/Winds-Studio/Leaf/blob/ver/1.21/patches/server/0082-Fix-MC-119417.patch - -diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java -index 6f39fd99ffb28d6c0267f4251c54af0c519289db..0da41d7b11ac98d4cbd059f48be75fb43012118e 100644 ---- a/src/main/java/net/minecraft/server/level/ServerPlayer.java -+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java -@@ -2487,6 +2487,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple - this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, (float) gameMode.getId())); - if (gameMode == GameType.SPECTATOR) { - this.removeEntitiesOnShoulder(); -+ this.stopSleeping(); // DivineMC - Fix MC-119417 - this.stopRiding(); - EnchantmentHelper.stopLocationBasedEffects(this); - } else { diff --git a/patches/server/0044-Fix-MC-200418.patch b/patches/server/0044-Fix-MC-200418.patch deleted file mode 100644 index 569014e..0000000 --- a/patches/server/0044-Fix-MC-200418.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Thu, 11 Jul 2024 19:15:23 +0300 -Subject: [PATCH] Fix MC-200418 - -Original bug on Mojira: https://bugs.mojang.com/browse/MC-200418 -Took here: https://github.com/Winds-Studio/Leaf/blob/ver/1.21/patches/server/0081-Fix-MC-200418.patch - -diff --git a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -index bba3562bf316878e7b8ba6a138889d9583a1b0f6..7ce3a667b819c096257732d8505b1aae03b0a148 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -+++ b/src/main/java/net/minecraft/world/entity/monster/ZombieVillager.java -@@ -330,6 +330,12 @@ public class ZombieVillager extends Zombie implements VillagerDataHolder { - if (!this.isSilent()) { - world.levelEvent((Player) null, 1027, this.blockPosition(), 0); - } -+ -+ // DivineMC start - Fix MC-200418 -+ if (entityvillager.isPassenger() && entityvillager.getVehicle() instanceof net.minecraft.world.entity.animal.Chicken && entityvillager.isBaby()) { -+ entityvillager.removeVehicle(); -+ } -+ // DivineMC end - // CraftBukkit start - }, EntityTransformEvent.TransformReason.CURED, CreatureSpawnEvent.SpawnReason.CURED); - if (converted == null) { diff --git a/patches/server/0045-Option-to-disable-moved-wrongly-threshold.patch b/patches/server/0045-Option-to-disable-moved-wrongly-threshold.patch deleted file mode 100644 index 25f3b0e..0000000 --- a/patches/server/0045-Option-to-disable-moved-wrongly-threshold.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 13 Jul 2024 01:44:48 +0300 -Subject: [PATCH] Option to disable moved wrongly threshold - - -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 0fef16501c1a971dc8bd2616c7476dffabb6ea08..ac1d8c5eb616d11fc1bda929a8607daf2d616b46 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -592,7 +592,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - } - // Paper end - Prevent moving into unloaded chunks - -- if (d10 - d9 > Math.max(100.0D, Math.pow((double) (org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed), 2)) && !this.isSingleplayerOwner()) { -+ if (!space.bxteam.divinemc.configuration.DivineConfig.disableMovedWronglyThreshold && d10 - d9 > Math.max(100.0D, Math.pow((double) (org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed), 2)) && !this.isSingleplayerOwner()) { // DivineMC - Option to disable moved wrongly threshold - // CraftBukkit end - ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", new Object[]{entity.getName().getString(), this.player.getName().getString(), d6, d7, d8}); - this.send(ClientboundMoveVehiclePacket.fromEntity(entity)); -@@ -628,8 +628,8 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - d10 = d6 * d6 + d7 * d7 + d8 * d8; - boolean flag2 = false; - -- if (d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot -- flag2 = true; // Paper - diff on change, this should be moved wrongly -+ if (!space.bxteam.divinemc.configuration.DivineConfig.disableMovedWronglyThreshold && d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot // DivineMC - Option to disable moved wrongly threshold -+ flag2 = true;// Paper - diff on change, this should be moved wrongly - ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", new Object[]{entity.getName().getString(), this.player.getName().getString(), Math.sqrt(d10)}); - } - -@@ -1537,7 +1537,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY, - toX, toY, toZ, toYaw, toPitch, true); - if (!event.isAllowed()) { -- if (event.getLogWarning()) -+ if (!space.bxteam.divinemc.configuration.DivineConfig.disableMovedWronglyThreshold && event.getLogWarning()) // DivineMC - Option to disable moved wrongly threshold - ServerGamePacketListenerImpl.LOGGER.warn("{} moved too quickly! {},{},{}", new Object[]{this.player.getName().getString(), d6, d7, d8}); - this.teleport(this.player.getX(), this.player.getY(), this.player.getZ(), this.player.getYRot(), this.player.getXRot()); - return; -@@ -1607,7 +1607,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - d10 = d6 * d6 + d7 * d7 + d8 * d8; - boolean movedWrongly = false; // Paper - Add fail move event; rename - -- if (!this.player.isChangingDimension() && d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot -+ if (!space.bxteam.divinemc.configuration.DivineConfig.disableMovedWronglyThreshold && !this.player.isChangingDimension() && d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot // DivineMC - Option to disable moved wrongly threshold - // Paper start - Add fail move event - io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_WRONGLY, - toX, toY, toZ, toYaw, toPitch, true); -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -index 028589d94e04828686ff09e6a6b1a8fc71f054a0..9deb9310b6829f2e1bac36327bbb21cee099b22b 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -157,9 +157,11 @@ public class DivineConfig { - - public static boolean disableNonEditableSignWarning = true; - public static boolean removeVanillaUsernameCheck = false; -+ public static boolean disableMovedWronglyThreshold = false; - private static void miscSettings() { - disableNonEditableSignWarning = getBoolean("settings.misc.disable-non-editable-sign-warning", disableNonEditableSignWarning); - removeVanillaUsernameCheck = getBoolean("settings.misc.remove-vanilla-username-check", removeVanillaUsernameCheck); -+ disableMovedWronglyThreshold = getBoolean("settings.misc.disable-moved-wrongly-threshold", disableMovedWronglyThreshold); - } - - public static boolean biomeManagerOptimization = true; diff --git a/patches/server/0046-Implement-Secure-Seed.patch b/patches/server/0046-Implement-Secure-Seed.patch deleted file mode 100644 index 56da7a7..0000000 --- a/patches/server/0046-Implement-Secure-Seed.patch +++ /dev/null @@ -1,832 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sat, 20 Jul 2024 22:04:52 +0300 -Subject: [PATCH] Implement Secure Seed - -Original license: GPLv3 -Original project: https://github.com/plasmoapp/matter - -diff --git a/src/main/java/net/minecraft/server/commands/SeedCommand.java b/src/main/java/net/minecraft/server/commands/SeedCommand.java -index 0b500b19a99fa6c2740c0db350a166462668df9c..f13185628dec90a044bf03cf38394b5be8ab2003 100644 ---- a/src/main/java/net/minecraft/server/commands/SeedCommand.java -+++ b/src/main/java/net/minecraft/server/commands/SeedCommand.java -@@ -12,6 +12,17 @@ public class SeedCommand { - long l = context.getSource().getLevel().getSeed(); - Component component = ComponentUtils.copyOnClickText(String.valueOf(l)); - context.getSource().sendSuccess(() -> Component.translatable("commands.seed.success", component), false); -+ -+ // DivineMC start - Implement Secure Seed -+ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ space.bxteam.divinemc.seed.Globals.setupGlobals(context.getSource().getLevel()); -+ String seedStr = space.bxteam.divinemc.seed.Globals.seedToString(space.bxteam.divinemc.seed.Globals.worldSeed); -+ Component featureSeedComponent = ComponentUtils.copyOnClickText(seedStr); -+ -+ context.getSource().sendSuccess(() -> Component.translatable(("Feature seed: %s"), featureSeedComponent), false); -+ } -+ // DivineMC 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 05e16103af3fd276f0196ddf1a2e5b729b025c34..e0118dfee89d4319f70a0d2f84ba4c21b03a9ed9 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java -@@ -167,7 +167,17 @@ public class DedicatedServerProperties extends Settings { - return GsonHelper.parse(!s1.isEmpty() ? s1 : "{}"); - }, new JsonObject()), (String) this.get("level-type", (s1) -> { -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index aea9a45c0916501f71018d3250b56da435f5664e..564395b9be9f161dc3ea7956b9b2432dc8bf45fa 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -692,6 +692,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon - } - - public ChunkGenerator getGenerator() { -+ space.bxteam.divinemc.seed.Globals.setupGlobals(level); // DivineMC - Implement Secure Seed - 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 fe8a1a073920b7cbbe3791ac1fcac3fccec6b9f7..b0095c0848ca0162944961a24c7b807fb5846b06 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -644,6 +644,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - chunkgenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkgenerator, gen); - } - // CraftBukkit end -+ space.bxteam.divinemc.seed.Globals.setupGlobals(this); // DivineMC - Implement Secure Seed - boolean flag2 = minecraftserver.forceSynchronousWrites(); - DataFixer datafixer = minecraftserver.getFixerUpper(); - EntityPersistentStorage entitypersistentstorage = new EntityStorage(new SimpleRegionStorage(new RegionStorageInfo(convertable_conversionsession.getLevelId(), resourcekey, "entities"), convertable_conversionsession.getDimensionPath(resourcekey).resolve("entities"), datafixer, flag2, DataFixTypes.ENTITY_CHUNK), this, minecraftserver); -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 d58f7b251d0c322d63e7e5e8ed30b41427a11227..7fe1ae012dc3fec82bd75c6f44f1d32bf30fd666 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Slime.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Slime.java -@@ -433,8 +433,13 @@ public class Slime extends Mob implements Enemy { - return false; - } - -- ChunkPos chunkcoordintpair = new ChunkPos(pos); -- boolean flag = world.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(chunkcoordintpair.x, chunkcoordintpair.z, ((WorldGenLevel) world).getSeed(), world.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper -+ ChunkPos chunkcoordintpair = new ChunkPos(pos); -+ // DivineMC start - Implement Secure Seed -+ boolean isSlimeChunk = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? world.getChunk(chunkcoordintpair.x, chunkcoordintpair.z).isSlimeChunk() -+ : WorldgenRandom.seedSlimeChunk(chunkcoordintpair.x, chunkcoordintpair.z, ((WorldGenLevel) world).getSeed(), world.getMinecraftWorld().spigotConfig.slimeSeed).nextInt(10) == 0; // Spigot // Paper -+ boolean flag = world.getMinecraftWorld().paperConfig().entities.spawning.allChunksAreSlimeChunks || isSlimeChunk; -+ // DivineMC end - - // 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 f87abb22dd161b2b74401086de80dc95c9ac2dbb..5ca052b94ff9fa36e3241a745229839a1e9bc852 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkAccess.java -@@ -85,6 +85,11 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh - protected final LevelHeightAccessor levelHeightAccessor; - protected final LevelChunkSection[] sections; - -+ // DivineMC start - Implement Secure Seed -+ private boolean slimeChunk; -+ private boolean hasComputedSlimeChunk; -+ // DivineMC end -+ - // 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); -@@ -189,6 +194,17 @@ public abstract class ChunkAccess implements BiomeManager.NoiseBiomeSource, Ligh - return GameEventListenerRegistry.NOOP; - } - -+ // DivineMC start - Implement Secure Seed -+ public boolean isSlimeChunk() { -+ if (!hasComputedSlimeChunk) { -+ hasComputedSlimeChunk = true; -+ slimeChunk = space.bxteam.divinemc.seed.WorldgenCryptoRandom.seedSlimeChunk(chunkPos.x, chunkPos.z).nextInt(10) == 0; -+ } -+ -+ return slimeChunk; -+ } -+ // DivineMC end -+ - public abstract BlockState getBlockState(final int x, final int y, final int z); // Paper - @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 e0cb360ece042c4fc6aa0d10106923fe25288f5c..b42ab192fec723a17acd70aeb093895c8938ece2 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGenerator.java -@@ -345,7 +345,11 @@ public abstract class ChunkGenerator { - return structure.step().ordinal(); - })); - List list = (List) this.featuresPerStep.get(); -- WorldgenRandom seededrandom = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom seededrandom = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(blockposition.getX(), blockposition.getZ(), space.bxteam.divinemc.seed.Globals.Salt.UNDEFINED, 0) -+ : new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); -+ // DivineMC end - long i = seededrandom.setDecorationSeed(generatoraccessseed.getSeed(), blockposition.getX(), blockposition.getZ()); - Set> set = new ObjectArraySet(); - -@@ -584,9 +588,18 @@ 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); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom seededrandom; -+ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ seededrandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( -+ chunkcoordintpair.x, chunkcoordintpair.z, space.bxteam.divinemc.seed.Globals.Salt.GENERATE_FEATURE, 0 -+ ); -+ } else { -+ seededrandom = new WorldgenRandom(new LegacyRandomSource(0L)); -+ -+ seededrandom.setLargeFeatureSeed(placementCalculator.getLevelSeed(), chunkcoordintpair.x, chunkcoordintpair.z); -+ } -+ // DivineMC end - 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 a20520a6bd28bae1cee82258ac49d9753faba2bd..a0fbcf642f5a2aa6354d4287961d93779893d852 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java -+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkGeneratorStructureState.java -@@ -224,15 +224,20 @@ public class ChunkGeneratorStructureState { - List> list = new ArrayList(j); - int k = placement.spread(); - HolderSet holderset = placement.preferredBiomes(); -- RandomSource randomsource = RandomSource.create(); -- -- // Paper start - Add missing structure set seed configs -- if (this.conf.strongholdSeed != null && structureSetEntry.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { -- randomsource.setSeed(this.conf.strongholdSeed); -- } else { -- // Paper end - Add missing structure set seed configs -- randomsource.setSeed(this.concentricRingsSeed); -- } // Paper - Add missing structure set seed configs -+ // DivineMC start - Implement Secure Seed -+ RandomSource randomsource = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(0, 0, space.bxteam.divinemc.seed.Globals.Salt.STRONGHOLDS, 0) -+ : RandomSource.create(); -+ -+ if (!space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ // Paper start - Add missing structure set seed configs -+ if (this.conf.strongholdSeed != null && structureSetEntry.is(net.minecraft.world.level.levelgen.structure.BuiltinStructureSets.STRONGHOLDS)) { -+ randomsource.setSeed(this.conf.strongholdSeed); -+ } else { -+ // Paper end - Add missing structure set seed configs -+ randomsource.setSeed(this.concentricRingsSeed); -+ } // Paper - Add missing structure set seed configs -+ } // DivineMC end - double d0 = randomsource.nextDouble() * Math.PI * 2.0D; - int l = 0; - int i1 = 0; -diff --git a/src/main/java/net/minecraft/world/level/chunk/status/ChunkStep.java b/src/main/java/net/minecraft/world/level/chunk/status/ChunkStep.java -index f9aad1b8c02b70e620efdc2a58cadf4fff0f3ed5..261661977c3670d5173c677f041b2305a212718c 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/status/ChunkStep.java -+++ b/src/main/java/net/minecraft/world/level/chunk/status/ChunkStep.java -@@ -60,6 +60,7 @@ public final class ChunkStep implements ca.spottedleaf.moonrise.patches.chunk_sy - } - - public CompletableFuture apply(WorldGenContext context, StaticCache2D staticCache2D, ChunkAccess chunk) { -+ space.bxteam.divinemc.seed.Globals.setupGlobals(context.level()); // DivineMC - Implement Secure Seed - if (chunk.getPersistedStatus().isBefore(this.targetStatus)) { - ProfiledDuration profiledDuration = JvmProfiler.INSTANCE.onChunkGenerate(chunk.getPos(), context.level().dimension(), this.targetStatus.getName()); - return this.task.doWork(context, this, staticCache2D, chunk).thenApply(generated -> this.completeChunkGeneration(generated, profiledDuration)); -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 41c19e4e7bde4632879da564f52f3d373de27ec4..6070110bdfada835dd6b04f66644a47aa868fe4e 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/WorldOptions.java -@@ -9,8 +9,19 @@ import net.minecraft.util.RandomSource; - import org.apache.commons.lang3.StringUtils; - - public class WorldOptions { -+ // DivineMC start - Implement Secure Seed -+ private static final boolean isSecureSeedEnabled = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed; - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( -- instance -> instance.group( -+ instance -> isSecureSeedEnabled -+ ? 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)) -+ : 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), -@@ -18,8 +29,14 @@ public class WorldOptions { - ) - .apply(instance, instance.stable(WorldOptions::new)) - ); -- public static final WorldOptions DEMO_OPTIONS = new WorldOptions((long)"North Carolina".hashCode(), true, true); -+ // DivineMC end -+ // DivineMC start - Implement Secure Seed -+ public static final WorldOptions DEMO_OPTIONS = isSecureSeedEnabled -+ ? new WorldOptions((long) "North Carolina".hashCode(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), true, true) -+ : new WorldOptions("North Carolina".hashCode(), true, true); -+ // DivineMC end - private final long seed; -+ private long[] featureSeed = space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(); // DivineMC - Implement Secure Seed - private final boolean generateStructures; - private final boolean generateBonusChest; - private final Optional legacyCustomOptions; -@@ -28,8 +45,18 @@ public class WorldOptions { - this(seed, generateStructures, bonusChest, Optional.empty()); - } - -+ public WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest) { -+ this(seed, featureSeed, generateStructures, bonusChest, Optional.empty()); -+ } -+ - public static WorldOptions defaultWithRandomSeed() { -- return new WorldOptions(randomSeed(), true, false); -+ return isSecureSeedEnabled -+ ? new WorldOptions(randomSeed(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), true, false) -+ : new WorldOptions(randomSeed(), true, false); -+ } -+ -+ private WorldOptions(long seed, java.util.stream.LongStream featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { -+ this(seed, featureSeed.toArray(), generateStructures, bonusChest, legacyCustomOptions); - } - - public static WorldOptions testWorldWithRandomSeed() { -@@ -43,10 +70,27 @@ public class WorldOptions { - this.legacyCustomOptions = legacyCustomOptions; - } - -+ // DivineMC start - Implement Secure Seed -+ private WorldOptions(long seed, long[] featureSeed, boolean generateStructures, boolean bonusChest, Optional legacyCustomOptions) { -+ this(seed, generateStructures, bonusChest, legacyCustomOptions); -+ this.featureSeed = featureSeed; -+ } -+ // DivineMC end -+ - public long seed() { - return this.seed; - } - -+ // DivineMC start - Implement Secure Seed -+ public long[] featureSeed() { -+ return this.featureSeed; -+ } -+ -+ public java.util.stream.LongStream featureSeedStream() { -+ return java.util.stream.LongStream.of(this.featureSeed); -+ } -+ // DivineMC end -+ - public boolean generateStructures() { - return this.generateStructures; - } -@@ -59,17 +103,25 @@ public class WorldOptions { - return this.legacyCustomOptions.isPresent(); - } - -+ // DivineMC start - Implement Secure Seed - public WorldOptions withBonusChest(boolean bonusChest) { -- return new WorldOptions(this.seed, this.generateStructures, bonusChest, this.legacyCustomOptions); -+ return isSecureSeedEnabled -+ ? new WorldOptions(this.seed, this.featureSeed, this.generateStructures, bonusChest, this.legacyCustomOptions) -+ : new WorldOptions(this.seed, this.generateStructures, bonusChest, this.legacyCustomOptions); - } - - public WorldOptions withStructures(boolean structures) { -- return new WorldOptions(this.seed, structures, this.generateBonusChest, this.legacyCustomOptions); -+ return isSecureSeedEnabled -+ ? new WorldOptions(this.seed, this.featureSeed, structures, this.generateBonusChest, this.legacyCustomOptions) -+ : new WorldOptions(this.seed, structures, this.generateBonusChest, this.legacyCustomOptions); - } - - public WorldOptions withSeed(OptionalLong seed) { -- return new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions); -+ return isSecureSeedEnabled -+ ? new WorldOptions(seed.orElse(randomSeed()), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions) -+ : new WorldOptions(seed.orElse(randomSeed()), this.generateStructures, this.generateBonusChest, this.legacyCustomOptions); - } -+ // DivineMC end - - public static OptionalLong parseSeed(String seed) { - seed = seed.trim(); -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 270db8b29cdf65e9bb932637425214eefeca86b7..6cf307b4ddb87ad54ead02dd10290a7a825095e3 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 -@@ -41,7 +41,11 @@ 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())); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom worldgenRandom = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(0, 0, space.bxteam.divinemc.seed.Globals.Salt.GEODE_FEATURE, 0) -+ : new WorldgenRandom(new LegacyRandomSource(worldGenLevel.getSeed())); -+ // DivineMC end - 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 13f7fce1959c0f44e047616674198176e667067f..23528e2eef5d097cad44e8f51a6199a7bf718044 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 -@@ -248,6 +248,14 @@ public abstract class Structure { - } - - private static WorldgenRandom makeRandom(long seed, ChunkPos chunkPos) { -+ // DivineMC start - Implement Secure Seed -+ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ return new space.bxteam.divinemc.seed.WorldgenCryptoRandom( -+ chunkPos.x, chunkPos.z, space.bxteam.divinemc.seed.Globals.Salt.GENERATE_FEATURE, seed -+ ); -+ } -+ // DivineMC end -+ - WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); - worldgenRandom.setLargeFeatureSeed(seed, chunkPos.x, chunkPos.z); - return worldgenRandom; -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 f873a0a0734b4fe74ba5b5f8ae0cc3c78fa76b9f..1e29d5554ac2be3219f075092778e49022788723 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 -@@ -71,8 +71,17 @@ 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()); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom worldgenRandom; -+ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ worldgenRandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( -+ i, j, space.bxteam.divinemc.seed.Globals.Salt.POTENTIONAL_FEATURE, this.salt -+ ); -+ } else { -+ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); -+ worldgenRandom.setLargeFeatureWithSalt(seed, i, j, this.salt()); -+ } -+ // DivineMC end - 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 cbf13e4f2da6a27619e9bc9a7cd73bb6e69cad2a..8aebe917973f65123a3b0744172a307f95b05cb0 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 -@@ -118,8 +118,18 @@ 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 -- WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); -- worldgenRandom.setLargeFeatureWithSalt(seed, salt, chunkX, chunkZ); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom worldgenRandom; -+ if (space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) { -+ worldgenRandom = new space.bxteam.divinemc.seed.WorldgenCryptoRandom( -+ chunkX, chunkZ, space.bxteam.divinemc.seed.Globals.Salt.UNDEFINED, salt -+ ); -+ } else { -+ worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L)); -+ worldgenRandom.setLargeFeatureWithSalt(seed, salt, chunkX, chunkZ); -+ } -+ // DivineMC end -+ - return worldgenRandom.nextFloat() < frequency; - } - -diff --git a/src/main/java/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java b/src/main/java/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java -index 65a0d9e7dd742732974774daabce02e9e52039ac..53e1261ef3ec66d90bcc17e99cb21e7d8ef9ccea 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/structure/pools/JigsawPlacement.java -@@ -64,7 +64,11 @@ public class JigsawPlacement { - ChunkGenerator chunkGenerator = context.chunkGenerator(); - StructureTemplateManager structureTemplateManager = context.structureTemplateManager(); - LevelHeightAccessor levelHeightAccessor = context.heightAccessor(); -- WorldgenRandom worldgenRandom = context.random(); -+ // DivineMC start - Implement Secure Seed -+ WorldgenRandom worldgenRandom = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? new space.bxteam.divinemc.seed.WorldgenCryptoRandom(context.chunkPos().x, context.chunkPos().z, space.bxteam.divinemc.seed.Globals.Salt.JIGSAW_PLACEMENT, 0) -+ : context.random(); -+ // DivineMC end - Registry registry = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL); - Rotation rotation = Rotation.getRandom(worldgenRandom); - StructureTemplatePool structureTemplatePool = structurePool.unwrapKey() -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -index c2bffe3450ee9f768e00a23ec09df74d7a06d49b..922d1037352198432c16b782c892467c76e17428 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -@@ -206,7 +206,12 @@ public class CraftChunk implements Chunk { - @Override - public boolean isSlimeChunk() { - // 987234911L is deterimined in EntitySlime when seeing if a slime can spawn in a chunk -- return this.worldServer.paperConfig().entities.spawning.allChunksAreSlimeChunks || WorldgenRandom.seedSlimeChunk(this.getX(), this.getZ(), this.getWorld().getSeed(), worldServer.spigotConfig.slimeSeed).nextInt(10) == 0; // Paper -+ // DivineMC start - Implement Secure Seed -+ boolean isSlimeChunk = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? worldServer.getChunk(this.getX(), this.getZ()).isSlimeChunk() -+ : WorldgenRandom.seedSlimeChunk(this.getX(), this.getZ(), this.getWorld().getSeed(), worldServer.spigotConfig.slimeSeed).nextInt(10) == 0; // Paper -+ return this.worldServer.paperConfig().entities.spawning.allChunksAreSlimeChunks || isSlimeChunk; -+ // DivineMC end - } - - @Override -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 88343cbdaa95f7f2bf6a196b58f23195186749a2..62807b021b936477db6e266633767d81a9436bb2 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -@@ -1409,7 +1409,11 @@ public final class CraftServer implements Server { - iregistrycustom_dimension = leveldataanddimensions.dimensions().dimensionsRegistryAccess(); - } else { - LevelSettings worldsettings; -- WorldOptions worldoptions = new WorldOptions(creator.seed(), creator.generateStructures(), false); -+ // DivineMC start - Implement Secure Seed -+ WorldOptions worldoptions = space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed -+ ? new WorldOptions(creator.seed(), space.bxteam.divinemc.seed.Globals.createRandomWorldSeed(), creator.generateStructures(), false) -+ : new WorldOptions(creator.seed(), creator.generateStructures(), false); -+ // DivineMC end - WorldDimensions worlddimensions; - - DedicatedServerProperties.WorldDimensionData properties = new DedicatedServerProperties.WorldDimensionData(GsonHelper.parse((creator.generatorSettings().isEmpty()) ? "{}" : creator.generatorSettings()), creator.type().name().toLowerCase(Locale.ROOT)); -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -index 9deb9310b6829f2e1bac36327bbb21cee099b22b..c494d88cda016ff7a4da37ac1cee2a05876e595f 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -158,10 +158,12 @@ public class DivineConfig { - public static boolean disableNonEditableSignWarning = true; - public static boolean removeVanillaUsernameCheck = false; - public static boolean disableMovedWronglyThreshold = false; -+ public static boolean enableSecureSeed = false; - private static void miscSettings() { - disableNonEditableSignWarning = getBoolean("settings.misc.disable-non-editable-sign-warning", disableNonEditableSignWarning); - removeVanillaUsernameCheck = getBoolean("settings.misc.remove-vanilla-username-check", removeVanillaUsernameCheck); - disableMovedWronglyThreshold = getBoolean("settings.misc.disable-moved-wrongly-threshold", disableMovedWronglyThreshold); -+ enableSecureSeed = getBoolean("settings.misc.enable-secure-seed", enableSecureSeed); - } - - public static boolean biomeManagerOptimization = true; -diff --git a/src/main/java/space/bxteam/divinemc/seed/Globals.java b/src/main/java/space/bxteam/divinemc/seed/Globals.java -new file mode 100644 -index 0000000000000000000000000000000000000000..39d2d74f6a89ecbe42ef45d9fc500762a2dfce51 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/seed/Globals.java -@@ -0,0 +1,94 @@ -+package space.bxteam.divinemc.seed; -+ -+import com.google.common.collect.Iterables; -+import net.minecraft.server.level.ServerLevel; -+ -+import java.math.BigInteger; -+import java.security.SecureRandom; -+import java.util.Optional; -+ -+public class Globals { -+ public static final int WORLD_SEED_LONGS = 16; -+ public static final int WORLD_SEED_BITS = WORLD_SEED_LONGS * 64; -+ -+ public static final long[] worldSeed = new long[WORLD_SEED_LONGS]; -+ public static final ThreadLocal dimension = ThreadLocal.withInitial(() -> 0); -+ -+ public enum Salt { -+ UNDEFINED, -+ BASTION_FEATURE, -+ WOODLAND_MANSION_FEATURE, -+ MINESHAFT_FEATURE, -+ BURIED_TREASURE_FEATURE, -+ NETHER_FORTRESS_FEATURE, -+ PILLAGER_OUTPOST_FEATURE, -+ GEODE_FEATURE, -+ NETHER_FOSSIL_FEATURE, -+ OCEAN_MONUMENT_FEATURE, -+ RUINED_PORTAL_FEATURE, -+ POTENTIONAL_FEATURE, -+ GENERATE_FEATURE, -+ JIGSAW_PLACEMENT, -+ STRONGHOLDS, -+ POPULATION, -+ DECORATION, -+ SLIME_CHUNK -+ } -+ -+ public static void setupGlobals(ServerLevel world) { -+ if (!space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) return; -+ -+ long[] seed = world.getServer().getWorldData().worldGenOptions().featureSeed(); -+ System.arraycopy(seed, 0, worldSeed, 0, WORLD_SEED_LONGS); -+ int worldIndex = Iterables.indexOf(world.getServer().levelKeys(), it -> it == world.dimension()); -+ if (worldIndex == -1) -+ worldIndex = world.getServer().levelKeys().size(); // if we are in world construction it may not have been added to the map yet -+ dimension.set(worldIndex); -+ } -+ -+ public static long[] createRandomWorldSeed() { -+ long[] seed = new long[WORLD_SEED_LONGS]; -+ SecureRandom rand = new SecureRandom(); -+ for (int i = 0; i < WORLD_SEED_LONGS; i++) { -+ seed[i] = rand.nextLong(); -+ } -+ return seed; -+ } -+ -+ // 1024-bit string -> 16 * 64 long[] -+ public static Optional parseSeed(String seedStr) { -+ if (seedStr.isEmpty()) return Optional.empty(); -+ -+ if (seedStr.length() != WORLD_SEED_BITS) { -+ throw new IllegalArgumentException("Secure seed length must be " + WORLD_SEED_BITS + "-bit but found " + seedStr.length() + "-bit."); -+ } -+ -+ long[] seed = new long[WORLD_SEED_LONGS]; -+ -+ for (int i = 0; i < WORLD_SEED_LONGS; i++) { -+ int start = i * 64; -+ int end = start + 64; -+ String seedSection = seedStr.substring(start, end); -+ -+ BigInteger seedInDecimal = new BigInteger(seedSection, 2); -+ seed[i] = seedInDecimal.longValue(); -+ } -+ -+ return Optional.of(seed); -+ } -+ -+ // 16 * 64 long[] -> 1024-bit string -+ public static String seedToString(long[] seed) { -+ StringBuilder sb = new StringBuilder(); -+ -+ for (long longV : seed) { -+ // Convert to 64-bit binary string per long -+ // Use format to keep 64-bit length, and use 0 to complete space -+ String binaryStr = String.format("%64s", Long.toBinaryString(longV)).replace(' ', '0'); -+ -+ sb.append(binaryStr); -+ } -+ -+ return sb.toString(); -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/seed/Hashing.java b/src/main/java/space/bxteam/divinemc/seed/Hashing.java -new file mode 100644 -index 0000000000000000000000000000000000000000..5974e52ec4ad6422ef0eac19d0b32be233889a40 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/seed/Hashing.java -@@ -0,0 +1,73 @@ -+package space.bxteam.divinemc.seed; -+ -+public class Hashing { -+ // https://en.wikipedia.org/wiki/BLAKE_(hash_function) -+ // https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java -+ -+ private final static long[] blake2b_IV = { -+ 0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL, -+ 0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL, -+ 0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L -+ }; -+ -+ private final static byte[][] blake2b_sigma = { -+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, -+ {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, -+ {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, -+ {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, -+ {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, -+ {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, -+ {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, -+ {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, -+ {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, -+ {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, -+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, -+ {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3} -+ }; -+ -+ public static long[] hashWorldSeed(long[] worldSeed) { -+ long[] result = blake2b_IV.clone(); -+ result[0] ^= 0x01010040; -+ hash(worldSeed, result, new long[16], 0, false); -+ return result; -+ } -+ -+ public static void hash(long[] message, long[] chainValue, long[] internalState, long messageOffset, boolean isFinal) { -+ assert message.length == 16; -+ assert chainValue.length == 8; -+ assert internalState.length == 16; -+ -+ System.arraycopy(chainValue, 0, internalState, 0, chainValue.length); -+ System.arraycopy(blake2b_IV, 0, internalState, chainValue.length, 4); -+ internalState[12] = messageOffset ^ blake2b_IV[4]; -+ internalState[13] = blake2b_IV[5]; -+ if (isFinal) internalState[14] = ~blake2b_IV[6]; -+ internalState[15] = blake2b_IV[7]; -+ -+ for (int round = 0; round < 12; round++) { -+ G(message[blake2b_sigma[round][0]], message[blake2b_sigma[round][1]], 0, 4, 8, 12, internalState); -+ G(message[blake2b_sigma[round][2]], message[blake2b_sigma[round][3]], 1, 5, 9, 13, internalState); -+ G(message[blake2b_sigma[round][4]], message[blake2b_sigma[round][5]], 2, 6, 10, 14, internalState); -+ G(message[blake2b_sigma[round][6]], message[blake2b_sigma[round][7]], 3, 7, 11, 15, internalState); -+ G(message[blake2b_sigma[round][8]], message[blake2b_sigma[round][9]], 0, 5, 10, 15, internalState); -+ G(message[blake2b_sigma[round][10]], message[blake2b_sigma[round][11]], 1, 6, 11, 12, internalState); -+ G(message[blake2b_sigma[round][12]], message[blake2b_sigma[round][13]], 2, 7, 8, 13, internalState); -+ G(message[blake2b_sigma[round][14]], message[blake2b_sigma[round][15]], 3, 4, 9, 14, internalState); -+ } -+ -+ for (int i = 0; i < 8; i++) { -+ chainValue[i] ^= internalState[i] ^ internalState[i + 8]; -+ } -+ } -+ -+ private static void G(long m1, long m2, int posA, int posB, int posC, int posD, long[] internalState) { -+ internalState[posA] = internalState[posA] + internalState[posB] + m1; -+ internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 32); -+ internalState[posC] = internalState[posC] + internalState[posD]; -+ internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 24); // replaces 25 of BLAKE -+ internalState[posA] = internalState[posA] + internalState[posB] + m2; -+ internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 16); -+ internalState[posC] = internalState[posC] + internalState[posD]; -+ internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 63); // replaces 11 of BLAKE -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java b/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java -new file mode 100644 -index 0000000000000000000000000000000000000000..f6e69a105c8267c1cf85e77e406ec9fc634a8dd6 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/seed/WorldgenCryptoRandom.java -@@ -0,0 +1,159 @@ -+package space.bxteam.divinemc.seed; -+ -+import net.minecraft.util.Mth; -+import net.minecraft.util.RandomSource; -+import net.minecraft.world.level.levelgen.LegacyRandomSource; -+import net.minecraft.world.level.levelgen.WorldgenRandom; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.Arrays; -+ -+public class WorldgenCryptoRandom extends WorldgenRandom { -+ // hash the world seed to guard against badly chosen world seeds -+ private static final long[] HASHED_ZERO_SEED = Hashing.hashWorldSeed(new long[Globals.WORLD_SEED_LONGS]); -+ private static final ThreadLocal LAST_SEEN_WORLD_SEED = ThreadLocal.withInitial(() -> new long[Globals.WORLD_SEED_LONGS]); -+ private static final ThreadLocal HASHED_WORLD_SEED = ThreadLocal.withInitial(() -> HASHED_ZERO_SEED); -+ -+ private final long[] worldSeed = new long[Globals.WORLD_SEED_LONGS]; -+ private final long[] randomBits = new long[8]; -+ private int randomBitIndex; -+ private static final int MAX_RANDOM_BIT_INDEX = 64 * 8; -+ private static final int LOG2_MAX_RANDOM_BIT_INDEX = 9; -+ private long counter; -+ private final long[] message = new long[16]; -+ private final long[] cachedInternalState = new long[16]; -+ -+ public WorldgenCryptoRandom(int x, int z, Globals.Salt typeSalt, long salt) { -+ super(new LegacyRandomSource(0L)); -+ if (typeSalt != null) { -+ this.setSecureSeed(x, z, typeSalt, salt); -+ } -+ } -+ -+ public void setSecureSeed(int x, int z, Globals.Salt typeSalt, long salt) { -+ System.arraycopy(Globals.worldSeed, 0, this.worldSeed, 0, Globals.WORLD_SEED_LONGS); -+ message[0] = ((long) x << 32) | ((long) z & 0xffffffffL); -+ message[1] = ((long) Globals.dimension.get() << 32) | ((long) salt & 0xffffffffL); -+ message[2] = typeSalt.ordinal(); -+ message[3] = counter = 0; -+ randomBitIndex = MAX_RANDOM_BIT_INDEX; -+ } -+ -+ private long[] getHashedWorldSeed() { -+ if (!Arrays.equals(worldSeed, LAST_SEEN_WORLD_SEED.get())) { -+ HASHED_WORLD_SEED.set(Hashing.hashWorldSeed(worldSeed)); -+ System.arraycopy(worldSeed, 0, LAST_SEEN_WORLD_SEED.get(), 0, Globals.WORLD_SEED_LONGS); -+ } -+ return HASHED_WORLD_SEED.get(); -+ } -+ -+ private void moreRandomBits() { -+ message[3] = counter++; -+ System.arraycopy(getHashedWorldSeed(), 0, randomBits, 0, 8); -+ Hashing.hash(message, randomBits, cachedInternalState, 64, true); -+ } -+ -+ private long getBits(int count) { -+ if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) { -+ moreRandomBits(); -+ randomBitIndex -= MAX_RANDOM_BIT_INDEX; -+ } -+ -+ int alignment = randomBitIndex & 63; -+ if ((randomBitIndex >>> 6) == ((randomBitIndex + count) >>> 6)) { -+ long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << count) - 1); -+ randomBitIndex += count; -+ return result; -+ } else { -+ long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << (64 - alignment)) - 1); -+ randomBitIndex += count; -+ if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) { -+ moreRandomBits(); -+ randomBitIndex -= MAX_RANDOM_BIT_INDEX; -+ } -+ alignment = randomBitIndex & 63; -+ result <<= alignment; -+ result |= (randomBits[randomBitIndex >>> 6] >>> (64 - alignment)) & ((1L << alignment) - 1); -+ -+ return result; -+ } -+ } -+ -+ @Override -+ public @NotNull RandomSource fork() { -+ WorldgenCryptoRandom fork = new WorldgenCryptoRandom(0, 0, null, 0); -+ -+ System.arraycopy(Globals.worldSeed, 0, fork.worldSeed, 0, Globals.WORLD_SEED_LONGS); -+ fork.message[0] = this.message[0]; -+ fork.message[1] = this.message[1]; -+ fork.message[2] = this.message[2]; -+ fork.message[3] = this.message[3]; -+ fork.randomBitIndex = this.randomBitIndex; -+ fork.counter = this.counter; -+ fork.nextLong(); -+ -+ return fork; -+ } -+ -+ @Override -+ public int next(int bits) { -+ return (int) getBits(bits); -+ } -+ -+ @Override -+ public void consumeCount(int count) { -+ randomBitIndex += count; -+ if (randomBitIndex >= MAX_RANDOM_BIT_INDEX * 2) { -+ randomBitIndex -= MAX_RANDOM_BIT_INDEX; -+ counter += randomBitIndex >>> LOG2_MAX_RANDOM_BIT_INDEX; -+ randomBitIndex &= MAX_RANDOM_BIT_INDEX - 1; -+ randomBitIndex += MAX_RANDOM_BIT_INDEX; -+ } -+ } -+ -+ @Override -+ public int nextInt(int bound) { -+ int bits = Mth.ceillog2(bound); -+ int result; -+ do { -+ result = (int) getBits(bits); -+ } while (result >= bound); -+ -+ return result; -+ } -+ -+ @Override -+ public long nextLong() { -+ return getBits(64); -+ } -+ -+ @Override -+ public double nextDouble() { -+ return getBits(53) * 0x1.0p-53; -+ } -+ -+ @Override -+ public long setDecorationSeed(long worldSeed, int blockX, int blockZ) { -+ setSecureSeed(blockX, blockZ, Globals.Salt.POPULATION, 0); -+ return ((long) blockX << 32) | ((long) blockZ & 0xffffffffL); -+ } -+ -+ @Override -+ public void setFeatureSeed(long populationSeed, int index, int step) { -+ setSecureSeed((int) (populationSeed >> 32), (int) populationSeed, Globals.Salt.DECORATION, index + 10000L * step); -+ } -+ -+ @Override -+ public void setLargeFeatureSeed(long worldSeed, int chunkX, int chunkZ) { -+ super.setLargeFeatureSeed(worldSeed, chunkX, chunkZ); -+ } -+ -+ @Override -+ public void setLargeFeatureWithSalt(long worldSeed, int regionX, int regionZ, int salt) { -+ super.setLargeFeatureWithSalt(worldSeed, regionX, regionZ, salt); -+ } -+ -+ public static RandomSource seedSlimeChunk(int chunkX, int chunkZ) { -+ return new WorldgenCryptoRandom(chunkX, chunkZ, Globals.Salt.SLIME_CHUNK, 0); -+ } -+} diff --git a/patches/server/0047-Fix-MC-183518.patch b/patches/server/0047-Fix-MC-183518.patch deleted file mode 100644 index e5bd037..0000000 --- a/patches/server/0047-Fix-MC-183518.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 17 Jun 2024 17:15:13 +0300 -Subject: [PATCH] Fix MC-183518 - -Issue on Mojira: https://bugs.mojang.com/browse/MC-183518 -P.S. Btw, I took this fix from this PR - https://github.com/Winds-Studio/Leaf/pull/69 - -diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -index 9b706276dc5b5f55b966c5472c6c4e864342b916..e9f2b88058bea5841fa648dad32903af19293328 100644 ---- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -+++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java -@@ -22,7 +22,7 @@ import net.minecraft.util.profiling.metrics.ProfilerMeasured; - import org.slf4j.Logger; - - public abstract class BlockableEventLoop implements ProfilerMeasured, TaskScheduler, Executor { -- public static final long BLOCK_TIME_NANOS = 100000L; -+ public static final long BLOCK_TIME_NANOS = 2000000L; // DivineMC - Fix MC-183518 - private final String name; - private static final Logger LOGGER = LogUtils.getLogger(); - private final Queue pendingRunnables = Queues.newConcurrentLinkedQueue(); -@@ -145,8 +145,7 @@ public abstract class BlockableEventLoop implements Profiler - } - - protected void waitForTasks() { -- Thread.yield(); -- LockSupport.parkNanos("waiting for tasks", 100000L); -+ LockSupport.parkNanos("waiting for tasks", 2000000L); // DivineMC - Fix MC-183518 - } - - protected void doRunTask(R task) { diff --git a/patches/server/0048-Delete-Timings.patch b/patches/server/0048-Delete-Timings.patch deleted file mode 100644 index 47d2584..0000000 --- a/patches/server/0048-Delete-Timings.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 18 Nov 2024 17:05:17 +0300 -Subject: [PATCH] Delete Timings - - -diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -index 7ce9ebba8ce304d1f3f21d4f15ee5f3560d7700b..a1c9726d25479b5326fe2fa2b0f5a98d6b2da4c5 100644 ---- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -@@ -1,6 +1,5 @@ - package io.papermc.paper.plugin.manager; - --import co.aikar.timings.TimedEventExecutor; - import com.destroystokyo.paper.event.server.ServerExceptionEvent; - import com.destroystokyo.paper.exception.ServerEventException; - import com.google.common.collect.Sets; -@@ -95,7 +94,6 @@ class PaperEventManager { - throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled"); - } - -- executor = new TimedEventExecutor(executor, plugin, null, event); - this.getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); - } - -@@ -182,7 +180,7 @@ class PaperEventManager { - } - } - -- EventExecutor executor = new TimedEventExecutor(EventExecutor.create(method, eventClass), plugin, method, eventClass); -+ EventExecutor executor = EventExecutor.create(method, eventClass); - eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); - } - return ret; -diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java -index 097500a59336db1bbfffcd1aa4cff7a8586e46ec..35b00c139864dd7925d46a2d6a317d7e3aae9638 100644 ---- a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java -+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java -@@ -232,7 +232,7 @@ public class PaperPluginManagerImpl implements PluginManager, DependencyContext - - @Override - public boolean useTimings() { -- return co.aikar.timings.Timings.isTimingsEnabled(); -+ return false; - } - - @Override -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index f9fcd20d8388fb8a81158f88733007cc45ea7895..3b9e50aa3b47233af7af4857537431900c9b86ee 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -3,9 +3,6 @@ package net.minecraft.server; - import com.google.common.base.Preconditions; - import com.google.common.base.Splitter; - import com.google.common.collect.ImmutableList; --import co.aikar.timings.Timings; --import com.destroystokyo.paper.event.server.PaperServerListPingEvent; --import com.google.common.base.Stopwatch; - import com.google.common.collect.Lists; - import com.google.common.collect.Maps; - import com.google.common.collect.Sets; diff --git a/patches/server/0049-Fix-MC-65198.patch b/patches/server/0049-Fix-MC-65198.patch deleted file mode 100644 index c58a2bb..0000000 --- a/patches/server/0049-Fix-MC-65198.patch +++ /dev/null @@ -1,65 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sun, 15 Dec 2024 23:28:29 +0300 -Subject: [PATCH] Fix MC-65198 - -Original post on Mojira: https://bugs.mojang.com/browse/MC-65198 - -diff --git a/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java b/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java -index ac9df238ef0f3d009f25976b95e0b750e963e952..9b187768d645759f12d1a474fe488663d304a068 100644 ---- a/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/ItemCombinerMenu.java -@@ -129,6 +129,7 @@ public abstract class ItemCombinerMenu extends AbstractContainerMenu { - ItemStack itemstack1 = slot1.getItem(); - - itemstack = itemstack1.copy(); -+ ItemStack itemStack2 = itemstack.copy(); // DivineMC - Fix MC-65198 - int j = this.getInventorySlotStart(); - int k = this.getUseRowEnd(); - -@@ -165,7 +166,7 @@ public abstract class ItemCombinerMenu extends AbstractContainerMenu { - } - - this.activeQuickItem = itemstack; // Purpur - Anvil API -- slot1.onTake(player, itemstack1); -+ slot1.onTake(player, itemStack2); // DivineMC - Fix MC-65198 - this.activeQuickItem = null; // Purpur - Anvil API - } - -diff --git a/src/main/java/net/minecraft/world/inventory/ResultSlot.java b/src/main/java/net/minecraft/world/inventory/ResultSlot.java -index ff30071f3ef37d1b28cf86e26ce4f7477335a07a..a7b2a88cf2c1e8f717e512a9bc01772a463009bb 100644 ---- a/src/main/java/net/minecraft/world/inventory/ResultSlot.java -+++ b/src/main/java/net/minecraft/world/inventory/ResultSlot.java -@@ -49,7 +49,7 @@ public class ResultSlot extends Slot { - @Override - protected void checkTakeAchievements(ItemStack stack) { - if (this.removeCount > 0) { -- stack.onCraftedBy(this.player.level(), this.player, this.removeCount); -+ stack.onCraftedBy(this.player.level(), this.player, stack.getCount()); // DivineMC - Fix MC-65198 - } - - if (this.container instanceof RecipeCraftingHolder recipeCraftingHolder) { -diff --git a/src/main/java/net/minecraft/world/inventory/StonecutterMenu.java b/src/main/java/net/minecraft/world/inventory/StonecutterMenu.java -index 5dce62aead43c7110e06196423458eea2ba31885..120094e07b73e81dbaa5bb1cf727feb3842f7380 100644 ---- a/src/main/java/net/minecraft/world/inventory/StonecutterMenu.java -+++ b/src/main/java/net/minecraft/world/inventory/StonecutterMenu.java -@@ -259,6 +259,7 @@ public class StonecutterMenu extends AbstractContainerMenu { - Item item = itemstack1.getItem(); - - itemstack = itemstack1.copy(); -+ ItemStack itemStack2 = itemstack.copy(); // DivineMC - Fix MC-65198 - if (slot == 1) { - item.onCraftedBy(itemstack1, player.level(), player); - if (!this.moveItemStackTo(itemstack1, 2, 38, true)) { -@@ -291,9 +292,9 @@ public class StonecutterMenu extends AbstractContainerMenu { - return ItemStack.EMPTY; - } - -- slot1.onTake(player, itemstack1); -+ slot1.onTake(player, itemStack2); // DivineMC - Fix MC-65198 - if (slot == 1) { -- player.drop(itemstack1, false); -+ player.drop(itemStack2, false); // DivineMC - Fix MC-65198 - } - - this.broadcastChanges(); diff --git a/patches/server/0051-C2ME-Optimize-world-gen-math.patch b/patches/server/0051-C2ME-Optimize-world-gen-math.patch deleted file mode 100644 index c73f147..0000000 --- a/patches/server/0051-C2ME-Optimize-world-gen-math.patch +++ /dev/null @@ -1,78 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 16 Dec 2024 00:13:15 +0300 -Subject: [PATCH] C2ME: Optimize world gen math - - -diff --git a/src/main/java/net/minecraft/world/level/ChunkPos.java b/src/main/java/net/minecraft/world/level/ChunkPos.java -index 0639e4565c3324d757dec1226adb4e99d841f2c0..7b659fc5cc373e5d26968c693e97b5f725a2e600 100644 ---- a/src/main/java/net/minecraft/world/level/ChunkPos.java -+++ b/src/main/java/net/minecraft/world/level/ChunkPos.java -@@ -110,7 +110,12 @@ public class ChunkPos { - - @Override - public boolean equals(Object object) { -- return this == object || object instanceof ChunkPos chunkPos && this.x == chunkPos.x && this.z == chunkPos.z; -+ // DivineMC start - Use standard equals -+ if (object == this) return true; -+ if (object == null || object.getClass() != this.getClass()) return false; -+ ChunkPos thatPos = (ChunkPos) object; -+ return this.x == thatPos.x && this.z == thatPos.z; -+ // DivineMC end - } - - public int getMiddleBlockX() { -diff --git a/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java b/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java -index ca93a97256350789ca56f910862c9d717ca7670b..3597fa53c6c58540cb37a9bf27c71e18ebf47660 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/Beardifier.java -@@ -132,8 +132,14 @@ public class Beardifier implements DensityFunctions.BeardifierOrMarker { - } - - private static double getBuryContribution(double x, double y, double z) { -- double d = Mth.length(x, y, z); -- return Mth.clampedMap(d, 0.0, 6.0, 1.0, 0.0); -+ // DivineMC start - Optimize method for beardifier -+ double d = Math.sqrt(x * x + y * y + z * z); -+ if (d > 6.0) { -+ return 0.0; -+ } else { -+ return 1.0 - d / 6.0; -+ } -+ // DivineMC end - } - - private static double getBeardContribution(int x, int y, int z, int yy) { -diff --git a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -index 3f39d6c786d9dfdd9ad591e08ff05fcbb41a1df6..3874da1d4d78be91b4a4d67ef7eb3374d944b373 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java -@@ -74,11 +74,10 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator { - Aquifer.FluidStatus aquifer_b = new Aquifer.FluidStatus(-54, Blocks.LAVA.defaultBlockState()); - int i = settings.seaLevel(); - Aquifer.FluidStatus aquifer_b1 = new Aquifer.FluidStatus(i, settings.defaultFluid()); -- Aquifer.FluidStatus aquifer_b2 = new Aquifer.FluidStatus(DimensionType.MIN_Y * 2, Blocks.AIR.defaultBlockState()); -- -- return (j, k, l) -> { -- return k < Math.min(-54, i) ? aquifer_b : aquifer_b1; -- }; -+ // DivineMC start - Optimize world gen -+ final int min = Math.min(-54, i); -+ return (j, k, l) -> k < min ? aquifer_b : aquifer_b1; -+ // DivineMC end - } - - @Override -diff --git a/src/main/java/net/minecraft/world/level/levelgen/synth/PerlinNoise.java b/src/main/java/net/minecraft/world/level/levelgen/synth/PerlinNoise.java -index 74a666a45289f0902b426ba57986cd93b41cb42c..c050f59051209dd14a1b8b175a3809769d47bd01 100644 ---- a/src/main/java/net/minecraft/world/level/levelgen/synth/PerlinNoise.java -+++ b/src/main/java/net/minecraft/world/level/levelgen/synth/PerlinNoise.java -@@ -218,7 +218,7 @@ public class PerlinNoise { - } - - public static double wrap(double value) { -- return value - (double)Mth.lfloor(value / 3.3554432E7 + 0.5) * 3.3554432E7; -+ return value - Math.floor(value / 3.3554432E7 + 0.5) * 3.3554432E7; // DivineMC - Avoid casting - } - - protected int firstOctave() { diff --git a/patches/server/0052-Petal-Async-Pathfinding.patch b/patches/server/0052-Petal-Async-Pathfinding.patch deleted file mode 100644 index 442b66e..0000000 --- a/patches/server/0052-Petal-Async-Pathfinding.patch +++ /dev/null @@ -1,1338 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Sun, 14 Jan 2024 00:46:20 +0300 -Subject: [PATCH] Petal: Async Pathfinding - -Original code by Bloom-host, licensed under GPL v3 -You can find the original code on https://github.com/Bloom-host/Petal - -Makes most pathfinding-related work happen asynchronously - -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 273ba657926ce72a7c82861e880a82bf7f322a0b..8fe72d0cfbc6428671081417a6c7d93276b2a3b1 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 -@@ -94,23 +94,56 @@ public class AcquirePoi { - } - } - // Paper end - optimise POI access -- Path path = findPathToPois(entity, set); -- if (path != null && path.canReach()) { -- BlockPos blockPos = path.getTarget(); -- poiManager.getType(blockPos).ifPresent(poiType -> { -- poiManager.take(poiPredicate, (holder, blockPos2) -> blockPos2.equals(blockPos), blockPos, 1); -- queryResult.set(GlobalPos.of(world.dimension(), blockPos)); -- entityStatus.ifPresent(status -> world.broadcastEntityEvent(entity, status)); -- long2ObjectMap.clear(); -- DebugPackets.sendPoiTicketCountPacket(world, blockPos); -+ // DivineMC start - Async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ // await on path async -+ Path possiblePath = findPathToPois(entity, set); -+ -+ // wait on the path to be processed -+ space.bxteam.divinemc.pathfinding.AsyncPathProcessor.awaitProcessing(possiblePath, path -> { -+ // read canReach check -+ if (path == null || !path.canReach()) { -+ for (Pair, BlockPos> pair : set) { -+ long2ObjectMap.computeIfAbsent( -+ pair.getSecond().asLong(), -+ m -> new JitteredLinearRetry(entity.level().random, time) -+ ); -+ } -+ return; -+ } -+ BlockPos blockPos = path.getTarget(); -+ poiManager.getType(blockPos).ifPresent(poiType -> { -+ poiManager.take(poiPredicate, -+ (holder, blockPos2) -> blockPos2.equals(blockPos), -+ blockPos, -+ 1 -+ ); -+ queryResult.set(GlobalPos.of(world.dimension(), blockPos)); -+ entityStatus.ifPresent(status -> world.broadcastEntityEvent(entity, status)); -+ long2ObjectMap.clear(); -+ DebugPackets.sendPoiTicketCountPacket(world, blockPos); -+ }); - }); - } else { -- for (Pair, BlockPos> pair : set) { -- long2ObjectMap.computeIfAbsent( -- pair.getSecond().asLong(), m -> new AcquirePoi.JitteredLinearRetry(world.random, time) -- ); -+ Path path = findPathToPois(entity, set); -+ if (path != null && path.canReach()) { -+ BlockPos blockPos = path.getTarget(); -+ poiManager.getType(blockPos).ifPresent(poiType -> { -+ poiManager.take(poiPredicate, (holder, blockPos2) -> blockPos2.equals(blockPos), blockPos, 1); -+ queryResult.set(GlobalPos.of(world.dimension(), blockPos)); -+ entityStatus.ifPresent(status -> world.broadcastEntityEvent(entity, status)); -+ long2ObjectMap.clear(); -+ DebugPackets.sendPoiTicketCountPacket(world, blockPos); -+ }); -+ } else { -+ for (Pair, BlockPos> pair : set) { -+ long2ObjectMap.computeIfAbsent( -+ pair.getSecond().asLong(), m -> new AcquirePoi.JitteredLinearRetry(world.random, time) -+ ); -+ } - } - } -+ // DivineMC end - - return true; - } -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 2a7a26ca447cc78f24e61a2bf557411c31eb16b2..58f55ce47e277e195058a5c1d07799e4a7850d7c 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 -@@ -21,6 +21,7 @@ public class MoveToTargetSink extends Behavior { - private int remainingCooldown; - @Nullable - private Path path; -+ private boolean finishedProcessing; // DivineMC - @Nullable - private BlockPos lastTargetPos; - private float speedModifier; -@@ -53,9 +54,11 @@ public class MoveToTargetSink extends Behavior { - Brain brain = entity.getBrain(); - WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); - boolean bl = this.reachedTarget(entity, walkTarget); -- if (!bl && this.tryComputePath(entity, walkTarget, world.getGameTime())) { -+ if (!space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding && !bl && this.tryComputePath(entity, walkTarget, world.getGameTime())) { // DivineMC - async pathfinding - this.lastTargetPos = walkTarget.getTarget().currentBlockPosition(); - return true; -+ } else if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding && !bl) { // DivineMC - async pathfinding -+ return true; - } else { - brain.eraseMemory(MemoryModuleType.WALK_TARGET); - if (bl) { -@@ -69,6 +72,7 @@ public class MoveToTargetSink extends Behavior { - - @Override - protected boolean canStillUse(ServerLevel world, Mob entity, long time) { -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding && !this.finishedProcessing) return true; // DivineMC - wait for processing - if (this.path != null && this.lastTargetPos != null) { - Optional optional = entity.getBrain().getMemory(MemoryModuleType.WALK_TARGET); - boolean bl = optional.map(MoveToTargetSink::isWalkTargetSpectator).orElse(false); -@@ -95,28 +99,98 @@ public class MoveToTargetSink extends Behavior { - - @Override - protected void start(ServerLevel serverLevel, Mob mob, long l) { -+ // DivineMC start - start processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ Brain brain = mob.getBrain(); -+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); -+ -+ this.finishedProcessing = false; -+ this.lastTargetPos = walkTarget.getTarget().currentBlockPosition(); -+ this.path = this.computePath(mob, walkTarget); -+ return; -+ } -+ // DivineMC end - mob.getBrain().setMemory(MemoryModuleType.PATH, this.path); - mob.getNavigation().moveTo(this.path, (double)this.speedModifier); - } - - @Override - protected void tick(ServerLevel serverLevel, Mob mob, long l) { -- Path path = mob.getNavigation().getPath(); -- Brain brain = mob.getBrain(); -- if (this.path != path) { -- this.path = path; -- brain.setMemory(MemoryModuleType.PATH, path); -- } -+ // DivineMC start - Async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ if (this.path != null && !this.path.isProcessed()) return; // wait for processing - -- if (path != null && this.lastTargetPos != null) { -- WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); -- if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0 -- && this.tryComputePath(mob, walkTarget, serverLevel.getGameTime())) { -- this.lastTargetPos = walkTarget.getTarget().currentBlockPosition(); -- this.start(serverLevel, mob, l); -+ if (!this.finishedProcessing) { -+ this.finishedProcessing = true; -+ -+ Brain brain = mob.getBrain(); -+ boolean canReach = this.path != null && this.path.canReach(); -+ if (canReach) { -+ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE); -+ } else if (!brain.hasMemoryValue(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE)) { -+ brain.setMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, l); -+ } -+ -+ if (!canReach) { -+ Optional walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET); -+ -+ if (!walkTarget.isPresent()) return; -+ -+ BlockPos blockPos = walkTarget.get().getTarget().currentBlockPosition(); -+ Vec3 vec3 = DefaultRandomPos.getPosTowards((PathfinderMob) mob, 10, 7, Vec3.atBottomCenterOf(blockPos), (float) Math.PI / 2F); -+ if (vec3 != null) { -+ // try recalculating the path using a random position -+ this.path = mob.getNavigation().createPath(vec3.x, vec3.y, vec3.z, 0); -+ this.finishedProcessing = false; -+ return; -+ } -+ } -+ -+ mob.getBrain().setMemory(MemoryModuleType.PATH, this.path); -+ mob.getNavigation().moveTo(this.path, this.speedModifier); -+ } -+ -+ Path path = mob.getNavigation().getPath(); -+ Brain brain = mob.getBrain(); -+ -+ if (path != null && this.lastTargetPos != null && brain.hasMemoryValue(MemoryModuleType.WALK_TARGET)) { -+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); // we know isPresent = true -+ if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0D) { -+ this.start(serverLevel, mob, l); -+ } -+ } -+ } else { -+ Path path = mob.getNavigation().getPath(); -+ Brain brain = mob.getBrain(); -+ if (this.path != path) { -+ this.path = path; -+ brain.setMemory(MemoryModuleType.PATH, path); - } -+ -+ if (path != null && this.lastTargetPos != null) { -+ WalkTarget walkTarget = brain.getMemory(MemoryModuleType.WALK_TARGET).get(); -+ if (walkTarget.getTarget().currentBlockPosition().distSqr(this.lastTargetPos) > 4.0 -+ && this.tryComputePath(mob, walkTarget, serverLevel.getGameTime())) { -+ this.lastTargetPos = walkTarget.getTarget().currentBlockPosition(); -+ this.start(serverLevel, mob, l); -+ } -+ } -+ } -+ // DivineMC end -+ } -+ -+ // DivineMC start - Async path processing -+ @Nullable -+ private Path computePath(Mob entity, WalkTarget walkTarget){ -+ BlockPos blockPos = walkTarget.getTarget().currentBlockPosition(); -+ this.speedModifier = walkTarget.getSpeedModifier(); -+ Brain brain = entity.getBrain(); -+ if (this.reachedTarget(entity, walkTarget)) { -+ brain.eraseMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE); - } -+ return entity.getNavigation().createPath(blockPos, 0); - } -+ // DivineMC end - - private boolean tryComputePath(Mob entity, WalkTarget walkTarget, long time) { - BlockPos blockPos = walkTarget.getTarget().currentBlockPosition(); -diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java b/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java -index 6802e0c4d331c7125114dd86409f6a110465ab82..59e4360006499ca9a90f1b12747024ee7b8ba3ad 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java -+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/SetClosestHomeAsWalkTarget.java -@@ -60,17 +60,39 @@ public class SetClosestHomeAsWalkTarget { - poiType -> poiType.is(PoiTypes.HOME), predicate, entity.blockPosition(), 48, PoiManager.Occupancy.ANY - ) - .collect(Collectors.toSet()); -- Path path = AcquirePoi.findPathToPois(entity, set); -- if (path != null && path.canReach()) { -- BlockPos blockPos = path.getTarget(); -- Optional> optional2 = poiManager.getType(blockPos); -- if (optional2.isPresent()) { -- walkTarget.set(new WalkTarget(blockPos, speed, 1)); -- DebugPackets.sendPoiTicketCountPacket(world, blockPos); -+ // DivineMC start - Async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ // await on path async -+ Path possiblePath = AcquirePoi.findPathToPois(entity, set); -+ -+ // wait on the path to be processed -+ space.bxteam.divinemc.pathfinding.AsyncPathProcessor.awaitProcessing(possiblePath, path -> { -+ if (path == null || !path.canReach() || mutableInt.getValue() < 5) { // read canReach check -+ long2LongMap.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue()); -+ return; -+ } -+ BlockPos blockPos = path.getTarget(); -+ Optional> optional2 = poiManager.getType(blockPos); -+ if (optional2.isPresent()) { -+ walkTarget.set(new WalkTarget(blockPos, speed, 1)); -+ DebugPackets.sendPoiTicketCountPacket(world, blockPos); -+ } -+ }); -+ } else { -+ // Kaiiju end -+ Path path = AcquirePoi.findPathToPois(entity, set); -+ if (path != null && path.canReach()) { -+ BlockPos blockPos = path.getTarget(); -+ Optional> optional2 = poiManager.getType(blockPos); -+ if (optional2.isPresent()) { -+ walkTarget.set(new WalkTarget(blockPos, speed, 1)); -+ DebugPackets.sendPoiTicketCountPacket(world, blockPos); -+ } -+ } else if (mutableInt.getValue() < 5) { -+ long2LongMap.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue()); - } -- } else if (mutableInt.getValue() < 5) { -- long2LongMap.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < mutableLong.getValue()); - } -+ // DivineMC end - - return true; - } else { -diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java -index 2846790fcd00788cf0284c348161ee1aee415f13..ce229db4fbd364a49d398fcafa9c45a5dff3f211 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java -+++ b/src/main/java/net/minecraft/world/entity/ai/goal/DoorInteractGoal.java -@@ -56,7 +56,7 @@ public abstract class DoorInteractGoal extends Goal { - } else { - GroundPathNavigation groundPathNavigation = (GroundPathNavigation)this.mob.getNavigation(); - Path path = groundPathNavigation.getPath(); -- if (path != null && !path.isDone()) { -+ if (path != null && path.isProcessed() && !path.isDone()) { // DivineMC - ensure path is processed - for (int i = 0; i < Math.min(path.getNextNodeIndex() + 2, path.getNodeCount()); i++) { - Node node = path.getNode(i); - this.doorPos = new BlockPos(node.x, node.y + 1, node.z); -diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java -index 29b852c3262c9cd0d2c77a93c01a386a2c184742..2e97ecce7684b2a701d8b89981426326a1f5bb2a 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java -+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/AmphibiousPathNavigation.java -@@ -12,9 +12,25 @@ public class AmphibiousPathNavigation extends PathNavigation { - super(mob, world); - } - -+ // DivineMC start - async path processing -+ private static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { -+ AmphibiousNodeEvaluator nodeEvaluator = new AmphibiousNodeEvaluator(false); -+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); -+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); -+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); -+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); -+ return nodeEvaluator; -+ }; -+ // DivineMC end -+ - @Override - protected PathFinder createPathFinder(int range) { - this.nodeEvaluator = new AmphibiousNodeEvaluator(false); -+ // DivineMC start - async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ return new PathFinder(this.nodeEvaluator, range, nodeEvaluatorGenerator); -+ } -+ // DivineMC end - return new PathFinder(this.nodeEvaluator, range); - } - -diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java -index 2bd66da93227d4e4fc2ec4df47ae94b17f4d39d3..7b47d4f91eaf38cb132681bbfd0a17a9119ed930 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java -+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/FlyingPathNavigation.java -@@ -16,9 +16,25 @@ public class FlyingPathNavigation extends PathNavigation { - super(entity, world); - } - -+ // DivineMC start - async path processing -+ private static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { -+ FlyNodeEvaluator nodeEvaluator = new FlyNodeEvaluator(); -+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); -+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); -+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); -+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); -+ return nodeEvaluator; -+ }; -+ // DivineMC end -+ - @Override - protected PathFinder createPathFinder(int range) { - this.nodeEvaluator = new FlyNodeEvaluator(); -+ // DivineMC start - async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ return new PathFinder(this.nodeEvaluator, range, nodeEvaluatorGenerator); -+ } -+ // DivineMC end - return new PathFinder(this.nodeEvaluator, range); - } - -@@ -48,6 +64,7 @@ public class FlyingPathNavigation extends PathNavigation { - if (this.hasDelayedRecomputation) { - this.recomputePath(); - } -+ if (this.path != null && !this.path.isProcessed()) return; // DivineMC - async path processing - - if (!this.isDone()) { - if (this.canUpdatePath()) { -diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java -index 2796df7af365c452b28373adfd7daf1d6730bac5..ec14600b3de361d2265f86092e69eca61d732c44 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java -+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/GroundPathNavigation.java -@@ -24,9 +24,25 @@ public class GroundPathNavigation extends PathNavigation { - super(entity, world); - } - -+ // DivineMC start - async path processing -+ protected static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { -+ WalkNodeEvaluator nodeEvaluator = new WalkNodeEvaluator(); -+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); -+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); -+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); -+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); -+ return nodeEvaluator; -+ }; -+ // DivineMC end -+ - @Override - protected PathFinder createPathFinder(int range) { - this.nodeEvaluator = new WalkNodeEvaluator(); -+ // DivineMC start - async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ return new PathFinder(this.nodeEvaluator, range, nodeEvaluatorGenerator); -+ } -+ // DivineMC end - return new PathFinder(this.nodeEvaluator, range); - } - -diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java -index 48c0de870a5bbf647309e69361dfb10ab56c65ab..fc9605882fe5011606d05f6a0d77728f134a5733 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java -+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java -@@ -168,6 +168,10 @@ public abstract class PathNavigation { - return null; - } else if (!this.canUpdatePath()) { - return null; -+ // DivineMC start - catch early if it's still processing these positions let it keep processing -+ } else if (this.path instanceof space.bxteam.divinemc.pathfinding.AsyncPath asyncPath && !asyncPath.isProcessed() && asyncPath.hasSameProcessingPositions(positions)) { -+ return this.path; -+ // DivineMC end - } else if (this.path != null && !this.path.isDone() && positions.contains(this.targetPos)) { - return this.path; - } else { -@@ -194,12 +198,30 @@ public abstract class PathNavigation { - int i = (int)(followRange + (float)range); - PathNavigationRegion pathNavigationRegion = new PathNavigationRegion(this.level, blockPos.offset(-i, -i, -i), blockPos.offset(i, i, i)); - Path path = this.pathFinder.findPath(pathNavigationRegion, this.mob, positions, followRange, distance, this.maxVisitedNodesMultiplier); -- profilerFiller.pop(); -- if (path != null && path.getTarget() != null) { -- this.targetPos = path.getTarget(); -- this.reachRange = distance; -- this.resetStuckTimeout(); -+ // DivineMC start - async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ // assign early a target position. most calls will only have 1 position -+ if (!positions.isEmpty()) this.targetPos = positions.iterator().next(); -+ -+ space.bxteam.divinemc.pathfinding.AsyncPathProcessor.awaitProcessing(path, processedPath -> { -+ // check that processing didn't take so long that we calculated a new path -+ if (processedPath != this.path) return; -+ -+ if (processedPath != null && processedPath.getTarget() != null) { -+ this.targetPos = processedPath.getTarget(); -+ this.reachRange = distance; -+ this.resetStuckTimeout(); -+ } -+ }); -+ } else { -+ profilerFiller.pop(); -+ if (path != null && path.getTarget() != null) { -+ this.targetPos = path.getTarget(); -+ this.reachRange = distance; -+ this.resetStuckTimeout(); -+ } - } -+ // DivineMC end - - return path; - } -@@ -250,8 +272,8 @@ public abstract class PathNavigation { - if (this.isDone()) { - return false; - } else { -- this.trimPath(); -- if (this.path.getNodeCount() <= 0) { -+ if (path.isProcessed()) this.trimPath(); // DivineMC - only trim if processed -+ if (path.isProcessed() && this.path.getNodeCount() <= 0) { // DivineMC - only check node count if processed - return false; - } else { - this.speedModifier = speed; -@@ -274,6 +296,7 @@ public abstract class PathNavigation { - if (this.hasDelayedRecomputation) { - this.recomputePath(); - } -+ if (this.path != null && !this.path.isProcessed()) return; // DivineMC - skip pathfinding if we're still processing - - if (!this.isDone()) { - if (this.canUpdatePath()) { -@@ -300,6 +323,7 @@ public abstract class PathNavigation { - } - - protected void followThePath() { -+ if (!this.path.isProcessed()) return; // DivineMC - skip if not processed - Vec3 vec3 = this.getTempMobPos(); - this.maxDistanceToWaypoint = this.mob.getBbWidth() > 0.75F ? this.mob.getBbWidth() / 2.0F : 0.75F - this.mob.getBbWidth() / 2.0F; - Vec3i vec3i = this.path.getNextNodePos(); -@@ -456,7 +480,7 @@ public abstract class PathNavigation { - public boolean shouldRecomputePath(BlockPos pos) { - if (this.hasDelayedRecomputation) { - return false; -- } else if (this.path != null && !this.path.isDone() && this.path.getNodeCount() != 0) { -+ } else if (this.path != null && this.path.isProcessed() && !this.path.isDone() && this.path.getNodeCount() != 0) { // DivineMC - Skip if not processed - Node node = this.path.getEndNode(); - Vec3 vec3 = new Vec3(((double)node.x + this.mob.getX()) / 2.0, ((double)node.y + this.mob.getY()) / 2.0, ((double)node.z + this.mob.getZ()) / 2.0); - return pos.closerToCenterThan(vec3, (double)(this.path.getNodeCount() - this.path.getNextNodeIndex())); -diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java -index 943c9944ae17fa7cd72e437cce61beaf3fc9505e..606d00d251126d56d57d2d4f1383dea5087a1147 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java -+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/WaterBoundPathNavigation.java -@@ -15,11 +15,27 @@ public class WaterBoundPathNavigation extends PathNavigation { - super(entity, world); - } - -+ // DivineMC start - async path processing -+ private static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { -+ SwimNodeEvaluator nodeEvaluator = new SwimNodeEvaluator(nodeEvaluatorFeatures.allowBreaching()); -+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); -+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); -+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); -+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); -+ return nodeEvaluator; -+ }; -+ // DivineMC end -+ - @Override - protected PathFinder createPathFinder(int range) { - this.allowBreaching = this.mob.getType() == EntityType.DOLPHIN; - this.nodeEvaluator = new SwimNodeEvaluator(this.allowBreaching); - this.nodeEvaluator.setCanPassDoors(false); -+ // DivineMC start - async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ return new PathFinder(this.nodeEvaluator, range, nodeEvaluatorGenerator); -+ } -+ // DivineMC end - return new PathFinder(this.nodeEvaluator, range); - } - -diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -index 9104d7010bda6f9f73b478c11490ef9c53f76da2..e609d1b11d8c8924e4838e3cf5e9b6ac14807e8d 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/NearestBedSensor.java -@@ -57,17 +57,37 @@ public class NearestBedSensor extends Sensor { - java.util.List, BlockPos>> poiposes = new java.util.ArrayList<>(); - // don't ask me why it's unbounded. ask mojang. - io.papermc.paper.util.PoiAccess.findAnyPoiPositions(poiManager, type -> type.is(PoiTypes.HOME), predicate, entity.blockPosition(), world.purpurConfig.villagerNearestBedSensorSearchRadius, PoiManager.Occupancy.ANY, false, Integer.MAX_VALUE, poiposes); // Purpur -- Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes)); -- // Paper end - optimise POI access -- if (path != null && path.canReach()) { -- BlockPos blockPos = path.getTarget(); -- Optional> optional = poiManager.getType(blockPos); -- if (optional.isPresent()) { -- entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, blockPos); -+ // DivineMC start - await on async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ Path possiblePath = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes)); -+ space.bxteam.divinemc.pathfinding.AsyncPathProcessor.awaitProcessing(possiblePath, path -> { -+ // read canReach check -+ if ((path == null || !path.canReach()) && this.triedCount < 5) { -+ this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate); -+ return; -+ } -+ if (path == null) return; -+ -+ BlockPos blockPos = path.getTarget(); -+ Optional> optional = poiManager.getType(blockPos); -+ if (optional.isPresent()) { -+ entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, blockPos); -+ } -+ }); -+ } else { -+ Path path = AcquirePoi.findPathToPois(entity, new java.util.HashSet<>(poiposes)); -+ // Paper end - optimise POI access -+ if (path != null && path.canReach()) { -+ BlockPos blockPos = path.getTarget(); -+ Optional> optional = poiManager.getType(blockPos); -+ if (optional.isPresent()) { -+ entity.getBrain().setMemory(MemoryModuleType.NEAREST_BED, blockPos); -+ } -+ } else if (this.triedCount < 5) { -+ this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate); - } -- } else if (this.triedCount < 5) { -- this.batchCache.long2LongEntrySet().removeIf(entry -> entry.getLongValue() < this.lastUpdate); - } -+ // DivineMC end - } - } - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java -index 98ae165bba22390b098f8a96d1e513f72ef71224..3dd6162a9cfccd24f1312bc22dcf0e0766e0e2d5 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/Bee.java -+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java -@@ -1243,7 +1243,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } else { - Bee.this.pathfindRandomlyTowards(Bee.this.hivePos); - } -- } else { -+ } else if (navigation.getPath() != null && navigation.getPath().isProcessed()) { // DivineMC - check processing - boolean flag = this.pathfindDirectlyTowards(Bee.this.hivePos); - - if (!flag) { -@@ -1302,7 +1302,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal { - } else { - Path pathentity = Bee.this.navigation.getPath(); - -- return pathentity != null && pathentity.getTarget().equals(pos) && pathentity.canReach() && pathentity.isDone(); -+ return pathentity != null && pathentity.isProcessed() && pathentity.getTarget().equals(pos) && pathentity.canReach() && pathentity.isDone(); // DivineMC - ensure path is processed - } - } - } -diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -index 2ce47ff58e5db6fc5387667bc8b02fdef73f9f7c..127602a7a7360cdb8625dee9b3fc2e444ae84474 100644 ---- a/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -+++ b/src/main/java/net/minecraft/world/entity/animal/frog/Frog.java -@@ -501,6 +501,17 @@ public class Frog extends Animal implements VariantHolder> { - super(frog, world); - } - -+ // DivineMC start - async path processing -+ private static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { -+ Frog.FrogNodeEvaluator nodeEvaluator = new Frog.FrogNodeEvaluator(true); -+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); -+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); -+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); -+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); -+ return nodeEvaluator; -+ }; -+ // DivineMC end -+ - @Override - public boolean canCutCorner(PathType nodeType) { - return nodeType != PathType.WATER_BORDER && super.canCutCorner(nodeType); -@@ -509,6 +520,11 @@ public class Frog extends Animal implements VariantHolder> { - @Override - protected PathFinder createPathFinder(int range) { - this.nodeEvaluator = new Frog.FrogNodeEvaluator(true); -+ // DivineMC start - async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ return new PathFinder(this.nodeEvaluator, range, nodeEvaluatorGenerator); -+ } -+ // DivineMC end - return new PathFinder(this.nodeEvaluator, range); - } - } -diff --git a/src/main/java/net/minecraft/world/entity/monster/Drowned.java b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -index 76db5a2d27ab435fdfd1e0ec0c77ef5012e128d4..758cdacf50af7dd6378b2c004ebb3d4be41b9897 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Drowned.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Drowned.java -@@ -309,7 +309,7 @@ public class Drowned extends Zombie implements RangedAttackMob { - - protected boolean closeToNextPos() { - Path path = this.getNavigation().getPath(); -- if (path != null) { -+ if (path != null && path.isProcessed()) { // DivineMC - ensure path is processed - BlockPos blockPos = path.getTarget(); - if (blockPos != null) { - double d = this.distanceToSqr((double)blockPos.getX(), (double)blockPos.getY(), (double)blockPos.getZ()); -diff --git a/src/main/java/net/minecraft/world/entity/monster/Strider.java b/src/main/java/net/minecraft/world/entity/monster/Strider.java -index df44d9fbb71ff252cd261fc8da6de14383e054de..af2631624cff2270aee24f5e58d7613bd352a1c9 100644 ---- a/src/main/java/net/minecraft/world/entity/monster/Strider.java -+++ b/src/main/java/net/minecraft/world/entity/monster/Strider.java -@@ -611,9 +611,25 @@ public class Strider extends Animal implements ItemSteerable, Saddleable { - super(entity, world); - } - -+ // DivineMC start - async path processing -+ private static final space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator = (space.bxteam.divinemc.pathfinding.NodeEvaluatorFeatures nodeEvaluatorFeatures) -> { -+ WalkNodeEvaluator nodeEvaluator = new WalkNodeEvaluator(); -+ nodeEvaluator.setCanPassDoors(nodeEvaluatorFeatures.canPassDoors()); -+ nodeEvaluator.setCanFloat(nodeEvaluatorFeatures.canFloat()); -+ nodeEvaluator.setCanWalkOverFences(nodeEvaluatorFeatures.canWalkOverFences()); -+ nodeEvaluator.setCanOpenDoors(nodeEvaluatorFeatures.canOpenDoors()); -+ return nodeEvaluator; -+ }; -+ // DivineMC end -+ - @Override - protected PathFinder createPathFinder(int range) { - this.nodeEvaluator = new WalkNodeEvaluator(); -+ // DivineMC start - async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ return new PathFinder(this.nodeEvaluator, range, nodeEvaluatorGenerator); -+ } -+ // DivineMC end - return new PathFinder(this.nodeEvaluator, range); - } - -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 6d8c7639c6a264f40b2c402684c54cb694c05c63..c194d66a0118e5196602be8b1fc26839dd514d35 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 -@@ -635,6 +635,16 @@ public class Warden extends Monster implements VibrationSystem { - @Override - protected PathFinder createPathFinder(int range) { - this.nodeEvaluator = new WalkNodeEvaluator(); -+ // DivineMC start - async path processing -+ if (space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) { -+ return new PathFinder(this.nodeEvaluator, range, GroundPathNavigation.nodeEvaluatorGenerator) { -+ @Override -+ protected float distance(Node a, Node b) { -+ return a.distanceToXZ(b); -+ } -+ }; -+ } -+ // DivineMC end - return new PathFinder(this.nodeEvaluator, range) { // CraftBukkit - decompile error - @Override - protected float distance(Node a, Node b) { -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/Path.java b/src/main/java/net/minecraft/world/level/pathfinder/Path.java -index d9d0fff9962131808d54cca20f209df50b8e4af1..4f4ab4068dd72aad9b06f6bfe50045b12a48c139 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/Path.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/Path.java -@@ -27,6 +27,17 @@ public class Path { - this.reached = reachesTarget; - } - -+ // DivineMC start - async path processing -+ /** -+ * checks if the path is completely processed in the case of it being computed async -+ * -+ * @return true if the path is processed -+ */ -+ public boolean isProcessed() { -+ return true; -+ } -+ // DivineMC end -+ - public void advance() { - this.nextNodeIndex++; - } -@@ -100,6 +111,7 @@ public class Path { - } - - public boolean sameAs(@Nullable Path o) { -+ if (o == this) return true; // DivineMC - short circuit - if (o == null) { - return false; - } else if (o.nodes.size() != this.nodes.size()) { -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java -index cc7d94144e39f7dace7b569b4567def98396e8f9..6a4d85e61efeb47d4b277e44d761dfc6329f4855 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/PathFinder.java -@@ -25,11 +25,19 @@ public class PathFinder { - public final NodeEvaluator nodeEvaluator; - private static final boolean DEBUG = false; - private final BinaryHeap openSet = new BinaryHeap(); -+ private final @Nullable space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator; // DivineMC - petal - we use this later to generate an evaluator - -- public PathFinder(NodeEvaluator pathNodeMaker, int range) { -+ public PathFinder(NodeEvaluator pathNodeMaker, int range, @Nullable space.bxteam.divinemc.pathfinding.NodeEvaluatorGenerator nodeEvaluatorGenerator) { // DivineMC - add nodeEvaluatorGenerator - this.nodeEvaluator = pathNodeMaker; - this.maxVisitedNodes = range; -+ // DivineMC start - support nodeEvaluatorgenerators -+ this.nodeEvaluatorGenerator = nodeEvaluatorGenerator; -+ } -+ -+ public PathFinder(NodeEvaluator pathNodeMaker, int range) { -+ this(pathNodeMaker, range, null); - } -+ // DivineMC end - - public void setMaxVisitedNodes(int range) { - this.maxVisitedNodes = range; -@@ -37,27 +45,64 @@ public class PathFinder { - - @Nullable - public Path findPath(PathNavigationRegion world, Mob mob, Set positions, float followRange, int distance, float rangeMultiplier) { -- this.openSet.clear(); -- this.nodeEvaluator.prepare(world, mob); -- Node node = this.nodeEvaluator.getStart(); -+ if (!space.bxteam.divinemc.configuration.DivineConfig.asyncPathfinding) -+ this.openSet.clear(); // DivineMC - it's always cleared in processPath -+ // DivineMC start - use a generated evaluator if we have one otherwise run sync -+ NodeEvaluator nodeEvaluator = this.nodeEvaluatorGenerator == null -+ ? this.nodeEvaluator -+ : space.bxteam.divinemc.pathfinding.NodeEvaluatorCache.takeNodeEvaluator(this.nodeEvaluatorGenerator, this.nodeEvaluator); -+ nodeEvaluator.prepare(world, mob); -+ Node node = nodeEvaluator.getStart(); -+ // DivineMC end - if (node == null) { -+ space.bxteam.divinemc.pathfinding.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator); // DivineMC - handle nodeEvaluatorGenerator - return null; - } else { - // Paper start - Perf: remove streams and optimize collection - List> map = Lists.newArrayList(); - for (final BlockPos pos : positions) { -- map.add(new java.util.AbstractMap.SimpleEntry<>(this.nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos)); -+ map.add(new java.util.AbstractMap.SimpleEntry<>(nodeEvaluator.getTarget(pos.getX(), pos.getY(), pos.getZ()), pos)); // DivineMC - handle nodeEvaluatorGenerator - } - // Paper end - Perf: remove streams and optimize collection -- Path path = this.findPath(node, map, followRange, distance, rangeMultiplier); -- this.nodeEvaluator.done(); -- return path; -+ // DivineMC start - async path processing -+ if (this.nodeEvaluatorGenerator == null) { -+ // run sync :( -+ space.bxteam.divinemc.pathfinding.NodeEvaluatorCache.removeNodeEvaluator(nodeEvaluator); -+ return this.findPath(node, map, followRange, distance, rangeMultiplier); // Gale - Purpur - remove vanilla profiler -+ } -+ -+ return new space.bxteam.divinemc.pathfinding.AsyncPath(Lists.newArrayList(), positions, () -> { -+ try { -+ return this.processPath(nodeEvaluator, node, map, followRange, distance, rangeMultiplier); -+ } catch (Exception e) { -+ e.printStackTrace(); -+ return null; -+ } finally { -+ nodeEvaluator.done(); -+ space.bxteam.divinemc.pathfinding.NodeEvaluatorCache.returnNodeEvaluator(nodeEvaluator); -+ } -+ }); -+ // DivineMC end - } - } - - @Nullable - // Paper start - Perf: remove streams and optimize collection - private Path findPath(Node startNode, List> positions, float followRange, int distance, float rangeMultiplier) { -+ // DivineMC start - split pathfinding into the original sync method for compat and processing for delaying -+ try { -+ return this.processPath(this.nodeEvaluator, startNode, positions, followRange, distance, rangeMultiplier); -+ } catch (Exception e) { -+ e.printStackTrace(); -+ return null; -+ } finally { -+ this.nodeEvaluator.done(); -+ } -+ } -+ -+ private synchronized @org.jetbrains.annotations.NotNull Path processPath(NodeEvaluator nodeEvaluator, Node startNode, List> positions, float followRange, int distance, float rangeMultiplier) { // sync to only use the caching functions in this class on a single thread -+ org.apache.commons.lang3.Validate.isTrue(!positions.isEmpty()); // ensure that we have at least one position, which means we'll always return a path -+ // DivineMC end - ProfilerFiller profilerFiller = Profiler.get(); - profilerFiller.push("find_path"); - profilerFiller.markForCharting(MetricCategory.PATH_FINDING); -@@ -96,7 +141,7 @@ public class PathFinder { - } - - if (!(node.distanceTo(startNode) >= followRange)) { -- int k = this.nodeEvaluator.getNeighbors(this.neighbors, node); -+ int k = nodeEvaluator.getNeighbors(this.neighbors, node); // DivineMC - use provided nodeEvaluator - - for (int l = 0; l < k; l++) { - Node node2 = this.neighbors[l]; -diff --git a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java -index 6308822f819d7cb84c8070c8a7eec1a3f822114b..c54b4c10976851588b74d09220b01aa6d96fab81 100644 ---- a/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java -+++ b/src/main/java/net/minecraft/world/level/pathfinder/SwimNodeEvaluator.java -@@ -15,7 +15,7 @@ import net.minecraft.world.level.block.state.BlockState; - import net.minecraft.world.level.material.FluidState; - - public class SwimNodeEvaluator extends NodeEvaluator { -- private final boolean allowBreaching; -+ public final boolean allowBreaching; // DivineMC - make public - private final Long2ObjectMap pathTypesByPosCache = new Long2ObjectOpenHashMap<>(); - - public SwimNodeEvaluator(boolean canJumpOutOfWater) { -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -index c494d88cda016ff7a4da37ac1cee2a05876e595f..ed44eaad59afa76d8ca8ac74d0cee4fb681dfe83 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -179,4 +179,21 @@ public class DivineConfig { - private static void chatMessageSignatures() { - noChatSign = getBoolean("settings.no-chat-sign", noChatSign); - } -+ -+ public static boolean asyncPathfinding = true; -+ public static int asyncPathfindingMaxThreads = 0; -+ public static int asyncPathfindingKeepalive = 60; -+ private static void asyncPathfinding() { -+ asyncPathfinding = getBoolean("settings.async-pathfinding.enable", asyncPathfinding); -+ asyncPathfindingMaxThreads = getInt("settings.async-pathfinding.max-threads", asyncPathfindingMaxThreads); -+ asyncPathfindingKeepalive = getInt("settings.async-pathfinding.keepalive", asyncPathfindingKeepalive); -+ if (asyncPathfindingMaxThreads < 0) -+ asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncPathfindingMaxThreads, 1); -+ else if (asyncPathfindingMaxThreads == 0) -+ asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1); -+ if (!asyncPathfinding) -+ asyncPathfindingMaxThreads = 0; -+ else -+ Bukkit.getLogger().log(Level.INFO, "Using " + asyncPathfindingMaxThreads + " threads for Async Pathfinding"); -+ } - } -diff --git a/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPath.java b/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPath.java -new file mode 100644 -index 0000000000000000000000000000000000000000..da62b8ad100c809753c37220b395d979df9b6b2e ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPath.java -@@ -0,0 +1,291 @@ -+package space.bxteam.divinemc.pathfinding; -+ -+import net.minecraft.core.BlockPos; -+import net.minecraft.world.entity.Entity; -+import net.minecraft.world.level.pathfinder.Node; -+import net.minecraft.world.level.pathfinder.Path; -+import net.minecraft.world.phys.Vec3; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.ArrayList; -+import java.util.List; -+import java.util.Set; -+import java.util.function.Supplier; -+ -+public class AsyncPath extends Path { -+ /** -+ * marks whether this async path has been processed -+ */ -+ private volatile PathProcessState processState = PathProcessState.WAITING; -+ -+ /** -+ * runnables waiting for this to be processed -+ */ -+ private final List postProcessing = new ArrayList<>(0); -+ -+ /** -+ * a list of positions that this path could path towards -+ */ -+ private final Set positions; -+ -+ /** -+ * the supplier of the real processed path -+ */ -+ private final Supplier pathSupplier; -+ -+ /* -+ * Processed values -+ */ -+ -+ /** -+ * this is a reference to the nodes list in the parent `Path` object -+ */ -+ private final List nodes; -+ /** -+ * the block we're trying to path to -+ *

-+ * while processing, we have no idea where this is so consumers of `Path` should check that the path is processed before checking the target block -+ */ -+ private @Nullable BlockPos target; -+ /** -+ * how far we are to the target -+ *

-+ * while processing, the target could be anywhere but theoretically we're always "close" to a theoretical target so default is 0 -+ */ -+ private float distToTarget = 0; -+ /** -+ * whether we can reach the target -+ *

-+ * while processing, we can always theoretically reach the target so default is true -+ */ -+ private boolean canReach = true; -+ -+ public AsyncPath(@NotNull List emptyNodeList, @NotNull Set positions, @NotNull Supplier pathSupplier) { -+ //noinspection ConstantConditions -+ super(emptyNodeList, null, false); -+ -+ this.nodes = emptyNodeList; -+ this.positions = positions; -+ this.pathSupplier = pathSupplier; -+ -+ AsyncPathProcessor.queue(this); -+ } -+ -+ @Override -+ public boolean isProcessed() { -+ return this.processState == PathProcessState.COMPLETED; -+ } -+ -+ /** -+ * returns the future representing the processing state of this path -+ */ -+ public synchronized void postProcessing(@NotNull Runnable runnable) { -+ if (isProcessed()) { -+ runnable.run(); -+ } else { -+ this.postProcessing.add(runnable); -+ } -+ } -+ -+ /** -+ * an easy way to check if this processing path is the same as an attempted new path -+ * -+ * @param positions - the positions to compare against -+ * @return true if we are processing the same positions -+ */ -+ public boolean hasSameProcessingPositions(final Set positions) { -+ if (this.positions.size() != positions.size()) { -+ return false; -+ } -+ -+ return this.positions.containsAll(positions); -+ } -+ -+ /** -+ * starts processing this path -+ */ -+ public synchronized void process() { -+ if (this.processState == PathProcessState.COMPLETED || -+ this.processState == PathProcessState.PROCESSING) { -+ return; -+ } -+ -+ processState = PathProcessState.PROCESSING; -+ -+ final Path bestPath = this.pathSupplier.get(); -+ -+ this.nodes.addAll(bestPath.nodes); // we mutate this list to reuse the logic in Path -+ this.target = bestPath.getTarget(); -+ this.distToTarget = bestPath.getDistToTarget(); -+ this.canReach = bestPath.canReach(); -+ -+ processState = PathProcessState.COMPLETED; -+ -+ for (Runnable runnable : this.postProcessing) { -+ runnable.run(); -+ } // Run tasks after processing -+ } -+ -+ /** -+ * if this path is accessed while it hasn't processed, just process it in-place -+ */ -+ private void checkProcessed() { -+ if (this.processState == PathProcessState.WAITING || -+ this.processState == PathProcessState.PROCESSING) { // Block if we are on processing -+ this.process(); -+ } -+ } -+ -+ /* -+ * overrides we need for final fields that we cannot modify after processing -+ */ -+ -+ @Override -+ public @NotNull BlockPos getTarget() { -+ this.checkProcessed(); -+ -+ return this.target; -+ } -+ -+ @Override -+ public float getDistToTarget() { -+ this.checkProcessed(); -+ -+ return this.distToTarget; -+ } -+ -+ @Override -+ public boolean canReach() { -+ this.checkProcessed(); -+ -+ return this.canReach; -+ } -+ -+ /* -+ * overrides to ensure we're processed first -+ */ -+ -+ @Override -+ public boolean isDone() { -+ return this.processState == PathProcessState.COMPLETED && super.isDone(); -+ } -+ -+ @Override -+ public void advance() { -+ this.checkProcessed(); -+ -+ super.advance(); -+ } -+ -+ @Override -+ public boolean notStarted() { -+ this.checkProcessed(); -+ -+ return super.notStarted(); -+ } -+ -+ @Nullable -+ @Override -+ public Node getEndNode() { -+ this.checkProcessed(); -+ -+ return super.getEndNode(); -+ } -+ -+ @Override -+ public Node getNode(int index) { -+ this.checkProcessed(); -+ -+ return super.getNode(index); -+ } -+ -+ @Override -+ public void truncateNodes(int length) { -+ this.checkProcessed(); -+ -+ super.truncateNodes(length); -+ } -+ -+ @Override -+ public void replaceNode(int index, Node node) { -+ this.checkProcessed(); -+ -+ super.replaceNode(index, node); -+ } -+ -+ @Override -+ public int getNodeCount() { -+ this.checkProcessed(); -+ -+ return super.getNodeCount(); -+ } -+ -+ @Override -+ public int getNextNodeIndex() { -+ this.checkProcessed(); -+ -+ return super.getNextNodeIndex(); -+ } -+ -+ @Override -+ public void setNextNodeIndex(int nodeIndex) { -+ this.checkProcessed(); -+ -+ super.setNextNodeIndex(nodeIndex); -+ } -+ -+ @Override -+ public Vec3 getEntityPosAtNode(Entity entity, int index) { -+ this.checkProcessed(); -+ -+ return super.getEntityPosAtNode(entity, index); -+ } -+ -+ @Override -+ public BlockPos getNodePos(int index) { -+ this.checkProcessed(); -+ -+ return super.getNodePos(index); -+ } -+ -+ @Override -+ public Vec3 getNextEntityPos(Entity entity) { -+ this.checkProcessed(); -+ -+ return super.getNextEntityPos(entity); -+ } -+ -+ @Override -+ public BlockPos getNextNodePos() { -+ this.checkProcessed(); -+ -+ return super.getNextNodePos(); -+ } -+ -+ @Override -+ public Node getNextNode() { -+ this.checkProcessed(); -+ -+ return super.getNextNode(); -+ } -+ -+ @Nullable -+ @Override -+ public Node getPreviousNode() { -+ this.checkProcessed(); -+ -+ return super.getPreviousNode(); -+ } -+ -+ @Override -+ public boolean hasNext() { -+ this.checkProcessed(); -+ -+ return super.hasNext(); -+ } -+ -+ public PathProcessState getProcessState() { -+ return processState; -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPathProcessor.java b/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPathProcessor.java -new file mode 100644 -index 0000000000000000000000000000000000000000..b3222add41b5725cc7409b6651ffe48b0a2858d7 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/pathfinding/AsyncPathProcessor.java -@@ -0,0 +1,48 @@ -+package space.bxteam.divinemc.pathfinding; -+ -+import com.google.common.util.concurrent.ThreadFactoryBuilder; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.world.level.pathfinder.Path; -+import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; -+ -+import java.util.concurrent.*; -+import java.util.function.Consumer; -+ -+/** -+ * used to handle the scheduling of async path processing -+ */ -+public class AsyncPathProcessor { -+ private static final Executor pathProcessingExecutor = new ThreadPoolExecutor( -+ 1, -+ space.bxteam.divinemc.configuration.DivineConfig.asyncPathfindingMaxThreads, -+ space.bxteam.divinemc.configuration.DivineConfig.asyncPathfindingKeepalive, TimeUnit.SECONDS, -+ new LinkedBlockingQueue<>(), -+ new ThreadFactoryBuilder() -+ .setNameFormat("DivineMC Async Pathfinding Thread - %d") -+ .setPriority(Thread.NORM_PRIORITY - 2) -+ .build() -+ ); -+ -+ protected static CompletableFuture queue(@NotNull AsyncPath path) { -+ return CompletableFuture.runAsync(path::process, pathProcessingExecutor); -+ } -+ -+ /** -+ * takes a possibly unprocessed path, and waits until it is completed -+ * the consumer will be immediately invoked if the path is already processed -+ * the consumer will always be called on the main thread -+ * -+ * @param path a path to wait on -+ * @param afterProcessing a consumer to be called -+ */ -+ public static void awaitProcessing(@Nullable Path path, Consumer<@Nullable Path> afterProcessing) { -+ if (path != null && !path.isProcessed() && path instanceof AsyncPath asyncPath) { -+ asyncPath.postProcessing(() -> -+ MinecraftServer.getServer().scheduleOnMain(() -> afterProcessing.accept(path)) -+ ); -+ } else { -+ afterProcessing.accept(path); -+ } -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorCache.java b/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorCache.java -new file mode 100644 -index 0000000000000000000000000000000000000000..11d4c50dbe54cdbf539883cf3ef2f35fd6658307 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorCache.java -@@ -0,0 +1,44 @@ -+package space.bxteam.divinemc.pathfinding; -+ -+import net.minecraft.world.level.pathfinder.NodeEvaluator; -+import org.apache.commons.lang.Validate; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.Map; -+import java.util.Queue; -+import java.util.concurrent.ConcurrentHashMap; -+import java.util.concurrent.ConcurrentLinkedQueue; -+ -+public class NodeEvaluatorCache { -+ private static final Map> threadLocalNodeEvaluators = new ConcurrentHashMap<>(); -+ private static final Map nodeEvaluatorToGenerator = new ConcurrentHashMap<>(); -+ -+ private static @NotNull Queue getQueueForFeatures(@NotNull NodeEvaluatorFeatures nodeEvaluatorFeatures) { -+ return threadLocalNodeEvaluators.computeIfAbsent(nodeEvaluatorFeatures, key -> new ConcurrentLinkedQueue<>()); -+ } -+ -+ public static @NotNull NodeEvaluator takeNodeEvaluator(@NotNull NodeEvaluatorGenerator generator, @NotNull NodeEvaluator localNodeEvaluator) { -+ final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(localNodeEvaluator); -+ NodeEvaluator nodeEvaluator = getQueueForFeatures(nodeEvaluatorFeatures).poll(); -+ -+ if (nodeEvaluator == null) { -+ nodeEvaluator = generator.generate(nodeEvaluatorFeatures); -+ } -+ -+ nodeEvaluatorToGenerator.put(nodeEvaluator, generator); -+ -+ return nodeEvaluator; -+ } -+ -+ public static void returnNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) { -+ final NodeEvaluatorGenerator generator = nodeEvaluatorToGenerator.remove(nodeEvaluator); -+ Validate.notNull(generator, "NodeEvaluator already returned"); -+ -+ final NodeEvaluatorFeatures nodeEvaluatorFeatures = NodeEvaluatorFeatures.fromNodeEvaluator(nodeEvaluator); -+ getQueueForFeatures(nodeEvaluatorFeatures).offer(nodeEvaluator); -+ } -+ -+ public static void removeNodeEvaluator(@NotNull NodeEvaluator nodeEvaluator) { -+ nodeEvaluatorToGenerator.remove(nodeEvaluator); -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorFeatures.java b/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorFeatures.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e1aeb6b58349042e649219ad521525bbacc5776f ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorFeatures.java -@@ -0,0 +1,23 @@ -+package space.bxteam.divinemc.pathfinding; -+ -+import net.minecraft.world.level.pathfinder.NodeEvaluator; -+import net.minecraft.world.level.pathfinder.SwimNodeEvaluator; -+ -+public record NodeEvaluatorFeatures( -+ NodeEvaluatorType type, -+ boolean canPassDoors, -+ boolean canFloat, -+ boolean canWalkOverFences, -+ boolean canOpenDoors, -+ boolean allowBreaching -+) { -+ public static NodeEvaluatorFeatures fromNodeEvaluator(NodeEvaluator nodeEvaluator) { -+ NodeEvaluatorType type = NodeEvaluatorType.fromNodeEvaluator(nodeEvaluator); -+ boolean canPassDoors = nodeEvaluator.canPassDoors(); -+ boolean canFloat = nodeEvaluator.canFloat(); -+ boolean canWalkOverFences = nodeEvaluator.canWalkOverFences(); -+ boolean canOpenDoors = nodeEvaluator.canOpenDoors(); -+ boolean allowBreaching = nodeEvaluator instanceof SwimNodeEvaluator swimNodeEvaluator && swimNodeEvaluator.allowBreaching; -+ return new NodeEvaluatorFeatures(type, canPassDoors, canFloat, canWalkOverFences, canOpenDoors, allowBreaching); -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorGenerator.java b/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorGenerator.java -new file mode 100644 -index 0000000000000000000000000000000000000000..75704577983e59f36f05451a635f0964f1345a8c ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorGenerator.java -@@ -0,0 +1,9 @@ -+package space.bxteam.divinemc.pathfinding; -+ -+import net.minecraft.world.level.pathfinder.NodeEvaluator; -+import org.jetbrains.annotations.NotNull; -+ -+public interface NodeEvaluatorGenerator { -+ @NotNull -+ NodeEvaluator generate(NodeEvaluatorFeatures nodeEvaluatorFeatures); -+} -diff --git a/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorType.java b/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorType.java -new file mode 100644 -index 0000000000000000000000000000000000000000..897e4f676a9c7e47a291f24602ad56adc0f6bd10 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/pathfinding/NodeEvaluatorType.java -@@ -0,0 +1,17 @@ -+package space.bxteam.divinemc.pathfinding; -+ -+import net.minecraft.world.level.pathfinder.*; -+ -+public enum NodeEvaluatorType { -+ WALK, -+ SWIM, -+ AMPHIBIOUS, -+ FLY; -+ -+ public static NodeEvaluatorType fromNodeEvaluator(NodeEvaluator nodeEvaluator) { -+ if (nodeEvaluator instanceof SwimNodeEvaluator) return SWIM; -+ if (nodeEvaluator instanceof FlyNodeEvaluator) return FLY; -+ if (nodeEvaluator instanceof AmphibiousNodeEvaluator) return AMPHIBIOUS; -+ return WALK; -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/pathfinding/PathProcessState.java b/src/main/java/space/bxteam/divinemc/pathfinding/PathProcessState.java -new file mode 100644 -index 0000000000000000000000000000000000000000..e73550cf2368ca16f3e01a8a0705064182570eeb ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/pathfinding/PathProcessState.java -@@ -0,0 +1,7 @@ -+package space.bxteam.divinemc.pathfinding; -+ -+public enum PathProcessState { -+ WAITING, -+ PROCESSING, -+ COMPLETED, -+} diff --git a/patches/server/0053-lithium-hashed_list.patch b/patches/server/0053-lithium-hashed_list.patch deleted file mode 100644 index 62f19d4..0000000 --- a/patches/server/0053-lithium-hashed_list.patch +++ /dev/null @@ -1,309 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 16 Dec 2024 01:14:03 +0300 -Subject: [PATCH] lithium: hashed_list - - -diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 4ce5cf4f5e2974e3d87514aee920d296a973938e..baa6922fe4547f4fef5719bd54251531340ef44f 100644 ---- a/src/main/java/net/minecraft/world/level/Level.java -+++ b/src/main/java/net/minecraft/world/level/Level.java -@@ -119,9 +119,9 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - public static final int TICKS_PER_DAY = 24000; - public static final int MAX_ENTITY_SPAWN_Y = 20000000; - public static final int MIN_ENTITY_SPAWN_Y = -20000000; -- public final List blockEntityTickers = Lists.newArrayList(); // Paper - public -+ public final List blockEntityTickers = new space.bxteam.divinemc.util.lithium.HashedReferenceList<>(Lists.newArrayList()); // Paper - public // DivineMC - lithium: hashed_list - protected final NeighborUpdater neighborUpdater; -- private final List pendingBlockEntityTickers = Lists.newArrayList(); -+ private final List pendingBlockEntityTickers = new space.bxteam.divinemc.util.lithium.HashedReferenceList<>(Lists.newArrayList()); // DivineMC - lithium: hashed_list - private boolean tickingBlockEntities; - public final Thread thread; - private final boolean isDebug; -diff --git a/src/main/java/space/bxteam/divinemc/util/lithium/HashedReferenceList.java b/src/main/java/space/bxteam/divinemc/util/lithium/HashedReferenceList.java -new file mode 100644 -index 0000000000000000000000000000000000000000..aafa3e581587e3593cec4bb19bd0417ea28d5e67 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/util/lithium/HashedReferenceList.java -@@ -0,0 +1,281 @@ -+package space.bxteam.divinemc.util.lithium; -+ -+import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; -+import it.unimi.dsi.fastutil.objects.ReferenceArrayList; -+import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.Collection; -+import java.util.Iterator; -+import java.util.List; -+import java.util.ListIterator; -+import java.util.NoSuchElementException; -+ -+/** -+ * Wraps a {@link List} with a hash table which provides O(1) lookups for {@link Collection#contains(Object)}. The type -+ * contained by this list must use reference-equality semantics. -+ */ -+@SuppressWarnings("SuspiciousMethodCalls") -+public class HashedReferenceList implements List { -+ private final ReferenceArrayList list; -+ private final Reference2IntOpenHashMap counter; -+ -+ public HashedReferenceList(List list) { -+ this.list = new ReferenceArrayList<>(); -+ this.list.addAll(list); -+ -+ this.counter = new Reference2IntOpenHashMap<>(); -+ this.counter.defaultReturnValue(0); -+ -+ for (T obj : this.list) { -+ this.counter.addTo(obj, 1); -+ } -+ } -+ -+ @Override -+ public int size() { -+ return this.list.size(); -+ } -+ -+ @Override -+ public boolean isEmpty() { -+ return this.list.isEmpty(); -+ } -+ -+ @Override -+ public boolean contains(Object o) { -+ return this.counter.containsKey(o); -+ } -+ -+ @Override -+ public Iterator iterator() { -+ return this.listIterator(); -+ } -+ -+ @Override -+ public Object[] toArray() { -+ return this.list.toArray(); -+ } -+ -+ @Override -+ public T1[] toArray(T1 @NotNull [] a) { -+ return this.list.toArray(a); -+ } -+ -+ @Override -+ public boolean add(T t) { -+ this.trackReferenceAdded(t); -+ -+ return this.list.add(t); -+ } -+ -+ @Override -+ public boolean remove(Object o) { -+ this.trackReferenceRemoved(o); -+ -+ return this.list.remove(o); -+ } -+ -+ @Override -+ public boolean containsAll(Collection c) { -+ for (Object obj : c) { -+ if (!this.counter.containsKey(obj)) { -+ return false; -+ } -+ } -+ -+ return true; -+ } -+ -+ @Override -+ public boolean addAll(Collection c) { -+ for (T obj : c) { -+ this.trackReferenceAdded(obj); -+ } -+ -+ return this.list.addAll(c); -+ } -+ -+ @Override -+ public boolean addAll(int index, Collection c) { -+ for (T obj : c) { -+ this.trackReferenceAdded(obj); -+ } -+ -+ return this.list.addAll(index, c); -+ } -+ -+ @Override -+ public boolean removeAll(@NotNull Collection c) { -+ if (this.size() >= 2 && c.size() > 4 && c instanceof List) { -+ //HashReferenceList uses reference equality, so using ReferenceOpenHashSet is fine -+ c = new ReferenceOpenHashSet<>(c); -+ } -+ this.counter.keySet().removeAll(c); -+ return this.list.removeAll(c); -+ } -+ -+ @Override -+ public boolean retainAll(@NotNull Collection c) { -+ this.counter.keySet().retainAll(c); -+ return this.list.retainAll(c); -+ } -+ -+ @Override -+ public void clear() { -+ this.counter.clear(); -+ this.list.clear(); -+ } -+ -+ @Override -+ public T get(int index) { -+ return this.list.get(index); -+ } -+ -+ @Override -+ public T set(int index, T element) { -+ T prev = this.list.set(index, element); -+ -+ if (prev != element) { -+ if (prev != null) { -+ this.trackReferenceRemoved(prev); -+ } -+ -+ this.trackReferenceAdded(element); -+ } -+ -+ return prev; -+ } -+ -+ @Override -+ public void add(int index, T element) { -+ this.trackReferenceAdded(element); -+ -+ this.list.add(index, element); -+ } -+ -+ @Override -+ public T remove(int index) { -+ T prev = this.list.remove(index); -+ -+ if (prev != null) { -+ this.trackReferenceRemoved(prev); -+ } -+ -+ return prev; -+ } -+ -+ @Override -+ public int indexOf(Object o) { -+ return this.list.indexOf(o); -+ } -+ -+ @Override -+ public int lastIndexOf(Object o) { -+ return this.list.lastIndexOf(o); -+ } -+ -+ @Override -+ public ListIterator listIterator() { -+ return this.listIterator(0); -+ } -+ -+ @Override -+ public ListIterator listIterator(int index) { -+ return new ListIterator<>() { -+ private final ListIterator inner = HashedReferenceList.this.list.listIterator(index); -+ -+ @Override -+ public boolean hasNext() { -+ return this.inner.hasNext(); -+ } -+ -+ @Override -+ public T next() { -+ return this.inner.next(); -+ } -+ -+ @Override -+ public boolean hasPrevious() { -+ return this.inner.hasPrevious(); -+ } -+ -+ @Override -+ public T previous() { -+ return this.inner.previous(); -+ } -+ -+ @Override -+ public int nextIndex() { -+ return this.inner.nextIndex(); -+ } -+ -+ @Override -+ public int previousIndex() { -+ return this.inner.previousIndex(); -+ } -+ -+ @Override -+ public void remove() { -+ int last = this.previousIndex(); -+ -+ if (last == -1) { -+ throw new NoSuchElementException(); -+ } -+ -+ T prev = HashedReferenceList.this.get(last); -+ -+ if (prev != null) { -+ HashedReferenceList.this.trackReferenceRemoved(prev); -+ } -+ -+ this.inner.remove(); -+ } -+ -+ @Override -+ public void set(T t) { -+ int last = this.previousIndex(); -+ -+ if (last == -1) { -+ throw new NoSuchElementException(); -+ } -+ -+ T prev = HashedReferenceList.this.get(last); -+ -+ if (prev != t) { -+ if (prev != null) { -+ HashedReferenceList.this.trackReferenceRemoved(prev); -+ } -+ -+ HashedReferenceList.this.trackReferenceAdded(t); -+ } -+ -+ this.inner.remove(); -+ } -+ -+ @Override -+ public void add(T t) { -+ HashedReferenceList.this.trackReferenceAdded(t); -+ -+ this.inner.add(t); -+ } -+ }; -+ } -+ -+ @Override -+ public List subList(int fromIndex, int toIndex) { -+ return this.list.subList(fromIndex, toIndex); -+ } -+ -+ private void trackReferenceAdded(T t) { -+ this.counter.addTo(t, 1); -+ } -+ -+ @SuppressWarnings("unchecked") -+ private void trackReferenceRemoved(Object o) { -+ if (this.counter.addTo((T) o, -1) <= 1) { -+ this.counter.removeInt(o); -+ } -+ } -+ -+} diff --git a/patches/server/0054-Fix-MC-177381.patch b/patches/server/0054-Fix-MC-177381.patch deleted file mode 100644 index e53d6e6..0000000 --- a/patches/server/0054-Fix-MC-177381.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 16 Dec 2024 01:22:14 +0300 -Subject: [PATCH] Fix MC-177381 - -Original bug on Mojira https://bugs.mojang.com/browse/MC-177381 - -diff --git a/src/main/java/net/minecraft/server/commands/LocateCommand.java b/src/main/java/net/minecraft/server/commands/LocateCommand.java -index 2972f041eea95b92b37c2ab869f9f8ed3d142a27..894bc308d623be6afded9aceca9a85c3ec4ac66d 100644 ---- a/src/main/java/net/minecraft/server/commands/LocateCommand.java -+++ b/src/main/java/net/minecraft/server/commands/LocateCommand.java -@@ -196,8 +196,10 @@ public class LocateCommand { - } - - private static float dist(int x1, int y1, int x2, int y2) { -- int i = x2 - x1; -- int j = y2 - y1; -- return Mth.sqrt((float)(i * i + j * j)); -+ // DivineMC start - Fix MC-177381 -+ double i = x2 - x1; -+ double j = y2 - y1; -+ return (float) Math.hypot(i, j); -+ // DivineMC end - } - } diff --git a/patches/server/0055-Skip-event-if-no-listeners.patch b/patches/server/0055-Skip-event-if-no-listeners.patch deleted file mode 100644 index a965e98..0000000 --- a/patches/server/0055-Skip-event-if-no-listeners.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 16 Dec 2024 20:19:27 +0300 -Subject: [PATCH] Skip event if no listeners - - -diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -index a1c9726d25479b5326fe2fa2b0f5a98d6b2da4c5..2219edf3e56a2eade1049f448360247f48e3c52a 100644 ---- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -@@ -35,15 +35,16 @@ class PaperEventManager { - - // SimplePluginManager - public void callEvent(@NotNull Event event) { -+ // DivineMC start - Skip event if no listeners -+ RegisteredListener[] listeners = event.getHandlers().getRegisteredListeners(); -+ if (listeners.length == 0) return; -+ // DivineMC end - if (event.isAsynchronous() && this.server.isPrimaryThread()) { - throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously."); - } else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) { - throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously."); - } - -- HandlerList handlers = event.getHandlers(); -- RegisteredListener[] listeners = handlers.getRegisteredListeners(); -- - for (RegisteredListener registration : listeners) { - if (!registration.getPlugin().isEnabled()) { - continue; diff --git a/patches/server/0056-Multithreaded-Tracker.patch b/patches/server/0056-Multithreaded-Tracker.patch deleted file mode 100644 index ba5a91c..0000000 --- a/patches/server/0056-Multithreaded-Tracker.patch +++ /dev/null @@ -1,544 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Mon, 16 Dec 2024 20:43:11 +0300 -Subject: [PATCH] Multithreaded Tracker - - -diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -index 2219edf3e56a2eade1049f448360247f48e3c52a..6f50be80a88504d0948ce9635eadfee9a208ae82 100644 ---- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java -@@ -42,6 +42,12 @@ class PaperEventManager { - if (event.isAsynchronous() && this.server.isPrimaryThread()) { - throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously."); - } else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) { -+ // DivineMC start - Multithreaded tracker -+ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled) { -+ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(event::callEvent); -+ return; -+ } -+ // DivineMC end - throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously."); - } - -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index 7ef101bdb4e9b37bd2e73247ef916769789de2ea..9d344cc8b50a70efdd325a7b7fd8768f73285f58 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -243,9 +243,19 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - return; - } - final ServerPlayer[] backingSet = inRange.getRawDataUnchecked(); -- for (int i = 0, len = inRange.size(); i < len; i++) { -- ++(backingSet[i].mobCounts[index]); -+ // DivineMC start - Multithreaded tracker -+ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled) { -+ for (int i = 0, len = inRange.size(); i < len; i++) { -+ final ServerPlayer player = backingSet[i]; -+ if (player == null) continue; -+ ++(player.mobCounts[index]); -+ } -+ } else { -+ for (int i = 0, len = inRange.size(); i < len; i++) { -+ ++(backingSet[i].mobCounts[index]); -+ } - } -+ // DivineMC end - } - // Paper start - per player mob count backoff - public void updateFailurePlayerMobTypeMap(int chunkX, int chunkZ, net.minecraft.world.entity.MobCategory mobCategory) { -@@ -956,6 +966,21 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity)entity).moonrise$setTrackedEntity(null); // Paper - optimise entity tracker - } - -+ // DivineMC start - Multithreaded tracker -+ private final java.util.concurrent.ConcurrentLinkedQueue trackerMainThreadTasks = new java.util.concurrent.ConcurrentLinkedQueue<>(); -+ private boolean tracking = false; -+ -+ public void runOnTrackerMainThread(final Runnable runnable) { -+ //final boolean isOnMain = ca.spottedleaf.moonrise.common.util.TickThread.isTickThread(); -+ //System.out.println(isOnMain); -+ if (false && this.tracking) { // TODO: check here -+ this.trackerMainThreadTasks.add(runnable); -+ } else { -+ runnable.run(); -+ } -+ } -+ // DivineMC end -+ - // Paper start - optimise entity tracker - private void newTrackerTick() { - final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup entityLookup = (ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup)((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getEntityLookup();; -@@ -978,6 +1003,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - // Paper end - optimise entity tracker - - protected void tick() { -+ // DivineMC start - Multithreaded tracker -+ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled) { -+ final ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel level = this.level; -+ space.bxteam.divinemc.tracker.MultithreadedTracker.tick(level); -+ return; -+ } -+ // DivineMC end - // Paper start - optimise entity tracker - if (true) { - this.newTrackerTick(); -@@ -1127,7 +1159,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - final Entity entity; - private final int range; - SectionPos lastSectionPos; -- public final Set seenBy = new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl -+ public final Set seenBy = space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled -+ ? Sets.newConcurrentHashSet() -+ : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(); // Paper - Perf: optimise map impl // DivineMC - Multithreaded tracker - - // Paper start - optimise entity tracker - private long lastChunkUpdate = -1L; -@@ -1154,21 +1188,55 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - this.lastTrackedChunk = chunk; - - final ServerPlayer[] playersRaw = players.getRawDataUnchecked(); -+ final int playersLen = players.size(); // Ensure length won't change in the future tasks -+ -+ // DivineMC start - Multithreaded tracker -+ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled && space.bxteam.divinemc.configuration.DivineConfig.multithreadedCompatModeEnabled) { -+ final boolean isServerPlayer = this.entity instanceof ServerPlayer; -+ final boolean isRealPlayer = isServerPlayer && ((ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer) this.entity).moonrise$isRealPlayer(); -+ Runnable updatePlayerTasks = () -> { -+ for (int i = 0; i < playersLen; ++i) { -+ final ServerPlayer player = playersRaw[i]; -+ this.updatePlayer(player); -+ } - -- for (int i = 0, len = players.size(); i < len; ++i) { -- final ServerPlayer player = playersRaw[i]; -- this.updatePlayer(player); -- } -+ if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { -+ // need to purge any players possible not in the chunk list -+ for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { -+ final ServerPlayer player = conn.getPlayer(); -+ if (!players.contains(player)) { -+ this.removePlayer(player); -+ } -+ } -+ } -+ }; -+ -+ // Only update asynchronously for real player, and sync update for fake players -+ // This can fix compatibility issue with NPC plugins using real entity type, like Citizens -+ // To prevent visible issue with player type NPCs -+ // btw, still recommend to use packet based NPC plugins, like ZNPC Plus, Adyeshach, Fancy NPC, etc. -+ if (isRealPlayer || !isServerPlayer) { -+ space.bxteam.divinemc.tracker.MultithreadedTracker.getTrackerExecutor().execute(updatePlayerTasks); -+ } else { -+ updatePlayerTasks.run(); -+ } -+ } else { -+ for (int i = 0, len = players.size(); i < len; ++i) { -+ final ServerPlayer player = playersRaw[i]; -+ this.updatePlayer(player); -+ } - -- if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { -- // need to purge any players possible not in the chunk list -- for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { -- final ServerPlayer player = conn.getPlayer(); -- if (!players.contains(player)) { -- this.removePlayer(player); -+ if (lastChunkUpdate != currChunkUpdate || lastTrackedChunk != chunk) { -+ // need to purge any players possible not in the chunk list -+ for (final ServerPlayerConnection conn : new java.util.ArrayList<>(this.seenBy)) { -+ final ServerPlayer player = conn.getPlayer(); -+ if (!players.contains(player)) { -+ this.removePlayer(player); -+ } - } - } - } -+ // DivineMC end - } - - @Override -@@ -1228,14 +1296,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - public void broadcast(Packet packet) { -- Iterator iterator = this.seenBy.iterator(); -- -- while (iterator.hasNext()) { -- ServerPlayerConnection serverplayerconnection = (ServerPlayerConnection) iterator.next(); -- -+ // DivineMC start - Multithreaded tracker -+ for (ServerPlayerConnection serverplayerconnection : this.seenBy.toArray(new ServerPlayerConnection[0])) { - serverplayerconnection.send(packet); - } -- -+ // DivineMC end - } - - public void broadcastAndSend(Packet packet) { -@@ -1247,18 +1312,15 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - public void broadcastRemoved() { -- Iterator iterator = this.seenBy.iterator(); -- -- while (iterator.hasNext()) { -- ServerPlayerConnection serverplayerconnection = (ServerPlayerConnection) iterator.next(); -- -+ // DivineMC start - Multithreaded tracker -+ for (ServerPlayerConnection serverplayerconnection : this.seenBy.toArray(new ServerPlayerConnection[0])) { - this.serverEntity.removePairing(serverplayerconnection.getPlayer()); - } -- -+ // DivineMC end - } - - public void removePlayer(ServerPlayer player) { -- org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot -+ //org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot // DivineMC - Multithreaded tracker - we don't need this - if (this.seenBy.remove(player.connection)) { - this.serverEntity.removePairing(player); - } -@@ -1266,8 +1328,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - } - - public void updatePlayer(ServerPlayer player) { -- org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot -+ //org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot // DivineMC - Multithreaded tracker - we don't need this - if (player != this.entity) { -+ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled && player == null) return; // DivineMC - Multithreaded tracker - // Paper start - remove allocation of Vec3D here - // Vec3 vec3d = player.position().subtract(this.entity.position()); - double vec3d_dx = player.getX() - this.entity.getX(); -diff --git a/src/main/java/net/minecraft/server/level/ServerBossEvent.java b/src/main/java/net/minecraft/server/level/ServerBossEvent.java -index 4f91107f9ae42f96c060c310596db9aa869a8dbc..39dada67dd2ed13f7f191e42c366f4a96767f275 100644 ---- a/src/main/java/net/minecraft/server/level/ServerBossEvent.java -+++ b/src/main/java/net/minecraft/server/level/ServerBossEvent.java -@@ -13,7 +13,9 @@ import net.minecraft.util.Mth; - import net.minecraft.world.BossEvent; - - public class ServerBossEvent extends BossEvent { -- private final Set players = Sets.newHashSet(); -+ private final Set players = space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled -+ ? Sets.newConcurrentHashSet() -+ : Sets.newHashSet(); // DivineMC - Multithreaded tracker - players can be removed in async tracking - private final Set unmodifiablePlayers = Collections.unmodifiableSet(this.players); - public boolean visible = true; - -diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java -index 944de5ff109b15b76b69326e69b6b1c2afb63dd1..415fe9e2bf9a110021edbdc39e4498ccc8366636 100644 ---- a/src/main/java/net/minecraft/server/level/ServerEntity.java -+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java -@@ -119,7 +119,13 @@ public class ServerEntity { - this.broadcastAndSend(new ClientboundSetPassengersPacket(this.entity)); // CraftBukkit - ServerEntity.removedPassengers(list, this.lastPassengers).forEach((entity) -> { - if (entity instanceof ServerPlayer entityplayer) { -- entityplayer.connection.teleport(entityplayer.getX(), entityplayer.getY(), entityplayer.getZ(), entityplayer.getYRot(), entityplayer.getXRot()); -+ // DivineMC start - Multithreaded tracker - Ensure teleport is executed on server thread -+ if (space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled && Thread.currentThread() instanceof space.bxteam.divinemc.tracker.MultithreadedTracker.MultithreadedTrackerThread) { -+ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> entityplayer.connection.teleport(entityplayer.getX(), entityplayer.getY(), entityplayer.getZ(), entityplayer.getYRot(), entityplayer.getXRot())); -+ } else { -+ entityplayer.connection.teleport(entityplayer.getX(), entityplayer.getY(), entityplayer.getZ(), entityplayer.getYRot(), entityplayer.getXRot()); -+ } -+ // DivineMC end - } - - }); -@@ -336,7 +342,11 @@ public class ServerEntity { - - public void removePairing(ServerPlayer player) { - this.entity.stopSeenByPlayer(player); -- player.connection.send(new ClientboundRemoveEntitiesPacket(new int[]{this.entity.getId()})); -+ // DivineMC start - Multithreaded tracker - send in main thread -+ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> -+ player.connection.send(new ClientboundRemoveEntitiesPacket(this.entity.getId())) -+ ); -+ // DivineMC end - } - - public void addPairing(ServerPlayer player) { -@@ -344,7 +354,11 @@ public class ServerEntity { - - Objects.requireNonNull(list); - this.sendPairingData(player, list::add); -- player.connection.send(new ClientboundBundlePacket(list)); -+ // DivineMC start - Multithreaded tracker - send in main thread -+ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> -+ player.connection.send(new ClientboundBundlePacket(list)) -+ ); -+ // DivineMC end - this.entity.startSeenByPlayer(player); - } - -@@ -451,7 +465,10 @@ public class ServerEntity { - - if (list != null) { - this.trackedDataValues = datawatcher.getNonDefaultValues(); -- this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list)); -+ // DivineMC start - Multithreaded tracker - send in main thread -+ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> -+ this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list)) -+ ); - } - - if (this.entity instanceof LivingEntity) { -@@ -461,12 +478,16 @@ public class ServerEntity { - // DivineMC end - - if (!set.isEmpty()) { -+ final Set copy = new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(set); -+ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> { - // CraftBukkit start - Send scaled max health - if (this.entity instanceof ServerPlayer) { -- ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(set, false); -+ ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(copy, false); - } - // CraftBukkit end -- this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), set)); -+ this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), copy)); -+ }); -+ // DivineMC end - } - - attributes.clear(); // DivineMC -diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index b0095c0848ca0162944961a24c7b807fb5846b06..8b812d1ec336fa30cd779d5ebb02b5811c97d369 100644 ---- a/src/main/java/net/minecraft/server/level/ServerLevel.java -+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -2604,7 +2604,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe - - @Override - public LevelEntityGetter getEntities() { -- org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot -+ //org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot // DivineMC - Multithreaded tracker - return this.moonrise$getEntityLookup(); // Paper - rewrite chunk system - } - -diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index ac1d8c5eb616d11fc1bda929a8607daf2d616b46..40e2da69d6907bf664ef37fe8dbe0951b03d3aec 100644 ---- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -1888,7 +1888,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl - } - - public void internalTeleport(PositionMoveRotation positionmoverotation, Set set) { -- org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper -+ //org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper // DivineMC - Multithreaded tracker - // Paper start - Prevent teleporting dead entities - if (player.isRemoved()) { - LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName()); -diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeInstance.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -index 27a7852a5d3f8c8960f098646ff5587c50556aa5..ac9534203a256b2a5c1d94e4b3996990e8861aca 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeInstance.java -@@ -24,8 +24,11 @@ public class AttributeInstance { - private final Map> modifiersByOperation = Maps.newEnumMap( - AttributeModifier.Operation.class - ); -- private final Map modifierById = new Object2ObjectArrayMap<>(); -- private final Map permanentModifiers = new Object2ObjectArrayMap<>(); -+ // DivineMC start - Multithreaded tracker -+ private final boolean multiThreadedTrackingEnabled = space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled; -+ private final Map modifierById = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>(); -+ private final Map permanentModifiers = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new Object2ObjectArrayMap<>(); -+ // DivineMC end - private double baseValue; - private boolean dirty = true; - private double cachedValue; -diff --git a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -index 98fa43c8a34650795a0ae1ebc28ce17ec1ba5271..3b27e866185f6dcd1c7baaa3fdef591f1a0bdac3 100644 ---- a/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -+++ b/src/main/java/net/minecraft/world/entity/ai/attributes/AttributeMap.java -@@ -19,9 +19,12 @@ import org.slf4j.Logger; - - public class AttributeMap { - private static final Logger LOGGER = LogUtils.getLogger(); -- private final Map, AttributeInstance> attributes = new Object2ObjectOpenHashMap<>(); -- private final Set attributesToSync = new ObjectOpenHashSet<>(); -- private final Set attributesToUpdate = new ObjectOpenHashSet<>(); -+ // DivineMC start - Multithreaded tracker -+ private final boolean multiThreadedTrackingEnabled = space.bxteam.divinemc.configuration.DivineConfig.multithreadedEnabled; -+ private final Map, AttributeInstance> attributes = multiThreadedTrackingEnabled ? new java.util.concurrent.ConcurrentHashMap<>() : new it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap<>(0); -+ private final Set attributesToSync = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); -+ private final Set attributesToUpdate = multiThreadedTrackingEnabled ? com.google.common.collect.Sets.newConcurrentHashSet() : new it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet<>(0); -+ // DivineMC end - private final AttributeSupplier supplier; - private final java.util.function.Function, AttributeInstance> createInstance; // Pufferfish - private final net.minecraft.world.entity.LivingEntity entity; // Purpur - Ridables -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -index ed44eaad59afa76d8ca8ac74d0cee4fb681dfe83..fe9bff9d542d731aede4903e807acd876b0286d0 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -196,4 +196,25 @@ public class DivineConfig { - else - Bukkit.getLogger().log(Level.INFO, "Using " + asyncPathfindingMaxThreads + " threads for Async Pathfinding"); - } -+ -+ public static boolean multithreadedEnabled = false; -+ public static boolean multithreadedCompatModeEnabled = false; -+ public static int asyncEntityTrackerMaxThreads = 0; -+ public static int asyncEntityTrackerKeepalive = 60; -+ private static void multithreadedTracker() { -+ multithreadedEnabled = getBoolean("settings.multithreaded-tracker.enable", multithreadedEnabled); -+ multithreadedCompatModeEnabled = getBoolean("settings.multithreaded-tracker.compat-mode", multithreadedCompatModeEnabled); -+ asyncEntityTrackerMaxThreads = getInt("settings.multithreaded-tracker.max-threads", asyncEntityTrackerMaxThreads); -+ asyncEntityTrackerKeepalive = getInt("settings.multithreaded-tracker.keepalive", asyncEntityTrackerKeepalive); -+ -+ if (asyncEntityTrackerMaxThreads < 0) -+ asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncEntityTrackerMaxThreads, 1); -+ else if (asyncEntityTrackerMaxThreads == 0) -+ asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1); -+ -+ if (!multithreadedEnabled) -+ asyncEntityTrackerMaxThreads = 0; -+ else -+ Bukkit.getLogger().log(Level.INFO, "Using " + asyncEntityTrackerMaxThreads + " threads for Async Entity Tracker"); -+ } - } -diff --git a/src/main/java/space/bxteam/divinemc/tracker/MultithreadedTracker.java b/src/main/java/space/bxteam/divinemc/tracker/MultithreadedTracker.java -new file mode 100644 -index 0000000000000000000000000000000000000000..3621d8641eb657d84b886589bc957bdf8798785a ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/tracker/MultithreadedTracker.java -@@ -0,0 +1,139 @@ -+package space.bxteam.divinemc.tracker; -+ -+import ca.spottedleaf.moonrise.common.list.ReferenceList; -+import ca.spottedleaf.moonrise.common.misc.NearbyPlayers; -+import ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel; -+import ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup; -+import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity; -+import ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity; -+import com.google.common.util.concurrent.ThreadFactoryBuilder; -+import net.minecraft.server.level.ChunkMap; -+import net.minecraft.server.level.FullChunkStatus; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.world.entity.Entity; -+import org.apache.logging.log4j.LogManager; -+import org.apache.logging.log4j.Logger; -+ -+import java.util.concurrent.Executor; -+import java.util.concurrent.LinkedBlockingQueue; -+import java.util.concurrent.ThreadPoolExecutor; -+import java.util.concurrent.TimeUnit; -+ -+public class MultithreadedTracker { -+ private static final Logger LOGGER = LogManager.getLogger("MultithreadedTracker"); -+ public static class MultithreadedTrackerThread extends Thread { -+ @Override -+ public void run() { -+ super.run(); -+ } -+ } -+ private static final Executor trackerExecutor = new ThreadPoolExecutor( -+ 1, -+ space.bxteam.divinemc.configuration.DivineConfig.asyncEntityTrackerMaxThreads, -+ space.bxteam.divinemc.configuration.DivineConfig.asyncEntityTrackerKeepalive, TimeUnit.SECONDS, -+ new LinkedBlockingQueue<>(), -+ new ThreadFactoryBuilder() -+ .setThreadFactory( -+ r -> new MultithreadedTrackerThread() { -+ @Override -+ public void run() { -+ r.run(); -+ } -+ } -+ ) -+ .setNameFormat("DivineMC Async Tracker Thread - %d") -+ .setPriority(Thread.NORM_PRIORITY - 2) -+ .build()); -+ -+ private MultithreadedTracker() { -+ } -+ -+ public static Executor getTrackerExecutor() { -+ return trackerExecutor; -+ } -+ -+ public static void tick(ChunkSystemServerLevel level) { -+ try { -+ if (!space.bxteam.divinemc.configuration.DivineConfig.multithreadedCompatModeEnabled) { -+ tickAsync(level); -+ } else { -+ tickAsyncWithCompatMode(level); -+ } -+ } catch (Exception e) { -+ LOGGER.error("Error occurred while executing async task.", e); -+ } -+ } -+ -+ private static void tickAsync(ChunkSystemServerLevel level) { -+ final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers(); -+ final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup(); -+ -+ final ReferenceList trackerEntities = entityLookup.trackerEntities; -+ final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked(); -+ -+ // Move tracking to off-main -+ trackerExecutor.execute(() -> { -+ for (final Entity entity : trackerEntitiesRaw) { -+ if (entity == null) continue; -+ -+ final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity(); -+ -+ if (tracker == null) continue; -+ -+ ((EntityTrackerTrackedEntity) tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); -+ tracker.serverEntity.sendChanges(); -+ } -+ }); -+ } -+ -+ private static void tickAsyncWithCompatMode(ChunkSystemServerLevel level) { -+ final NearbyPlayers nearbyPlayers = level.moonrise$getNearbyPlayers(); -+ final ServerEntityLookup entityLookup = (ServerEntityLookup) level.moonrise$getEntityLookup(); -+ -+ final ReferenceList trackerEntities = entityLookup.trackerEntities; -+ final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked(); -+ final Runnable[] sendChangesTasks = new Runnable[trackerEntitiesRaw.length]; -+ int index = 0; -+ -+ for (final Entity entity : trackerEntitiesRaw) { -+ if (entity == null) continue; -+ -+ final ChunkMap.TrackedEntity tracker = ((EntityTrackerEntity) entity).moonrise$getTrackedEntity(); -+ -+ if (tracker == null) continue; -+ -+ ((EntityTrackerTrackedEntity) tracker).moonrise$tick(nearbyPlayers.getChunk(entity.chunkPosition())); -+ sendChangesTasks[index++] = () -> tracker.serverEntity.sendChanges(); // Collect send changes to task array -+ } -+ -+ // batch submit tasks -+ trackerExecutor.execute(() -> { -+ for (final Runnable sendChanges : sendChangesTasks) { -+ if (sendChanges == null) continue; -+ -+ sendChanges.run(); -+ } -+ }); -+ } -+ -+ // Original ChunkMap#newTrackerTick of Paper -+ // Just for diff usage for future update -+ private static void tickOriginal(ServerLevel level) { -+ final ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup entityLookup = (ca.spottedleaf.moonrise.patches.chunk_system.level.entity.server.ServerEntityLookup) ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel) level).moonrise$getEntityLookup(); -+ -+ final ca.spottedleaf.moonrise.common.list.ReferenceList trackerEntities = entityLookup.trackerEntities; -+ final Entity[] trackerEntitiesRaw = trackerEntities.getRawDataUnchecked(); -+ for (int i = 0, len = trackerEntities.size(); i < len; ++i) { -+ final Entity entity = trackerEntitiesRaw[i]; -+ final ChunkMap.TrackedEntity tracker = ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity) entity).moonrise$getTrackedEntity(); -+ if (tracker == null) { -+ continue; -+ } -+ ((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity) tracker).moonrise$tick(((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity) entity).moonrise$getChunkData().nearbyPlayers); -+ if (((ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerTrackedEntity) tracker).moonrise$hasPlayers() -+ || ((ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity) entity).moonrise$getChunkStatus().isOrAfter(FullChunkStatus.ENTITY_TICKING)) { -+ tracker.serverEntity.sendChanges(); -+ } -+ } -+ } -+} diff --git a/patches/unapplied/server/0055-Implement-Linear-region-format.patch b/patches/unapplied/server/0055-Implement-Linear-region-format.patch deleted file mode 100644 index 5e8938f..0000000 --- a/patches/unapplied/server/0055-Implement-Linear-region-format.patch +++ /dev/null @@ -1,858 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: NONPLAYT <76615486+NONPLAYT@users.noreply.github.com> -Date: Wed, 10 Jul 2024 02:40:08 +0300 -Subject: [PATCH] Implement Linear region format - -Status: may be replaced with SlimeWorldManager soon - -diff --git a/build.gradle.kts b/build.gradle.kts -index 3e2a092c797ec7918f5c4b838f28b0778c70531c..bb44922202e3cdb705a4773ea7c9ec807b5f3de2 100644 ---- a/build.gradle.kts -+++ b/build.gradle.kts -@@ -30,6 +30,10 @@ dependencies { - alsoShade(log4jPlugins.output) - implementation("io.netty:netty-codec-haproxy:4.1.97.Final") // Paper - Add support for proxy protocol - // Paper end -+ // DivineMC start -+ implementation("com.github.luben:zstd-jni:1.5.6-3") -+ implementation("org.lz4:lz4-java:1.8.0") -+ // DivineMC end - implementation("org.apache.logging.log4j:log4j-iostreams:2.22.1") // Paper - remove exclusion - implementation("org.ow2.asm:asm-commons:9.7.1") - implementation("org.spongepowered:configurate-yaml:4.2.0-SNAPSHOT") // Paper - config files -diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java -index 73df26b27146bbad2106d57b22dd3c792ed3dd1d..a4d16996fae07f943ee078ce3d2e7b22747fc2d1 100644 ---- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java -+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java -@@ -7,8 +7,8 @@ public interface ChunkSystemRegionFileStorage { - - public boolean moonrise$doesRegionFileNotExistNoIO(final int chunkX, final int chunkZ); - -- public RegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ); -+ public space.bxteam.divinemc.region.AbstractRegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ); // DivineMC - -- public RegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException; -+ public space.bxteam.divinemc.region.AbstractRegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException; // DivineMC - - } -diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java -index 3218cbf84f54daf06e84442d5eb1a36d8da6b215..0db1f8f64a261780e6692755669fa573a2c2b199 100644 ---- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java -+++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java -@@ -1043,9 +1043,9 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread { - return ((ChunkSystemRegionFileStorage)(Object)this.getCache()).moonrise$doesRegionFileNotExistNoIO(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) { // DivineMC - final RegionFileStorage cache = this.getCache(); -- final RegionFile regionFile; -+ final space.bxteam.divinemc.region.AbstractRegionFile regionFile; // DivineMC - synchronized (cache) { - try { - if (existingOnly) { -@@ -1061,9 +1061,9 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread { - } - } - -- public T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function function) { -+ public T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function function) { // DivineMC - final RegionFileStorage cache = this.getCache(); -- final RegionFile regionFile; -+ final space.bxteam.divinemc.region.AbstractRegionFile regionFile; - - synchronized (cache) { - regionFile = ((ChunkSystemRegionFileStorage)(Object)cache).moonrise$getRegionFileIfLoaded(chunkX, chunkZ); -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 8658296b919fd6fa28e64a64186060d3704271db..52bdfd853cf9d42a6c58f0616faf031c8e91fed8 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -981,10 +981,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> progressMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap()); - volatile Component status = Component.translatable("optimizeWorld.stage.counting"); -- static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$"); -+ static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.(linear | mca)$"); // DivineMC - final DimensionDataStorage overworldDataStorage; - - public WorldUpgrader(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, RegistryAccess dynamicRegistryManager, boolean eraseCache, boolean recreateRegionFiles) { -@@ -400,7 +400,7 @@ public class WorldUpgrader { - - private static List getAllChunkPositions(RegionStorageInfo key, Path regionDirectory) { - File[] afile = regionDirectory.toFile().listFiles((file, s) -> { -- return s.endsWith(".mca"); -+ return s.endsWith(".linear") || s.endsWith(".mca"); // DivineMC - }); - - if (afile == null) { -@@ -420,7 +420,7 @@ public class WorldUpgrader { - List list1 = Lists.newArrayList(); - - try { -- RegionFile regionfile = new RegionFile(key, file.toPath(), regionDirectory, true); -+ space.bxteam.divinemc.region.AbstractRegionFile regionfile = space.bxteam.divinemc.region.AbstractRegionFileFactory.getAbstractRegionFile(key, file.toPath(), regionDirectory, true); // DivineMC - - try { - for (int i1 = 0; i1 < 32; ++i1) { -@@ -483,7 +483,7 @@ public class WorldUpgrader { - - protected abstract boolean tryProcessOnePosition(T storage, ChunkPos chunkPos, ResourceKey worldKey); - -- private void onFileFinished(RegionFile regionFile) { -+ private void onFileFinished(space.bxteam.divinemc.region.AbstractRegionFile regionFile) { // DivineMC - if (WorldUpgrader.this.recreateRegionFiles) { - if (this.previousWriteFuture != null) { - this.previousWriteFuture.join(); -@@ -508,7 +508,7 @@ public class WorldUpgrader { - } - } - -- static record FileToUpgrade(RegionFile file, List chunksToUpgrade) { -+ static record FileToUpgrade(space.bxteam.divinemc.region.AbstractRegionFile file, List chunksToUpgrade) { // DivineMC - - } - -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 1e0439cf3f4008fa430acb90b45f5bc4cdd6d7f2..05b356177fc0f65de51f03f7ef1a2f03d85e92ff 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 -@@ -28,7 +28,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, space.bxteam.divinemc.region.AbstractRegionFile { // DivineMC - - private static final Logger LOGGER = LogUtils.getLogger(); - private static final int SECTOR_BYTES = 4096; -@@ -129,7 +129,7 @@ public class RegionFile implements AutoCloseable { - } - - // note: only call for CHUNK regionfiles -- boolean recalculateHeader() throws IOException { -+ public boolean recalculateHeader() throws IOException { // DivineMC - make public - if (!this.canRecalcHeader) { - return false; - } -@@ -928,10 +928,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) { // DivineMC - 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 { // DivineMC - final int offset = getChunkIndex(x, z); - boolean previous = this.oversized[offset] == 1; - this.oversized[offset] = (byte) (oversized ? 1 : 0); -@@ -970,7 +970,7 @@ public class RegionFile implements AutoCloseable { - return this.path.getParent().resolve(this.path.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 { // DivineMC - 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 e623bb14c44a734607e7c6365620d59a3db9f1da..f64715e4fc875dcd714e3d5e9b4d53f32b8ea2e7 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,7 +21,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - - 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(); // DivineMC - private final RegionStorageInfo info; - private final Path folder; - private final boolean sync; -@@ -30,7 +30,9 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - private static final int REGION_SHIFT = 5; - private static final int MAX_NON_EXISTING_CACHE = 1024 * 64; - private final it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet nonExistingRegionFiles = new it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet(MAX_NON_EXISTING_CACHE+1); -- private static String getRegionFileName(final int chunkX, final int chunkZ) { -+ private static String getRegionFileName(final RegionStorageInfo info, final int chunkX, final int chunkZ) { -+ if (info.regionFormat().equals(space.bxteam.divinemc.region.RegionFileFormat.LINEAR)) -+ return "r." + (chunkX >> REGION_SHIFT) + "." + (chunkZ >> REGION_SHIFT) + ".linear"; // DivineMC - linear - return "r." + (chunkX >> REGION_SHIFT) + "." + (chunkZ >> REGION_SHIFT) + ".mca"; - } - -@@ -66,15 +68,15 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - } - - @Override -- public synchronized final RegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ) { -+ public synchronized final space.bxteam.divinemc.region.AbstractRegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ) { // DivineMC - return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT)); - } - - @Override -- public synchronized final RegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException { -+ public synchronized final space.bxteam.divinemc.region.AbstractRegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException { // DivineMC - final long key = ChunkPos.asLong(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT); - -- RegionFile ret = this.regionCache.getAndMoveToFirst(key); -+ space.bxteam.divinemc.region.AbstractRegionFile ret = this.regionCache.getAndMoveToFirst(key); // DivineMC - if (ret != null) { - return ret; - } -@@ -87,7 +89,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - this.regionCache.removeLast().close(); - } - -- final Path regionPath = this.folder.resolve(getRegionFileName(chunkX, chunkZ)); -+ final Path regionPath = this.folder.resolve(getRegionFileName(this.info, chunkX, chunkZ)); // DivineMC - - if (!java.nio.file.Files.exists(regionPath)) { - this.markNonExisting(key); -@@ -98,7 +100,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - - FileUtil.createDirectoriesSafe(this.folder); - -- ret = new RegionFile(this.info, regionPath, this.folder, this.sync); -+ ret = space.bxteam.divinemc.region.AbstractRegionFileFactory.getAbstractRegionFile(this.info, regionPath, this.folder, this.sync); // DivineMC - - this.regionCache.putAndMoveToFirst(key, ret); - -@@ -143,7 +145,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - this.isChunkData = isChunkDataFolder(this.folder); // Paper - recalculate region file headers - } - -- public RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - public -+ public space.bxteam.divinemc.region.AbstractRegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - public // DivineMC - // Paper start - rewrite chunk system - if (existingOnly) { - return this.moonrise$getRegionFileIfExists(chunkcoordintpair.x, chunkcoordintpair.z); -@@ -151,7 +153,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - synchronized (this) { - final long key = ChunkPos.asLong(chunkcoordintpair.x >> REGION_SHIFT, chunkcoordintpair.z >> REGION_SHIFT); - -- RegionFile ret = this.regionCache.getAndMoveToFirst(key); -+ space.bxteam.divinemc.region.AbstractRegionFile ret = this.regionCache.getAndMoveToFirst(key); // DivineMC - if (ret != null) { - return ret; - } -@@ -160,13 +162,13 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - this.regionCache.removeLast().close(); - } - -- final Path regionPath = this.folder.resolve(getRegionFileName(chunkcoordintpair.x, chunkcoordintpair.z)); -+ final Path regionPath = this.folder.resolve(getRegionFileName(this.info, chunkcoordintpair.x, chunkcoordintpair.z)); // DivineMC - - this.createRegionFile(key); - - FileUtil.createDirectoriesSafe(this.folder); - -- ret = new RegionFile(this.info, regionPath, this.folder, this.sync); -+ ret = space.bxteam.divinemc.region.AbstractRegionFileFactory.getAbstractRegionFile(this.info, regionPath, this.folder, this.sync); // DivineMC - - this.regionCache.putAndMoveToFirst(key, ret); - -@@ -180,7 +182,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - 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 DIVINEMC - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); // DivineMC - } - -- private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { -+ private static CompoundTag readOversizedChunk(space.bxteam.divinemc.region.AbstractRegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { // DivineMC - synchronized (regionfile) { - try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) { - CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z); -@@ -215,7 +217,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - @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); -+ space.bxteam.divinemc.region.AbstractRegionFile regionfile = this.getRegionFile(pos, true); // DivineMC - if (regionfile == null) { - return null; - } -@@ -279,7 +281,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - - 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); -+ space.bxteam.divinemc.region.AbstractRegionFile regionfile = this.getRegionFile(chunkPos, true); // DivineMC - if (regionfile == null) { - return; - } -@@ -309,7 +311,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - } - - public void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException { // Paper - public -- RegionFile regionfile = this.getRegionFile(pos, nbt == null); // CraftBukkit // Paper - rewrite chunk system -+ space.bxteam.divinemc.region.AbstractRegionFile regionfile = this.getRegionFile(pos, nbt == null); // CraftBukkit // Paper - rewrite chunk system // DivineMC - // Paper start - rewrite chunk system - if (regionfile == null) { - // if the RegionFile doesn't exist, no point in deleting from it -@@ -368,7 +370,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - // Paper start - rewrite chunk system - synchronized (this) { - final ExceptionCollector exceptionCollector = new ExceptionCollector<>(); -- for (final RegionFile regionFile : this.regionCache.values()) { -+ for (final space.bxteam.divinemc.region.AbstractRegionFile regionFile : this.regionCache.values()) { // DivineMC - try { - regionFile.close(); - } catch (final IOException ex) { -@@ -385,7 +387,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise - // Paper start - rewrite chunk system - synchronized (this) { - final ExceptionCollector exceptionCollector = new ExceptionCollector<>(); -- for (final RegionFile regionFile : this.regionCache.values()) { -+ for (final space.bxteam.divinemc.region.AbstractRegionFile regionFile : this.regionCache.values()) { // DivineMC - try { - regionFile.flush(); - } catch (final IOException ex) { -diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java -index 6111631c6673948b266286894603cc5e30451b02..8bfc0fe2b8389359a682458fde5ef1b0670f7a2d 100644 ---- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java -+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionStorageInfo.java -@@ -7,4 +7,20 @@ public record RegionStorageInfo(String level, ResourceKey dimension, Stri - public RegionStorageInfo withTypeSuffix(String suffix) { - return new RegionStorageInfo(this.level, this.dimension, this.type + suffix); - } -+ -+ // DivineMC start -+ public space.bxteam.divinemc.region.RegionFileFormat regionFormat() { -+ return ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(level)) -+ .getHandle() -+ .divinemcConfig -+ .regionFormat; -+ } -+ -+ public int linearCompressionLevel() { -+ return ((org.bukkit.craftbukkit.CraftWorld) org.bukkit.Bukkit.getWorld(level)) -+ .getHandle() -+ .divinemcConfig -+ .linearCompressionLevel; -+ } -+ // DivineMC end - } -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -index acf4a5287ba8d1ec8b4157426aa5794211654b7b..e8cbdcc5c41acfc18fa040997421fb712de801cb 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineConfig.java -@@ -194,4 +194,16 @@ public class DivineConfig { - private static void chatMessageSignatures() { - noChatSign = getBoolean("settings.no-chat-sign", noChatSign); - } -+ -+ public static int linearFlushFrequency = 10; -+ public static int linearFlushThreads = 1; -+ private static void linearSettings() { -+ linearFlushFrequency = getInt("settings.region-format.linear.flush-frequency", linearFlushFrequency); -+ linearFlushThreads = getInt("settings.region-format.linear.flush-max-threads", linearFlushThreads); -+ -+ if (linearFlushThreads < 0) -+ linearFlushThreads = Math.max(Runtime.getRuntime().availableProcessors() + linearFlushThreads, 1); -+ else -+ linearFlushThreads = Math.max(linearFlushThreads, 1); -+ } - } -diff --git a/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -index d94c51ea18d299dd52b9a8521a9cdc0d95b79356..f216f728dd5ca6e7b67f4f1384ce73faf9814384 100644 ---- a/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -+++ b/src/main/java/space/bxteam/divinemc/configuration/DivineWorldConfig.java -@@ -3,10 +3,12 @@ package space.bxteam.divinemc.configuration; - import org.apache.commons.lang.BooleanUtils; - import org.bukkit.World; - import org.bukkit.configuration.ConfigurationSection; -+import space.bxteam.divinemc.region.RegionFileFormat; - - import java.util.List; - import java.util.Map; - import java.util.function.Predicate; -+import java.util.logging.Level; - - import static space.bxteam.divinemc.configuration.DivineConfig.log; - -@@ -106,4 +108,22 @@ public class DivineWorldConfig { - private void suppressErrorsFromDirtyAttributes() { - suppressErrorsFromDirtyAttributes = getBoolean("suppress-errors-from-dirty-attributes", suppressErrorsFromDirtyAttributes); - } -+ -+ public RegionFileFormat regionFormat = RegionFileFormat.ANVIL; -+ public int linearCompressionLevel = 1; -+ private void regionFormatSettings() { -+ regionFormat = RegionFileFormat.fromString(getString("region-format.format", regionFormat.name())); -+ if (regionFormat.equals(RegionFileFormat.INVALID)) { -+ log(Level.SEVERE, "Unknown region format in linear.yml: " + regionFormat); -+ log(Level.SEVERE, "Falling back to ANVIL region file format."); -+ regionFormat = RegionFileFormat.ANVIL; -+ } -+ -+ linearCompressionLevel = getInt("region-format.linear.compression-level", linearCompressionLevel); -+ if (linearCompressionLevel > 23 || linearCompressionLevel < 1) { -+ log(Level.SEVERE, "Linear region compression level should be between 1 and 22 in linear.yml: " + linearCompressionLevel); -+ log(Level.SEVERE, "Falling back to compression level 1."); -+ linearCompressionLevel = 1; -+ } -+ } - } -diff --git a/src/main/java/space/bxteam/divinemc/region/AbstractRegionFile.java b/src/main/java/space/bxteam/divinemc/region/AbstractRegionFile.java -new file mode 100644 -index 0000000000000000000000000000000000000000..8bc4681e38ab29dae8173492d3430986d8baa164 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/region/AbstractRegionFile.java -@@ -0,0 +1,26 @@ -+package space.bxteam.divinemc.region; -+ -+import java.io.DataInputStream; -+import java.io.DataOutputStream; -+import java.io.IOException; -+import java.nio.file.Path; -+import net.minecraft.nbt.CompoundTag; -+import net.minecraft.world.level.ChunkPos; -+ -+public interface AbstractRegionFile { -+ -+ Path getPath(); -+ DataInputStream getChunkDataInputStream(ChunkPos pos) throws IOException; -+ DataOutputStream getChunkDataOutputStream(ChunkPos pos) throws IOException; -+ CompoundTag getOversizedData(int x, int z) throws IOException; -+ -+ boolean hasChunk(ChunkPos pos); -+ boolean doesChunkExist(ChunkPos pos); -+ boolean isOversized(int x, int z); -+ boolean recalculateHeader() throws IOException; -+ -+ void flush() throws IOException; -+ void close() throws IOException; -+ void clear(ChunkPos pos) throws IOException; -+ void setOversized(int x, int z, boolean oversized) throws IOException; -+} -diff --git a/src/main/java/space/bxteam/divinemc/region/AbstractRegionFileFactory.java b/src/main/java/space/bxteam/divinemc/region/AbstractRegionFileFactory.java -new file mode 100644 -index 0000000000000000000000000000000000000000..eb019b4863c2f812902318bdc8f3ab34ddb7aac5 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/region/AbstractRegionFileFactory.java -@@ -0,0 +1,22 @@ -+package space.bxteam.divinemc.region; -+ -+import java.io.IOException; -+import java.nio.file.Path; -+import net.minecraft.world.level.chunk.storage.RegionFile; -+import net.minecraft.world.level.chunk.storage.RegionFileVersion; -+import net.minecraft.world.level.chunk.storage.RegionStorageInfo; -+ -+public class AbstractRegionFileFactory { -+ -+ public static AbstractRegionFile getAbstractRegionFile(RegionStorageInfo storageKey, Path directory, Path path, boolean dsync) throws IOException { -+ return getAbstractRegionFile(storageKey, directory, path, RegionFileVersion.getCompressionFormat(), dsync); -+ } -+ -+ public static AbstractRegionFile getAbstractRegionFile(RegionStorageInfo storageKey, Path path, Path directory, RegionFileVersion compressionFormat, boolean dsync) throws IOException { -+ if (path.toString().endsWith(".linear")) { -+ return new LinearRegionFile(path, storageKey.linearCompressionLevel()); -+ } else { -+ return new RegionFile(storageKey, path, directory, compressionFormat, dsync); -+ } -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/region/LinearRegionFile.java b/src/main/java/space/bxteam/divinemc/region/LinearRegionFile.java -new file mode 100644 -index 0000000000000000000000000000000000000000..bcb69f307a63ae6aa2b1e8bdfbb1a499ad88d402 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/region/LinearRegionFile.java -@@ -0,0 +1,304 @@ -+package space.bxteam.divinemc.region; -+ -+import com.github.luben.zstd.ZstdInputStream; -+import com.github.luben.zstd.ZstdOutputStream; -+import com.mojang.logging.LogUtils; -+import java.io.BufferedOutputStream; -+import java.io.ByteArrayInputStream; -+import java.io.ByteArrayOutputStream; -+import java.io.DataInputStream; -+import java.io.DataOutputStream; -+import java.io.File; -+import java.io.FileInputStream; -+import java.io.FileOutputStream; -+import java.io.IOException; -+import java.io.InputStream; -+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 javax.annotation.Nullable; -+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 org.slf4j.Logger; -+ -+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 LZ4Compressor compressor; -+ private final LZ4FastDecompressor decompressor; -+ private final int compressionLevel; -+ private final 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; -+ } -+ } -+ } -+ } -+ } -+ -+ private static int getChunkIndex(int x, int z) { -+ return (x & 31) + ((z & 31) << 5); -+ } -+ -+ private static int getTimestamp() { -+ return (int) (System.currentTimeMillis() / 1000L); -+ } -+ -+ public Path getPath() { -+ return this.path; -+ } -+ -+ 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) { -+ return false; -+ } -+ -+ 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 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 ChunkBuffer(pos))); -+ } -+ -+ 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 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 -+ } -+ -+ 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; -+ } -+ -+ private class ChunkBuffer extends ByteArrayOutputStream { -+ private final ChunkPos pos; -+ -+ public ChunkBuffer(ChunkPos chunkcoordintpair) { -+ super(); -+ this.pos = chunkcoordintpair; -+ } -+ -+ public void close() { -+ ByteBuffer bytebuffer = ByteBuffer.wrap(this.buf, 0, this.count); -+ LinearRegionFile.this.write(this.pos, bytebuffer); -+ } -+ } -+} -diff --git a/src/main/java/space/bxteam/divinemc/region/LinearRegionFileFlusher.java b/src/main/java/space/bxteam/divinemc/region/LinearRegionFileFlusher.java -new file mode 100644 -index 0000000000000000000000000000000000000000..731dd3516a597e9cb97596bb7647db6c94363a9a ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/region/LinearRegionFileFlusher.java -@@ -0,0 +1,50 @@ -+package space.bxteam.divinemc.region; -+ -+import com.google.common.util.concurrent.ThreadFactoryBuilder; -+import java.util.Queue; -+import java.util.concurrent.ExecutorService; -+import java.util.concurrent.Executors; -+import java.util.concurrent.LinkedBlockingQueue; -+import java.util.concurrent.ScheduledExecutorService; -+import java.util.concurrent.TimeUnit; -+import org.bukkit.Bukkit; -+import space.bxteam.divinemc.configuration.DivineConfig; -+ -+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( -+ DivineConfig.linearFlushThreads, -+ new ThreadFactoryBuilder() -+ .setNameFormat("linear-flusher-%d") -+ .build() -+ ); -+ -+ public LinearRegionFileFlusher() { -+ Bukkit.getLogger().info("Using " + DivineConfig.linearFlushThreads + " threads for linear region flushing."); -+ scheduler.scheduleAtFixedRate(this::pollAndFlush, 0L, DivineConfig.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/space/bxteam/divinemc/region/RegionFileFormat.java b/src/main/java/space/bxteam/divinemc/region/RegionFileFormat.java -new file mode 100644 -index 0000000000000000000000000000000000000000..a799c9b2bb262b86be3872ba2a920ca3e8cb9d02 ---- /dev/null -+++ b/src/main/java/space/bxteam/divinemc/region/RegionFileFormat.java -@@ -0,0 +1,16 @@ -+package space.bxteam.divinemc.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/scripts/upstreamCommit.sh b/scripts/upstreamCommit.sh old mode 100644 new mode 100755 diff --git a/settings.gradle.kts b/settings.gradle.kts index bc6c6f3..880544d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,15 +2,39 @@ import java.util.Locale pluginManagement { repositories { - mavenLocal() gradlePluginPortal() - maven("https://papermc.io/repo/repository/maven-public/") + mavenLocal() + maven("https://repo.papermc.io/repository/maven-public/") } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" +} + +if (!file(".git").exists()) { + val errorText = """ + + =====================[ ERROR ]===================== + The DivineMC project directory is not a properly cloned Git repository. + + In order to build DivineMC from source you must clone + the DivineMC repository using Git, not download a code + zip from GitHub. + + Built Gale jars are available for download at + https://github.com/DivineMC/DivineMC/actions + + See https://github.com/PaperMC/Paper/blob/main/CONTRIBUTING.md + for further information on building and modifying Paper forks. + =================================================== + """.trimIndent() + error(errorText) +} + rootProject.name = "DivineMC" -for (name in listOf("DivineMC-API", "DivineMC-Server", "paper-api-generator")) { +for (name in listOf("divinemc-api", "divinemc-server")) { val projName = name.lowercase(Locale.ENGLISH) include(projName) findProject(":$projName")!!.projectDir = file(name)