diff --git a/patches/server/0002-Leaf-Config.patch b/patches/server/0002-Leaf-Config.patch index d1494b889..cb3697bb5 100644 --- a/patches/server/0002-Leaf-Config.patch +++ b/patches/server/0002-Leaf-Config.patch @@ -3,226 +3,361 @@ From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> Date: Wed, 12 Oct 2022 10:42:15 -0400 Subject: [PATCH] Leaf Config +TODO - Dreeam: Add header comment, world config, Bump Night Config to 3.7.1 + +Co-authored-by: MrHua269 diff --git a/build.gradle.kts b/build.gradle.kts -index 9bc4698a3d38ff3b6ae0d37a286ffc053f2d863f..d297578fe5990eb7ff55d995ec30525bf8776a52 100644 +index ac1d39ce2923716336c2ed31b9b7f1d93a9d5e30..812435d3912426865a651456de111aa76c53dc00 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -58,6 +58,13 @@ dependencies { - runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3") - runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3") +@@ -21,6 +21,9 @@ dependencies { + exclude("io.papermc.paper", "paper-api") + } + // Gale end - project setup ++ ++ implementation("com.electronwill.night-config:toml:3.6.7") // Leaf - Night config // Dreeam TODO: Bump to 3.7.1 ++ + // Paper start + implementation("org.jline:jline-terminal-jansi:3.21.0") + implementation("net.minecrell:terminalconsoleappender:1.3.0") +diff --git a/src/main/java/net/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java +index cb4a30bdf9beb4b1296ff47a6f6f299f0534b481..c963a18d289157cf60728d5f2e39b9a5674de68e 100644 +--- a/src/main/java/net/minecraft/server/Main.java ++++ b/src/main/java/net/minecraft/server/Main.java +@@ -138,6 +138,8 @@ public class Main { + Bootstrap.bootStrap(); + Bootstrap.validate(); + Util.startTimerHackThread(); ++ org.dreeam.leaf.config.LeafConfig.loadConfig(); // Leaf ++ org.dreeam.leaf.config.LeafConfig.setupLatch(); // Leaf + Path path1 = Paths.get("server.properties"); + DedicatedServerSettings dedicatedserversettings = new DedicatedServerSettings(optionset); // CraftBukkit - CLI argument support -+ // Pufferfish start -+ implementation("org.yaml:snakeyaml:2.1") -+ implementation("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") { -+ exclude(group = "org.yaml", module = "snakeyaml") +diff --git a/src/main/java/org/dreeam/leaf/config/ConfigInfo.java b/src/main/java/org/dreeam/leaf/config/ConfigInfo.java +new file mode 100644 +index 0000000000000000000000000000000000000000..6fbc0ad44069fbb3d85d3a6a4633e8e756196dfe +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/ConfigInfo.java +@@ -0,0 +1,11 @@ ++package org.dreeam.leaf.config; ++ ++import java.lang.annotation.Retention; ++import java.lang.annotation.RetentionPolicy; ++ ++@Retention(RetentionPolicy.RUNTIME) ++public @interface ConfigInfo { ++ String baseName(); ++ ++ String comments() default ""; ++} +diff --git a/src/main/java/org/dreeam/leaf/config/DoNotLoad.java b/src/main/java/org/dreeam/leaf/config/DoNotLoad.java +new file mode 100644 +index 0000000000000000000000000000000000000000..42ce82d388336906a91547b81f6f70766f2b10f0 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/DoNotLoad.java +@@ -0,0 +1,8 @@ ++package org.dreeam.leaf.config; ++ ++import java.lang.annotation.Retention; ++import java.lang.annotation.RetentionPolicy; ++ ++@Retention(RetentionPolicy.RUNTIME) ++public @interface DoNotLoad { ++} +diff --git a/src/main/java/org/dreeam/leaf/config/EnumConfigCategory.java b/src/main/java/org/dreeam/leaf/config/EnumConfigCategory.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f69663d0cfebcdb598ac808d06cb069572dd6052 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/EnumConfigCategory.java +@@ -0,0 +1,21 @@ ++package org.dreeam.leaf.config; ++ ++public enum EnumConfigCategory { ++ ++ ASYNC("async"), ++ PERFORMANCE("performance"), ++ NETWORK("network"), ++ FIXES("fixes"), ++ MISC("misc"), ++ GAMEPLAY("gameplay"); ++ ++ private final String baseKeyName; ++ ++ EnumConfigCategory(String baseKeyName) { ++ this.baseKeyName = baseKeyName; + } -+ // Pufferfish end -+ - testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test - testImplementation("junit:junit:4.13.2") - testImplementation("org.hamcrest:hamcrest-library:1.3") -diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java -index f749516e5d3c22e808638f3a7672aab837571b1a..61fc65624f7d9a3bfa399a58112efb7f55b31652 100644 ---- a/src/main/java/co/aikar/timings/TimingsExport.java -+++ b/src/main/java/co/aikar/timings/TimingsExport.java -@@ -303,8 +303,9 @@ public class TimingsExport extends Thread { - pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), - // Gale start - Gale configuration - include in timings - pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), -- pair("gale", mapAsJSON(Bukkit.spigot().getGaleConfig(), null)) -+ pair("gale", mapAsJSON(Bukkit.spigot().getGaleConfig(), null)), - // Gale end - Gale configuration - include in timings -+ pair("leaf", mapAsJSON(org.dreeam.leaf.LeafConfig.getConfigCopy(), null)) // Leaf - )); - - new TimingsExport(listeners, parent, history).start(); -diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index fcf8fe61e1c1cff550f86b83fca095a5513aa093..64bc644029dc9fa3cb802b58ecd8f197c3c4ad5b 100644 ---- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -232,6 +232,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface - SIMDDetection.initialize(); - } catch (Throwable ignored) {} - // Gale start - Pufferfish - SIMD support -+ org.dreeam.leaf.LeafConfig.load(); // Leaf - - this.setPvpAllowed(dedicatedserverproperties.pvp); - this.setFlightAllowed(dedicatedserverproperties.allowFlight); -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java ++ ++ public String getBaseKeyName() { ++ return this.baseKeyName; ++ } ++} +diff --git a/src/main/java/org/dreeam/leaf/config/HotReloadUnsupported.java b/src/main/java/org/dreeam/leaf/config/HotReloadUnsupported.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f7ab1ff5f298ff1e5e16fe5396d1e9e62a55fdfb +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/HotReloadUnsupported.java +@@ -0,0 +1,8 @@ ++package org.dreeam.leaf.config; ++ ++import java.lang.annotation.Retention; ++import java.lang.annotation.RetentionPolicy; ++ ++@Retention(RetentionPolicy.RUNTIME) ++public @interface HotReloadUnsupported { ++} +diff --git a/src/main/java/org/dreeam/leaf/config/IConfigModule.java b/src/main/java/org/dreeam/leaf/config/IConfigModule.java +new file mode 100644 +index 0000000000000000000000000000000000000000..da1474fa0512e50bedc8dda787bf6a1214f1ce37 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/IConfigModule.java +@@ -0,0 +1,23 @@ ++package org.dreeam.leaf.config; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import org.jetbrains.annotations.NotNull; ++ ++public interface IConfigModule { ++ ++ EnumConfigCategory getCategory(); ++ ++ String getBaseName(); ++ ++ default void onLoaded(CommentedFileConfig configInstance) { ++ } ++ ++ default T get(String keyName, T defaultValue, @NotNull CommentedFileConfig config) { ++ if (!config.contains(keyName)) { ++ config.set(keyName, defaultValue); ++ return defaultValue; ++ } ++ ++ return config.get(keyName); ++ } ++} +diff --git a/src/main/java/org/dreeam/leaf/config/LeafConfig.java b/src/main/java/org/dreeam/leaf/config/LeafConfig.java new file mode 100644 -index 0000000000000000000000000000000000000000..d924db6378b2c60c7b96cf5c45a6d50a091f9790 +index 0000000000000000000000000000000000000000..b20ad36bb89e5226d94928832fdbcce7e096d6e2 --- /dev/null -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -0,0 +1,171 @@ -+package org.dreeam.leaf; -+ -+import com.google.common.collect.ImmutableMap; -+import net.minecraft.server.MinecraftServer; -+import org.bukkit.configuration.ConfigurationSection; -+import org.bukkit.configuration.MemoryConfiguration; -+import org.jetbrains.annotations.Nullable; -+import org.simpleyaml.configuration.comments.CommentType; -+import org.simpleyaml.configuration.file.YamlFile; -+import org.simpleyaml.exceptions.InvalidConfigurationException; ++++ b/src/main/java/org/dreeam/leaf/config/LeafConfig.java +@@ -0,0 +1,220 @@ ++package org.dreeam.leaf.config; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import org.jetbrains.annotations.Contract; ++import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; -+import java.lang.reflect.Method; ++import java.lang.reflect.Field; ++import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; -+import java.util.List; -+import java.util.Map; ++import java.net.JarURLConnection; ++import java.net.URL; ++import java.net.URLDecoder; ++import java.nio.charset.StandardCharsets; ++import java.util.*; ++import java.util.concurrent.CompletableFuture; ++import java.util.jar.JarEntry; ++import java.util.jar.JarFile; ++ ++import org.apache.logging.log4j.LogManager; ++import org.apache.logging.log4j.Logger; ++import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; + +public class LeafConfig { ++ public static final Logger LOGGER = LogManager.getLogger(LeafConfig.class.getName()); ++ private static final File baseConfigFolder = new File("leaf_config"); ++ private static final File baseConfigFile = new File(baseConfigFolder, "leaf_global_config.toml"); ++ private static final Set allInstanced = new HashSet<>(); ++ private static CommentedFileConfig configFileInstance; ++ public static boolean alreadyInited = false; ++ private static MinecraftInternalPlugin NULL_PLUGIN = new MinecraftInternalPlugin(); ++ ++ public static void setupLatch() { ++ alreadyInited = true; ++ } + -+ private static final YamlFile config = new YamlFile(); -+ private static int updates = 0; -+ -+ private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) { -+ ConfigurationSection newSection = new MemoryConfiguration(); -+ for (String key : section.getKeys(false)) { -+ if (section.isConfigurationSection(key)) { -+ newSection.set(key, convertToBukkit(section.getConfigurationSection(key))); -+ } else { -+ newSection.set(key, section.get(key)); -+ } ++ public static void reload() { ++ dropAllInstanced(); ++ try { ++ loadConfig(); ++ } catch (Exception e) { ++ LOGGER.error(e); + } -+ return newSection; + } + -+ public static ConfigurationSection getConfigCopy() { -+ return convertToBukkit(config); ++ @Contract(" -> new") ++ public static @NotNull CompletableFuture reloadAsync() { ++ return new CompletableFuture<>(); + } + -+ public static int getUpdates() { -+ return updates; ++ public static void dropAllInstanced() { ++ allInstanced.clear(); + } + -+ public static void load() throws IOException { -+ File configFile = new File("leaf.yml"); ++ public static void loadConfig() throws IOException { ++ baseConfigFolder.mkdirs(); + -+ if (configFile.exists()) { -+ try { -+ config.load(configFile); -+ } catch (InvalidConfigurationException e) { -+ throw new IOException(e); -+ } ++ if (!baseConfigFile.exists()) { ++ baseConfigFile.createNewFile(); + } + -+ getString("info.version", "1.2"); -+ setComment("info", -+ "Leaf Config", -+ "Github Repo: https://github.com/Winds-Studio/Leaf", -+ "Discord: dreeam___ | QQ: 2682173972"); -+ -+ for (Method method : LeafConfig.class.getDeclaredMethods()) { -+ if (Modifier.isStatic(method.getModifiers()) && Modifier.isPrivate(method.getModifiers()) && method.getParameterCount() == 0 && -+ method.getReturnType() == Void.TYPE && !method.getName().startsWith("lambda")) { -+ method.setAccessible(true); -+ try { -+ method.invoke(null); -+ } catch (Throwable t) { -+ MinecraftServer.LOGGER.warn("Failed to load configuration option from " + method.getName(), t); -+ } -+ } -+ } ++ configFileInstance = CommentedFileConfig.ofConcurrent(baseConfigFile); + -+ updates++; ++ configFileInstance.load(); ++ configFileInstance.setComment("", """ ++ Leaf Config ++ Github Repo: https://github.com/Winds-Studio/Leaf ++ Discord: dreeam___ | QQ: 2682173972"""); // Leaf TODO - need to fix + -+ config.save(configFile); ++ try { ++ instanceAllModule(); ++ loadAllModules(); ++ } catch (Exception e) { ++ LOGGER.error("Failed to load config modules!", e); ++ } + ++ configFileInstance.save(); + } + -+ private static void setComment(String key, String... comment) { -+ if (config.contains(key)) { -+ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); ++ private static void loadAllModules() throws IllegalAccessException { ++ for (IConfigModule instanced : allInstanced) { ++ loadForSingle(instanced); + } + } + -+ private static void ensureDefault(String key, Object defaultValue, String... comment) { -+ if (!config.contains(key)) { -+ config.set(key, defaultValue); -+ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); ++ private static void instanceAllModule() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { ++ for (Class clazz : getClasses("org.dreeam.leaf.config.modules")) { ++ if (IConfigModule.class.isAssignableFrom(clazz)) { ++ allInstanced.add((IConfigModule) clazz.getConstructor().newInstance()); ++ } + } + } + -+ private static void set(String key, Object defaultValue) { -+ config.addDefault(key, defaultValue); -+ config.set(key, defaultValue); -+ } ++ private static void loadForSingle(@NotNull IConfigModule singleConfigModule) throws IllegalAccessException { ++ final EnumConfigCategory category = singleConfigModule.getCategory(); + -+ private static boolean getBoolean(String key, boolean defaultValue, String... comment) { -+ return getBoolean(key, null, defaultValue, comment); -+ } ++ Field[] fields = singleConfigModule.getClass().getDeclaredFields(); + -+ private static boolean getBoolean(String key, @Nullable String oldKey, boolean defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getBoolean(key, defaultValue); -+ } ++ for (Field field : fields) { ++ int modifiers = field.getModifiers(); ++ if (Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) { ++ boolean skipLoad = field.getAnnotation(DoNotLoad.class) != null || (alreadyInited && field.getAnnotation(HotReloadUnsupported.class) != null); ++ ConfigInfo configInfo = field.getAnnotation(ConfigInfo.class); + -+ private static int getInt(String key, int defaultValue, String... comment) { -+ return getInt(key, null, defaultValue, comment); -+ } ++ if (skipLoad || configInfo == null) { ++ continue; ++ } + -+ private static int getInt(String key, @Nullable String oldKey, int defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getInt(key, defaultValue); -+ } ++ final String fullConfigKeyName = category.getBaseKeyName() + "." + singleConfigModule.getBaseName() + "." + configInfo.baseName(); + -+ private static double getDouble(String key, double defaultValue, String... comment) { -+ return getDouble(key, null, defaultValue, comment); -+ } ++ field.setAccessible(true); ++ final Object currentValue = field.get(null); + -+ private static double getDouble(String key, @Nullable String oldKey, double defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getDouble(key, defaultValue); -+ } ++ if (!configFileInstance.contains(fullConfigKeyName)) { ++ if (currentValue == null) { ++ throw new UnsupportedOperationException("Config " + singleConfigModule.getBaseName() + "tried to add an null default value!"); ++ } + -+ private static String getString(String key, String defaultValue, String... comment) { -+ return getOldString(key, null, defaultValue, comment); -+ } ++ final String comments = configInfo.comments(); + -+ private static String getOldString(String key, @Nullable String oldKey, String defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getString(key, defaultValue); -+ } ++ if (!comments.isBlank()) { ++ configFileInstance.setComment(fullConfigKeyName, comments); ++ } + -+ private static List getStringList(String key, List defaultValue, String... comment) { -+ return getStringList(key, null, defaultValue, comment); -+ } ++ configFileInstance.add(fullConfigKeyName, currentValue); ++ continue; ++ } + -+ private static List getStringList(String key, @Nullable String oldKey, List defaultValue, String... comment) { -+ ensureDefault(key, defaultValue, comment); -+ return config.getStringList(key); ++ final Object actuallyValue = configFileInstance.get(fullConfigKeyName); ++ field.set(null, actuallyValue); ++ } ++ } ++ ++ singleConfigModule.onLoaded(configFileInstance); + } + -+ static Map getMap(String key, Map defaultValue) { -+ if (defaultValue != null && getConfigCopy().getConfigurationSection(key) == null) { -+ config.addDefault(key, defaultValue); -+ return defaultValue; ++ public static @NotNull Set> getClasses(String pack) { ++ Set> classes = new LinkedHashSet<>(); ++ String packageDirName = pack.replace('.', '/'); ++ Enumeration dirs; ++ ++ try { ++ dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); ++ while (dirs.hasMoreElements()) { ++ URL url = dirs.nextElement(); ++ String protocol = url.getProtocol(); ++ if ("file".equals(protocol)) { ++ String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8); ++ findClassesInPackageByFile(pack, filePath, classes); ++ } else if ("jar".equals(protocol)) { ++ JarFile jar; ++ try { ++ jar = ((JarURLConnection) url.openConnection()).getJarFile(); ++ Enumeration entries = jar.entries(); ++ findClassesInPackageByJar(pack, entries, packageDirName, classes); ++ } catch (IOException e) { ++ throw new RuntimeException(e); ++ } ++ } ++ } ++ } catch (IOException e) { ++ throw new RuntimeException(e); + } -+ return toMap(getConfigCopy().getConfigurationSection(key)); ++ ++ return classes; + } + -+ 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); ++ private static void findClassesInPackageByFile(String packageName, String packagePath, Set> classes) { ++ File dir = new File(packagePath); ++ ++ if (!dir.exists() || !dir.isDirectory()) { ++ return; ++ } ++ ++ File[] dirfiles = dir.listFiles((file) -> file.isDirectory() || file.getName().endsWith(".class")); ++ if (dirfiles != null) { ++ for (File file : dirfiles) { ++ if (file.isDirectory()) { ++ findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), classes); ++ } else { ++ String className = file.getName().substring(0, file.getName().length() - 6); ++ try { ++ classes.add(Class.forName(packageName + '.' + className)); ++ } catch (ClassNotFoundException e) { ++ throw new RuntimeException(e); ++ } + } + } + } -+ return builder.build(); + } + -+ private static void removal() { -+ } ++ private static void findClassesInPackageByJar(String packageName, Enumeration entries, String packageDirName, Set> classes) { ++ while (entries.hasMoreElements()) { ++ JarEntry entry = entries.nextElement(); ++ String name = entry.getName(); + -+ private static void performance() { -+ } ++ if (name.charAt(0) == '/') { ++ name = name.substring(1); ++ } ++ ++ if (name.startsWith(packageDirName)) { ++ int idx = name.lastIndexOf('/'); + -+ private static void network() { ++ if (idx != -1) { ++ packageName = name.substring(0, idx).replace('/', '.'); ++ } ++ ++ if (name.endsWith(".class") && !entry.isDirectory()) { ++ String className = name.substring(packageName.length() + 1, name.length() - 6); ++ try { ++ classes.add(Class.forName(packageName + '.' + className)); ++ } catch (ClassNotFoundException e) { ++ throw new RuntimeException(e); ++ } ++ } ++ } ++ } + } +} diff --git a/patches/server/0003-Leaf-Config-legacy-converter.patch b/patches/server/0003-Leaf-Config-legacy-converter.patch new file mode 100644 index 000000000..ba9e43d1a --- /dev/null +++ b/patches/server/0003-Leaf-Config-legacy-converter.patch @@ -0,0 +1,250 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Tue, 27 Feb 2024 19:42:03 -0500 +Subject: [PATCH] Leaf Config legacy converter + + +diff --git a/build.gradle.kts b/build.gradle.kts +index 812435d3912426865a651456de111aa76c53dc00..9f31e013809ab490442c0f6f629de7de9f89cfb4 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -24,6 +24,13 @@ dependencies { + + implementation("com.electronwill.night-config:toml:3.6.7") // Leaf - Night config // Dreeam TODO: Bump to 3.7.1 + ++ // Leaf start - Legacy config ++ implementation("org.yaml:snakeyaml:2.2") ++ implementation("com.github.Carleslc.Simple-YAML:Simple-Yaml:1.8.4") { ++ exclude(group = "org.yaml", module = "snakeyaml") ++ } ++ // Leaf end - Legacy config ++ + // Paper start + implementation("org.jline:jline-terminal-jansi:3.21.0") + implementation("net.minecrell:terminalconsoleappender:1.3.0") +diff --git a/src/main/java/org/dreeam/leaf/config/LeafConfig.java b/src/main/java/org/dreeam/leaf/config/LeafConfig.java +index b20ad36bb89e5226d94928832fdbcce7e096d6e2..c8da60bfb123079e85dd2f4b1f8925513e018297 100644 +--- a/src/main/java/org/dreeam/leaf/config/LeafConfig.java ++++ b/src/main/java/org/dreeam/leaf/config/LeafConfig.java +@@ -24,7 +24,9 @@ import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin; + + public class LeafConfig { + public static final Logger LOGGER = LogManager.getLogger(LeafConfig.class.getName()); +- private static final File baseConfigFolder = new File("leaf_config"); ++ public static long beginTime; // Leaf legacy config convert timer ++ private static final File legacyConfig = new File("leaf.yml"); // Leaf legacy config ++ public static final File baseConfigFolder = new File("leaf_config"); + private static final File baseConfigFile = new File(baseConfigFolder, "leaf_global_config.toml"); + private static final Set allInstanced = new HashSet<>(); + private static CommentedFileConfig configFileInstance; +@@ -76,6 +78,7 @@ public class LeafConfig { + } + + configFileInstance.save(); ++ upgradeLeagcyConfig(); // Upgrade legacy leaf config + } + + private static void loadAllModules() throws IllegalAccessException { +@@ -217,4 +220,21 @@ public class LeafConfig { + } + } + } ++ ++ private static void upgradeLeagcyConfig() { ++ if (legacyConfig.exists()) { ++ beginTime = System.nanoTime(); ++ LOGGER.info("Detected legacy config file!"); ++ ++ try { ++ Class clazz = Class.forName("org.dreeam.leaf.config.legacy.upgrader.V1ToV200"); ++ if (IConfigModule.class.isAssignableFrom(clazz)) { ++ loadForSingle((IConfigModule) clazz.getConstructor().newInstance()); ++ } ++ } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | ++ InstantiationException | IllegalAccessException e) { ++ LOGGER.error("Error in v1.x to v2.0.0 config upgrading process, this should not happen!", e); ++ } ++ } ++ } + } +diff --git a/src/main/java/org/dreeam/leaf/config/legacy/upgrader/V1ToV200.java b/src/main/java/org/dreeam/leaf/config/legacy/upgrader/V1ToV200.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8ead848b0b65af3cfd2e4c7715751f80e89be188 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/legacy/upgrader/V1ToV200.java +@@ -0,0 +1,175 @@ ++package org.dreeam.leaf.config.legacy.upgrader; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++import org.dreeam.leaf.config.LeafConfig; ++import org.simpleyaml.configuration.file.YamlFile; ++ ++import java.io.File; ++import java.io.IOException; ++import java.nio.file.Files; ++import java.nio.file.Path; ++import java.nio.file.StandardCopyOption; ++import java.util.Map; ++import java.util.concurrent.ConcurrentHashMap; ++ ++public class V1ToV200 implements IConfigModule { ++ ++ private static final YamlFile legacyConfig = new YamlFile(); ++ private static final File legacyConfigFile = new File("leaf.yml"); ++ ++ private static Object version, serverModName, serverGuiName, removeMojangUsernameCheck, removeSpigotCheckBungeeConfig, removeUseItemOnPacketTooFar, ++ maxUseItemDistance, disableMovedWronglyThreshold, enableAsyncMobSpawning, dabEnabled, startDistance, maximumActivationPrio, activationDistanceMod, ++ blackedEntities, throttleInactiveGoalSelectorTick, useSpigotItemMergingMechanism, optimizedPoweredRails, asyncPathfinding, asyncPathfindingMaxThreads, ++ asyncPathfindingKeepalive, asyncEntityTracker, asyncEntityTrackerMaxThreads, asyncEntityTrackerKeepalive, cacheMinecartCollision, ++ skipMapItemDataUpdatesIfMapDoesNotHaveCraftMapRenderer, jadeProtocol, appleskinProtocol, xaeroMapProtocol, xaeroMapServerID, syncmaticaProtocol, syncmaticaQuota, ++ syncmaticaQuotaLimit, sentryDsn, useVanillaEndTeleport, snowballCanKnockback, eggCanKnockback, fixTripwireDupe, including5sIngetTPS; ++ private static final Map ttls = new ConcurrentHashMap<>(); ++ ++ private static void initKeys() { ++ version = getKey("info.version"); ++ serverModName = getKey("server-mod-name"); ++ serverGuiName = getKey("server-Gui-name"); ++ removeMojangUsernameCheck = getKey("remove-Mojang-username-check"); ++ removeSpigotCheckBungeeConfig = getKey("remove-Spigot-check-bungee-config"); ++ removeUseItemOnPacketTooFar = getKey("remove-UseItemOnPacket-too-far-check"); ++ maxUseItemDistance = getKey("max-UseItem-distance"); ++ disableMovedWronglyThreshold = getKey("disable-MovedWronglyThreshold"); ++ enableAsyncMobSpawning = getKey("performance.enable-async-mob-spawning"); ++ dabEnabled = getKey("performance.dab.enabled", "dab.enabled"); ++ startDistance = getKey("performance.dab.start-distance", "dab.start-distance"); ++ maximumActivationPrio = getKey("performance.dab.max-tick-freq", "dab.max-tick-freq"); ++ activationDistanceMod = getKey("performance.dab.activation-dist-mod", "dab.activation-dist-mod"); ++ blackedEntities = getKey("performance.dab.blacklisted-entities", "dab.blacklisted-entities"); ++ throttleInactiveGoalSelectorTick = getKey("performance.inactive-goal-selector-throttle", "inactive-goal-selector-throttle"); ++ legacyConfig.getMapValues(true).forEach((key, ttl) -> { ++ if (key.startsWith("performance.entity_timeouts.")) { ++ String e = key.replaceAll("performance.entity_timeouts.", ""); ++ ttls.putIfAbsent(e, ttl); ++ } ++ }); ++ useSpigotItemMergingMechanism = getKey("performance.use-spigot-item-merging-mechanism"); ++ optimizedPoweredRails = getKey("performance.optimizedPoweredRails"); ++ asyncPathfinding = getKey("performance.async-pathfinding.enable"); ++ asyncPathfindingMaxThreads = getKey("performance.async-pathfinding.max-threads"); ++ asyncPathfindingKeepalive = getKey("performance.async-pathfinding.keepalive"); ++ asyncEntityTracker = getKey("performance.async-entity-tracker.enable"); ++ asyncEntityTrackerMaxThreads = getKey("performance.async-entity-tracker.max-threads"); ++ asyncEntityTrackerKeepalive = getKey("performance.async-entity-tracker.keepalive"); ++ cacheMinecartCollision = getKey("performance.cache-minecart-collision"); ++ skipMapItemDataUpdatesIfMapDoesNotHaveCraftMapRenderer = getKey("performance.skip-map-item-data-updates-if-map-does-not-have-craftmaprenderer"); ++ jadeProtocol = getKey("network.protocol.jade-protocol"); ++ appleskinProtocol = getKey("network.protocol.appleskin-protocol"); ++ xaeroMapProtocol = getKey("network.protocol.xaero-map-protocol"); ++ xaeroMapServerID = getKey("network.protocol.xaero-map-server-id"); ++ syncmaticaProtocol = getKey("network.protocol.syncmatica.enable"); ++ syncmaticaQuota = getKey("network.protocol.syncmatica.quota"); ++ syncmaticaQuotaLimit = getKey("network.protocol.syncmatica.quota-limit"); ++ sentryDsn = getKey("sentry-dsn", "performance.sentry-dsn"); ++ useVanillaEndTeleport = getKey("use-vanilla-end-teleport"); ++ snowballCanKnockback = getKey("playerKnockback.snowball-knockback-players"); ++ eggCanKnockback = getKey("playerKnockback.egg-knockback-players"); ++ fixTripwireDupe = getKey("gameplay.fix-tripwire-dupe"); ++ including5sIngetTPS = getKey("including-5s-in-getTPS"); ++ } ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.MISC; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "converter"; ++ } ++ ++ @Override ++ public void onLoaded(CommentedFileConfig config) { ++ loadLegacyConfig(); ++ ++ LeafConfig.LOGGER.info("Converting config from v{} to v2.0.0 format...", version); ++ ++ updateConfig("misc.rebrand.server-mod-name", serverModName, config); ++ updateConfig("misc.rebrand.server-gui-name", serverGuiName, config); ++ updateConfig("misc.remove_vanilla_username_check.enabled", removeMojangUsernameCheck, config); ++ updateConfig("misc.remove_spigot_check_bungee_config.enabled", removeSpigotCheckBungeeConfig, config); ++ updateConfig("misc.configurable_max_use_item_distance.remove-max-distance-check", removeUseItemOnPacketTooFar, config); ++ updateConfig("gameplay.configurable_max_use_item_distance.max-use-item-distance", maxUseItemDistance, config); ++ updateConfig("gameplay.disable_moved_wrongly_threshold.enabled", disableMovedWronglyThreshold, config); ++ updateConfig("async.async_mob_spawning.enabled", enableAsyncMobSpawning, config); ++ updateConfig("performance.dab.enabled", dabEnabled, config); ++ updateConfig("performance.dab.start-distance", startDistance, config); ++ updateConfig("performance.dab.max-tick-freq", maximumActivationPrio, config); ++ updateConfig("performance.dab.activation-dist-mod", activationDistanceMod, config); ++ updateConfig("performance.dab.blacklisted-entities", blackedEntities, config); ++ updateConfig("performance.inactive_goal_selector_throttle.enabled", throttleInactiveGoalSelectorTick, config); ++ ttls.forEach((e, ttl) -> updateConfig("performance.entity_timeouts." + e, ttl, config)); ++ updateConfig("gameplay.use-spigot-item-merging-mechanism.enabled", useSpigotItemMergingMechanism, config); ++ updateConfig("performance.optimized_powered_rails.enabled", optimizedPoweredRails, config); ++ updateConfig("async.async_pathfinding.enabled", asyncPathfinding, config); ++ updateConfig("async.async_pathfinding.max-threads", asyncPathfindingMaxThreads, config); ++ updateConfig("async.async_pathfinding.keepalive", asyncPathfindingKeepalive, config); ++ updateConfig("async.async_entity_tracker.enabled", asyncEntityTracker, config); ++ updateConfig("async.async_entity_tracker.max-threads", asyncEntityTrackerMaxThreads, config); ++ updateConfig("async.async_entity_tracker.keepalive", asyncEntityTrackerKeepalive, config); ++ updateConfig("performance.cache_minecart_collision.enabled", cacheMinecartCollision, config); ++ updateConfig("performance.skip_map_item_data_updates_if_map_does_not_have_craftmaprenderer.enabled", skipMapItemDataUpdatesIfMapDoesNotHaveCraftMapRenderer, config); ++ updateConfig("network.protocol_support.jade-protocol", jadeProtocol, config); ++ updateConfig("network.protocol_support.appleskin-protocol", appleskinProtocol, config); ++ updateConfig("network.protocol_support.xaero-map-protocol", xaeroMapProtocol, config); ++ updateConfig("network.protocol_support.xaero-map-server-id", xaeroMapServerID, config); ++ updateConfig("network.protocol_support.syncmatica-enabled", syncmaticaProtocol, config); ++ updateConfig("network.protocol_support.syncmatica-quota", syncmaticaQuota, config); ++ updateConfig("network.protocol_support.syncmatica-quota-limit", syncmaticaQuotaLimit, config); ++ updateConfig("misc.sentry_dsn.sentry-dsn", sentryDsn, config); ++ updateConfig("gameplay.use_vanilla_end_teleport.enabled", useVanillaEndTeleport, config); ++ updateConfig("gameplay.knockback.snowball-knockback-players", snowballCanKnockback, config); ++ updateConfig("gameplay.knockback.egg-knockback-players", eggCanKnockback, config); ++ updateConfig("fixes.fix_tripwire_dupe.enabled", fixTripwireDupe, config); ++ updateConfig("misc.including_5s_in_get_tps.enabled", including5sIngetTPS, config); ++ ++ config.save(); ++ ++ File backupFolder = new File(LeafConfig.baseConfigFolder + "/legacy_backup"); ++ ++ if (!backupFolder.exists()) { ++ backupFolder.mkdirs(); ++ } ++ ++ try { ++ Files.move(Path.of(legacyConfigFile.getPath()), Path.of(LeafConfig.baseConfigFolder + "/legacy_backup/leaf_v" + version + "_backup.yml"), StandardCopyOption.REPLACE_EXISTING); ++ } catch (IOException e) { ++ LeafConfig.LOGGER.error("Config upgraded failed!"); ++ return; ++ } ++ ++ LeafConfig.LOGGER.info("Config upgraded successfully!, elapsed {}ms.", (System.nanoTime() - LeafConfig.beginTime) / 1000000); ++ } ++ ++ private static void loadLegacyConfig() { ++ try { ++ legacyConfig.load(legacyConfigFile); ++ initKeys(); ++ } catch (IOException e) { ++ LeafConfig.LOGGER.error("Failed to load legacy config! Config upgraded failed!"); ++ } ++ } ++ ++ private static Object getKey(String key) { ++ return legacyConfig.contains(key) ? legacyConfig.get(key) : null; ++ } ++ ++ // In v1.x config, the old key still exists after converting to new key ++ // So check the new key whether exists first, then check the old key. ++ private static Object getKey(String key, String oldKey) { ++ return legacyConfig.contains(key) ? legacyConfig.get(key) : ++ legacyConfig.contains(oldKey) ? legacyConfig.get(oldKey) : ++ null; ++ } ++ ++ // TOML doesn't allow null key value, so do a null check. ++ private static void updateConfig(String key, Object value, CommentedFileConfig config) { ++ if (value != null) config.set(key, value); ++ } ++} diff --git a/patches/server/0003-Pufferfish-Utils.patch b/patches/server/0004-Pufferfish-Utils.patch similarity index 100% rename from patches/server/0003-Pufferfish-Utils.patch rename to patches/server/0004-Pufferfish-Utils.patch diff --git a/patches/server/0004-Pufferfish-Sentry.patch b/patches/server/0005-Pufferfish-Sentry.patch similarity index 84% rename from patches/server/0004-Pufferfish-Sentry.patch rename to patches/server/0005-Pufferfish-Sentry.patch index ddfde6dc0..f74800c0e 100644 --- a/patches/server/0004-Pufferfish-Sentry.patch +++ b/patches/server/0005-Pufferfish-Sentry.patch @@ -192,22 +192,45 @@ index 0000000000000000000000000000000000000000..ab7a4acec52320c30623023c5fc9423e + } + +} -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index d924db6378b2c60c7b96cf5c45a6d50a091f9790..b1fcd3588c22143f9852805a6cea3b6cf6c33a1f 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -168,4 +168,14 @@ public class LeafConfig { - - private static void network() { - } +diff --git a/src/main/java/org/dreeam/leaf/config/modules/misc/SentryDSN.java b/src/main/java/org/dreeam/leaf/config/modules/misc/SentryDSN.java +new file mode 100644 +index 0000000000000000000000000000000000000000..286af0f0e1d20d39e514e261dc8c0abd03284c5c +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/misc/SentryDSN.java +@@ -0,0 +1,36 @@ ++package org.dreeam.leaf.config.modules.misc; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import org.dreeam.leaf.config.ConfigInfo; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ ++public class SentryDSN implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.MISC; ++ } + ++ @Override ++ public String getBaseName() { ++ return "sentry_dsn"; ++ } ++ ++ @ConfigInfo(baseName = "sentry-dsn") + public static String sentryDsn = ""; -+ private static void sentryDsn() { ++ ++ @Override ++ public void onLoaded(CommentedFileConfig config) { ++ config.setComment("misc.sentry_dsn", """ ++ Sentry DSN for improved error logging, leave blank to disable, ++ Obtain from https://sentry.io/welcome/ ++ """); ++ + String sentryEnvironment = System.getenv("SENTRY_DSN"); -+ String sentryConfig = getString("sentry-dsn", sentryDsn, "Sentry DSN for improved error logging, leave blank to disable", "Obtain from https://sentry.io/welcome/"); -+ sentryDsn = sentryEnvironment == null ? sentryConfig : sentryEnvironment; ++ sentryDsn = sentryEnvironment == null ? sentryDsn : sentryEnvironment; + if (sentryDsn != null && !sentryDsn.isBlank()) { + gg.pufferfish.pufferfish.sentry.SentryManager.init(); + } + } - } ++} diff --git a/patches/server/0005-Pufferfish-Optimize-mob-spawning.patch b/patches/server/0006-Pufferfish-Optimize-mob-spawning.patch similarity index 86% rename from patches/server/0005-Pufferfish-Optimize-mob-spawning.patch rename to patches/server/0006-Pufferfish-Optimize-mob-spawning.patch index 286525129..d85b4a3db 100644 --- a/patches/server/0005-Pufferfish-Optimize-mob-spawning.patch +++ b/patches/server/0006-Pufferfish-Optimize-mob-spawning.patch @@ -32,10 +32,10 @@ index 3bee751a36c1aaab9b9a738edac80b4d2853bdd7..31b1b1c6a55b04cf1952ddb8ea08da98 AtomicReference atomicreference = new AtomicReference(); Thread thread = new io.papermc.paper.util.TickThread(() -> { // Paper - rewrite chunk system diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index b94972cfae11a955af14c382d7d679c520bfab13..b66e708b788800ba97c25104770904ffdc460fcc 100644 +index 73a492e629677b045914cf41b826ee1acb4497b7..6fa06ba5140ab135a5cfe8d6bde9ef6514ca69fa 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -@@ -350,6 +350,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface +@@ -349,6 +349,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface DedicatedServer.LOGGER.info("JMX monitoring enabled"); } @@ -44,7 +44,7 @@ index b94972cfae11a955af14c382d7d679c520bfab13..b66e708b788800ba97c25104770904ff } } diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index be0210aef3805237adb0142d3eb8b4a76a3270e5..bdaf3a157b9f9d8158d8112b0986c80f73b161a7 100644 +index 9feda080c522e3374024eabe4f9eebe9b16b701f..d9828fd85d39116867bdb7e4fbe899bae9fc0f2d 100644 --- a/src/main/java/net/minecraft/server/level/ChunkMap.java +++ b/src/main/java/net/minecraft/server/level/ChunkMap.java @@ -342,7 +342,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider @@ -166,28 +166,50 @@ index 4cdfc433df67afcd455422e9baf56f167dd712ae..57fcf3910f45ce371ac2e237b277b103 private void ensureActiveIsNotIterated() { // Paper - replace with better logic, do not delay removals -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index b1fcd3588c22143f9852805a6cea3b6cf6c33a1f..5d161351e7517acf57e98203bab8c9f9ab9d4005 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -163,7 +163,20 @@ public class LeafConfig { - private static void removal() { - } - -+ public static boolean enableAsyncMobSpawning = true; +diff --git a/src/main/java/org/dreeam/leaf/config/modules/async/AsyncMobSpawning.java b/src/main/java/org/dreeam/leaf/config/modules/async/AsyncMobSpawning.java +new file mode 100644 +index 0000000000000000000000000000000000000000..67c575439f7d60046586972dfc3212bb36a3d033 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/async/AsyncMobSpawning.java +@@ -0,0 +1,41 @@ ++package org.dreeam.leaf.config.modules.async; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import org.dreeam.leaf.config.ConfigInfo; ++import org.dreeam.leaf.config.DoNotLoad; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ ++public class AsyncMobSpawning implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.ASYNC; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "async_mob_spawning"; ++ } ++ ++ @ConfigInfo(baseName = "enabled") ++ public static boolean enabled = true; ++ @DoNotLoad + public static boolean asyncMobSpawningInitialized; - private static void performance() { -+ boolean asyncMobSpawning = getBoolean("performance.enable-async-mob-spawning", enableAsyncMobSpawning, -+ "Whether or not asynchronous mob spawning should be enabled.", -+ "On servers with many entities, this can improve performance by up to 15%. You must have", -+ "paper's per-player-mob-spawns setting set to true for this to work.", -+ "One quick note - this does not actually spawn mobs async (that would be very unsafe).", -+ "This just offloads some expensive calculations that are required for mob spawning."); ++ ++ @Override ++ public void onLoaded(CommentedFileConfig config) { ++ config.setComment("async.async_mob_spawning", """ ++ Whether or not asynchronous mob spawning should be enabled. ++ On servers with many entities, this can improve performance by up to 15%. You must have ++ paper's per-player-mob-spawns setting set to true for this to work. ++ One quick note - this does not actually spawn mobs async (that would be very unsafe). ++ This just offloads some expensive calculations that are required for mob spawning."""); ++ + // This prevents us from changing the value during a reload. + if (!asyncMobSpawningInitialized) { + asyncMobSpawningInitialized = true; -+ enableAsyncMobSpawning = asyncMobSpawning; ++ this.get("async.async_mob_spawning.enabled", enabled, config); + } - } - - private static void network() { ++ } ++} diff --git a/patches/server/0006-Pufferfish-Dynamic-Activation-of-Brain.patch b/patches/server/0007-Pufferfish-Dynamic-Activation-of-Brain.patch similarity index 86% rename from patches/server/0006-Pufferfish-Dynamic-Activation-of-Brain.patch rename to patches/server/0007-Pufferfish-Dynamic-Activation-of-Brain.patch index ee97fafa5..3dfce47b8 100644 --- a/patches/server/0006-Pufferfish-Dynamic-Activation-of-Brain.patch +++ b/patches/server/0007-Pufferfish-Dynamic-Activation-of-Brain.patch @@ -293,68 +293,73 @@ index ae3628efe7628427c53bb7d0f7fc6e457a511b94..e0fd0b00057bd715d80d1c223027618e if (this.assignProfessionWhenSpawned) { this.assignProfessionWhenSpawned = false; } -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index 5d161351e7517acf57e98203bab8c9f9ab9d4005..47c7b75b721bb2210eded56a7590612fbc3a395c 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -1,7 +1,9 @@ - package org.dreeam.leaf; - - import com.google.common.collect.ImmutableMap; +diff --git a/src/main/java/org/dreeam/leaf/config/modules/opt/DynamicActivationofBrain.java b/src/main/java/org/dreeam/leaf/config/modules/opt/DynamicActivationofBrain.java +new file mode 100644 +index 0000000000000000000000000000000000000000..2769eba9841ec866265e814e16f930a5e76de810 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/opt/DynamicActivationofBrain.java +@@ -0,0 +1,61 @@ ++package org.dreeam.leaf.config.modules.opt; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; +import net.minecraft.core.registries.BuiltInRegistries; - import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.MinecraftServer; +import net.minecraft.world.entity.EntityType; - import org.bukkit.configuration.ConfigurationSection; - import org.bukkit.configuration.MemoryConfiguration; - import org.jetbrains.annotations.Nullable; -@@ -13,6 +15,7 @@ import java.io.File; - import java.io.IOException; - import java.lang.reflect.Method; - import java.lang.reflect.Modifier; ++import org.dreeam.leaf.config.ConfigInfo; ++import org.dreeam.leaf.config.DoNotLoad; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ +import java.util.Collections; - import java.util.List; - import java.util.Map; - -@@ -165,6 +168,11 @@ public class LeafConfig { - - public static boolean enableAsyncMobSpawning = true; - public static boolean asyncMobSpawningInitialized; -+ public static boolean dabEnabled = true; ++import java.util.List; ++ ++public class DynamicActivationofBrain implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.PERFORMANCE; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "dab"; ++ } ++ ++ @ConfigInfo(baseName = "enabled") ++ public static boolean enabled = true; ++ @ConfigInfo(baseName = "start-distance", comments = """ ++ This value determines how far away an entity has to be ++ from the player to start being effected by DEAR.""") + public static int startDistance = 12; ++ @DoNotLoad + public static int startDistanceSquared; ++ @ConfigInfo(baseName = "max-tick-freq", comments = """ ++ This value defines how often in ticks, the furthest entity ++ will get their pathfinders and behaviors ticked. 20 = 1s""") + public static int maximumActivationPrio = 20; ++ @ConfigInfo(baseName = "activation-dist-mod", comments = """ ++ This value defines how much distance modifies an entity's ++ tick frequency. freq = (distanceToPlayer^2) / (2^value)", ++ If you want further away entities to tick less often, use 7. ++ If you want further away entities to tick more often, try 9.""") + public static int activationDistanceMod = 8; - private static void performance() { - boolean asyncMobSpawning = getBoolean("performance.enable-async-mob-spawning", enableAsyncMobSpawning, - "Whether or not asynchronous mob spawning should be enabled.", -@@ -177,6 +185,27 @@ public class LeafConfig { - asyncMobSpawningInitialized = true; - enableAsyncMobSpawning = asyncMobSpawning; - } -+ dabEnabled = getBoolean("performance.dab.enabled", "dab.enabled", dabEnabled); -+ startDistance = getInt("performance.dab.start-distance", "dab.start-distance", startDistance, -+ "This value determines how far away an entity has to be", -+ "from the player to start being effected by DEAR."); ++ @ConfigInfo(baseName = "blacklisted-entities", comments = "A list of entities to ignore for activation") ++ public static List blackedEntities = Collections.emptyList(); ++ ++ @Override ++ public void onLoaded(CommentedFileConfig config) { ++ config.setComment("performance.dab", """ ++ Optimizes entity brains when ++ they're far away from the player"""); ++ + startDistanceSquared = startDistance * startDistance; -+ maximumActivationPrio = getInt("performance.dab.max-tick-freq", "dab.max-tick-freq", maximumActivationPrio, -+ "This value defines how often in ticks, the furthest entity", -+ "will get their pathfinders and behaviors ticked. 20 = 1s"); -+ activationDistanceMod = getInt("performance.dab.activation-dist-mod", "dab.activation-dist-mod", activationDistanceMod, -+ "This value defines how much distance modifies an entity's", -+ "tick frequency. freq = (distanceToPlayer^2) / (2^value)", -+ "If you want further away entities to tick less often, use 7.", -+ "If you want further away entities to tick more often, try 9."); + for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { + entityType.dabEnabled = true; // reset all, before setting the ones to true + } -+ getStringList("performance.dab.blacklisted-entities", "dab.blacklisted-entities", Collections.emptyList(), "A list of entities to ignore for activation") -+ .forEach(name -> EntityType.byString(name).ifPresentOrElse(entityType -> { -+ entityType.dabEnabled = false; -+ }, () -> MinecraftServer.LOGGER.warn("Unknown entity \"" + name + "\""))); -+ setComment("performance.dab", "Optimizes entity brains when", "they're far away from the player"); - } - - private static void network() { ++ blackedEntities.forEach(name -> EntityType.byString(name).ifPresentOrElse(entityType -> ++ entityType.dabEnabled = false, () -> MinecraftServer.LOGGER.warn("Unknown entity \"{}\"", name))); ++ } ++} diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java index aa68ff1abced10ad03957c9c0ac24beaf112adcf..7d2b6657b0dbf5f899446d674dfc01b326fbc9c3 100644 --- a/src/main/java/org/spigotmc/ActivationRange.java diff --git a/patches/server/0007-Pufferfish-Throttle-goal-selector-during-inactive-ti.patch b/patches/server/0007-Pufferfish-Throttle-goal-selector-during-inactive-ti.patch deleted file mode 100644 index 3e18ed1a3..000000000 --- a/patches/server/0007-Pufferfish-Throttle-goal-selector-during-inactive-ti.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Kevin Raneri -Date: Sat, 11 Dec 2021 22:20:45 -0500 -Subject: [PATCH] Pufferfish: Throttle goal selector during inactive ticking - -Original license: GPL v3 -Original project: https://github.com/pufferfish-gg/Pufferfish - -diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java -index 979b76d58d05c9d83dfae45d3052eea9431dfc65..20e0e03efc8bc878bc4a1fe66d2d4027b381d7c4 100644 ---- a/src/main/java/net/minecraft/world/entity/Mob.java -+++ b/src/main/java/net/minecraft/world/entity/Mob.java -@@ -220,11 +220,13 @@ public abstract class Mob extends LivingEntity implements Targeting { - return this.lookControl; - } - -+ int _pufferfish_inactiveTickDisableCounter = 0; // Pufferfish - throttle inactive goal selector ticking - // Paper start - @Override - public void inactiveTick() { - super.inactiveTick(); -- if (this.goalSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priroity -+ boolean isThrottled = org.dreeam.leaf.LeafConfig.throttleInactiveGoalSelectorTick && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking -+ if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking - this.goalSelector.tick(); - } - if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priority -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index 47c7b75b721bb2210eded56a7590612fbc3a395c..9a11228726c9a489181fc8a0c511ea83cdcf0f6e 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -173,6 +173,7 @@ public class LeafConfig { - public static int startDistanceSquared; - public static int maximumActivationPrio = 20; - public static int activationDistanceMod = 8; -+ public static boolean throttleInactiveGoalSelectorTick = true; - private static void performance() { - boolean asyncMobSpawning = getBoolean("performance.enable-async-mob-spawning", enableAsyncMobSpawning, - "Whether or not asynchronous mob spawning should be enabled.", -@@ -206,6 +207,9 @@ public class LeafConfig { - entityType.dabEnabled = false; - }, () -> MinecraftServer.LOGGER.warn("Unknown entity \"" + name + "\""))); - setComment("performance.dab", "Optimizes entity brains when", "they're far away from the player"); -+ throttleInactiveGoalSelectorTick = getBoolean("performance.inactive-goal-selector-throttle", "inactive-goal-selector-throttle", throttleInactiveGoalSelectorTick, -+ "Throttles the AI goal selector in entity inactive ticks.", -+ "This can improve performance by a few percent, but has minor gameplay implications."); - } - - private static void network() { diff --git a/patches/server/0008-Pufferfish-Entity-TTL.patch b/patches/server/0008-Pufferfish-Entity-TTL.patch deleted file mode 100644 index 5f67df73b..000000000 --- a/patches/server/0008-Pufferfish-Entity-TTL.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Kevin Raneri -Date: Thu, 2 Jun 2022 19:54:09 -0500 -Subject: [PATCH] Pufferfish: Entity TTL - -Original license: GPL v3 -Original project: https://github.com/pufferfish-gg/Pufferfish - -diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java -index edfb51e5c54d443926d0c051a3732a97dcecbd00..31d248b0a3ec735edff3aeb5e00fd009940013da 100644 ---- a/src/main/java/net/minecraft/world/entity/Entity.java -+++ b/src/main/java/net/minecraft/world/entity/Entity.java -@@ -828,6 +828,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { - } - - public void tick() { -+ // Pufferfish start - entity TTL -+ if (type != EntityType.PLAYER && type.ttl >= 0 && this.tickCount >= type.ttl) { -+ discard(); -+ return; -+ } -+ // Pufferfish end - entity TTL - this.baseTick(); - } - -diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java -index 1679f0a3d095a7b758b468c77b6d3a4c078b7962..aa5cec6d56d7a8e80861aa4c9b4a74ca3e64be8c 100644 ---- a/src/main/java/net/minecraft/world/entity/EntityType.java -+++ b/src/main/java/net/minecraft/world/entity/EntityType.java -@@ -301,6 +301,7 @@ public class EntityType implements FeatureElement, EntityTypeT - private final int clientTrackingRange; - private final int updateInterval; - public boolean dabEnabled = false; // Pufferfish -+ public int ttl = -1; // Pufferfish - @Nullable - private String descriptionId; - @Nullable -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index 9a11228726c9a489181fc8a0c511ea83cdcf0f6e..9299d2fe1cda71b6881fd8bd65d3d74b1189a196 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -17,6 +17,7 @@ import java.lang.reflect.Method; - import java.lang.reflect.Modifier; - import java.util.Collections; - import java.util.List; -+import java.util.Locale; - import java.util.Map; - - public class LeafConfig { -@@ -174,6 +175,7 @@ public class LeafConfig { - public static int maximumActivationPrio = 20; - public static int activationDistanceMod = 8; - public static boolean throttleInactiveGoalSelectorTick = true; -+ public static Map projectileTimeouts; - private static void performance() { - boolean asyncMobSpawning = getBoolean("performance.enable-async-mob-spawning", enableAsyncMobSpawning, - "Whether or not asynchronous mob spawning should be enabled.", -@@ -210,6 +212,18 @@ public class LeafConfig { - throttleInactiveGoalSelectorTick = getBoolean("performance.inactive-goal-selector-throttle", "inactive-goal-selector-throttle", throttleInactiveGoalSelectorTick, - "Throttles the AI goal selector in entity inactive ticks.", - "This can improve performance by a few percent, but has minor gameplay implications."); -+ // Set some defaults -+ getInt("performance.entity_timeouts.SNOWBALL", -1); -+ getInt("performance.entity_timeouts.LLAMA_SPIT", -1); -+ setComment("entity_timeouts", -+ "These values define a entity's maximum lifespan. If an", -+ "entity is in this list and it has survived for longer than", -+ "that number of ticks, then it will be removed. Setting a value to", -+ "-1 disables this feature."); -+ for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { -+ String type = EntityType.getKey(entityType).getPath().toUpperCase(Locale.ROOT); -+ entityType.ttl = config.getInt("entity_timeouts." + type, -1); -+ } - } - - private static void network() { diff --git a/patches/server/0008-Pufferfish-Throttle-goal-selector-during-inactive-ti.patch b/patches/server/0008-Pufferfish-Throttle-goal-selector-during-inactive-ti.patch new file mode 100644 index 000000000..2432d5a4c --- /dev/null +++ b/patches/server/0008-Pufferfish-Throttle-goal-selector-during-inactive-ti.patch @@ -0,0 +1,62 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Kevin Raneri +Date: Sat, 11 Dec 2021 22:20:45 -0500 +Subject: [PATCH] Pufferfish: Throttle goal selector during inactive ticking + +Original license: GPL v3 +Original project: https://github.com/pufferfish-gg/Pufferfish + +diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java +index 8bd04d423a444c9b66a1ff787906eb7879721459..92a92c0f4a6bcecfecacd8e4a4ab8658124d9418 100644 +--- a/src/main/java/net/minecraft/world/entity/Mob.java ++++ b/src/main/java/net/minecraft/world/entity/Mob.java +@@ -220,11 +220,13 @@ public abstract class Mob extends LivingEntity implements Targeting { + return this.lookControl; + } + ++ int _pufferfish_inactiveTickDisableCounter = 0; // Pufferfish - throttle inactive goal selector ticking + // Paper start + @Override + public void inactiveTick() { + super.inactiveTick(); +- if (this.goalSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priroity ++ boolean isThrottled = org.dreeam.leaf.LeafConfig.throttleInactiveGoalSelectorTick && _pufferfish_inactiveTickDisableCounter++ % 20 != 0; // Pufferfish - throttle inactive goal selector ticking ++ if (this.goalSelector.inactiveTick(this.activatedPriority, true) && !isThrottled) { // Pufferfish - pass activated priroity // Pufferfish - throttle inactive goal selector ticking + this.goalSelector.tick(); + } + if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priority +diff --git a/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleInactiveGoalSelectorTick.java b/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleInactiveGoalSelectorTick.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ad6354ca7c7f8c96831fe4ef8fc624b4bcb90af5 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/opt/ThrottleInactiveGoalSelectorTick.java +@@ -0,0 +1,29 @@ ++package org.dreeam.leaf.config.modules.opt; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import org.dreeam.leaf.config.ConfigInfo; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ ++public class ThrottleInactiveGoalSelectorTick implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.PERFORMANCE; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "inactive_goal_selector_throttle"; ++ } ++ ++ @ConfigInfo(baseName = "enabled") ++ public static boolean enabled = true; ++ ++ @Override ++ public void onLoaded(CommentedFileConfig config) { ++ config.setComment("performance.inactive_goal_selector_throttle", """ ++ Throttles the AI goal selector in entity inactive ticks. ++ This can improve performance by a few percent, but has minor gameplay implications."""); ++ } ++} diff --git a/patches/server/0009-Pufferfish-Entity-TTL.patch b/patches/server/0009-Pufferfish-Entity-TTL.patch new file mode 100644 index 000000000..81888cc58 --- /dev/null +++ b/patches/server/0009-Pufferfish-Entity-TTL.patch @@ -0,0 +1,82 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Kevin Raneri +Date: Thu, 2 Jun 2022 19:54:09 -0500 +Subject: [PATCH] Pufferfish: Entity TTL + +Original license: GPL v3 +Original project: https://github.com/pufferfish-gg/Pufferfish + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index edfb51e5c54d443926d0c051a3732a97dcecbd00..31d248b0a3ec735edff3aeb5e00fd009940013da 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -828,6 +828,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource { + } + + public void tick() { ++ // Pufferfish start - entity TTL ++ if (type != EntityType.PLAYER && type.ttl >= 0 && this.tickCount >= type.ttl) { ++ discard(); ++ return; ++ } ++ // Pufferfish end - entity TTL + this.baseTick(); + } + +diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java +index 1679f0a3d095a7b758b468c77b6d3a4c078b7962..aa5cec6d56d7a8e80861aa4c9b4a74ca3e64be8c 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityType.java ++++ b/src/main/java/net/minecraft/world/entity/EntityType.java +@@ -301,6 +301,7 @@ public class EntityType implements FeatureElement, EntityTypeT + private final int clientTrackingRange; + private final int updateInterval; + public boolean dabEnabled = false; // Pufferfish ++ public int ttl = -1; // Pufferfish + @Nullable + private String descriptionId; + @Nullable +diff --git a/src/main/java/org/dreeam/leaf/config/modules/opt/EntityTTL.java b/src/main/java/org/dreeam/leaf/config/modules/opt/EntityTTL.java +new file mode 100644 +index 0000000000000000000000000000000000000000..8530cca54105d74f94a2c7e2ed7eab9b6f2f06f0 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/opt/EntityTTL.java +@@ -0,0 +1,39 @@ ++package org.dreeam.leaf.config.modules.opt; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import net.minecraft.core.registries.BuiltInRegistries; ++import net.minecraft.world.entity.EntityType; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ ++import java.util.Locale; ++ ++public class EntityTTL implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.PERFORMANCE; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "entity_timeouts"; ++ } ++ ++ @Override ++ public void onLoaded(CommentedFileConfig config) { ++ config.setComment("performance.entity_timeouts", """ ++ These values define a entity's maximum lifespan. If an ++ entity is in this list and it has survived for longer than ++ that number of ticks, then it will be removed. Setting a value to ++ -1 disables this feature."""); ++ ++ // Set some defaults ++ this.get("performance.entity_timeouts.SNOWBALL", -1, config); ++ this.get("performance.entity_timeouts.LLAMA_SPIT", -1, config); ++ for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { ++ String type = EntityType.getKey(entityType).getPath().toUpperCase(Locale.ROOT); ++ entityType.ttl = this.get("performance.entity_timeouts." + type, -1, config); ++ } ++ } ++} diff --git a/patches/server/0009-Purpur-Server-Changes.patch b/patches/server/0010-Purpur-Server-Changes.patch similarity index 99% rename from patches/server/0009-Purpur-Server-Changes.patch rename to patches/server/0010-Purpur-Server-Changes.patch index 38bb76fae..cf76734a0 100644 --- a/patches/server/0009-Purpur-Server-Changes.patch +++ b/patches/server/0010-Purpur-Server-Changes.patch @@ -30,10 +30,10 @@ Spark-Profiler.patch Halloween-options-and-optimizations.patch diff --git a/build.gradle.kts b/build.gradle.kts -index 566a23c88d7a7b156ecedd75096a16b679247df8..03e817485d50d63802dcbbacd972ffabf8612dd8 100644 +index 9f31e013809ab490442c0f6f629de7de9f89cfb4..76108aa043130d37aa3a1a4d951e12bf00fc9451 100644 --- a/build.gradle.kts +++ b/build.gradle.kts -@@ -54,6 +54,10 @@ dependencies { +@@ -64,6 +64,10 @@ dependencies { } // Paper end @@ -44,7 +44,7 @@ index 566a23c88d7a7b156ecedd75096a16b679247df8..03e817485d50d63802dcbbacd972ffab runtimeOnly("org.apache.maven:maven-resolver-provider:3.8.5") runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3") runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3") -@@ -182,7 +186,7 @@ fun TaskContainer.registerRunTask( +@@ -185,7 +189,7 @@ fun TaskContainer.registerRunTask( } } // Gale end - use default Java installation for development runs @@ -789,7 +789,7 @@ index d601d287e94a59ff93b8a83a44dac02544d211df..0ff3b06a98b2f4514b2d861b92dd70fe itemstack1.setCount(1); entityitem = entityplayer.drop(itemstack1, false, false, false); // SPIGOT-2942: Add boolean to call event diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java -index b66e708b788800ba97c25104770904ffdc460fcc..0b13a32dc14191a14ab2b30d34492295f5b6100f 100644 +index 6fa06ba5140ab135a5cfe8d6bde9ef6514ca69fa..3dbbac8128e984baff14a41379f4665a585bf877 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -98,6 +98,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface @@ -816,7 +816,7 @@ index b66e708b788800ba97c25104770904ffdc460fcc..0b13a32dc14191a14ab2b30d34492295 com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider // Paper end -@@ -279,6 +289,30 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface +@@ -278,6 +288,30 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface DedicatedServer.LOGGER.warn("Perhaps a server is already running on that port?"); return false; } @@ -847,7 +847,7 @@ index b66e708b788800ba97c25104770904ffdc460fcc..0b13a32dc14191a14ab2b30d34492295 // CraftBukkit start // this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage)); // Spigot - moved up -@@ -351,6 +385,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface +@@ -350,6 +384,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface } if (org.dreeam.leaf.LeafConfig.enableAsyncMobSpawning) mobSpawnExecutor.start(); // Pufferfish diff --git a/patches/server/0011-Purpur-Configurable-server-mod-name.patch b/patches/server/0011-Purpur-Configurable-server-mod-name.patch index b8274b790..0c5f90b61 100644 --- a/patches/server/0011-Purpur-Configurable-server-mod-name.patch +++ b/patches/server/0011-Purpur-Configurable-server-mod-name.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Purpur: Configurable server mod name diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index d6f41a95d1fe6891cbfdb6e3c7fc7872e2effbbb..bae93e3bbe3067250f08b4c8fad160f9bdf6f6d3 100644 +index 3328eb0715dab1786a677029bf6356d9118059b9..d9d92e74b101b2a4276b419ad9dcbdc361cb6860 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1657,7 +1657,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop getSkullAsynchronously() { -+ ExecutorService executorService = Executors.newCachedThreadPool(); ++ java.util.concurrent.ExecutorService executorService = java.util.concurrent.Executors.newCachedThreadPool(); + -+ CompletableFuture future = (CompletableFuture) executorService.submit(() -> getSkull()); ++ CompletableFuture future = (CompletableFuture) executorService.submit(this::getSkull); + executorService.shutdown(); + + return future; + } -+ // KeYi end ++ // Leaf end - KeYi - Player Skull API } diff --git a/patches/server/0019-KeYi-Disable-arrow-despawn-counter-by-default.patch b/patches/server/0020-KeYi-Disable-arrow-despawn-counter-by-default.patch similarity index 100% rename from patches/server/0019-KeYi-Disable-arrow-despawn-counter-by-default.patch rename to patches/server/0020-KeYi-Disable-arrow-despawn-counter-by-default.patch diff --git a/patches/server/0020-KeYi-Add-an-option-for-spigot-item-merging-mechanism.patch b/patches/server/0021-KeYi-Add-an-option-for-spigot-item-merging-mechanism.patch similarity index 51% rename from patches/server/0020-KeYi-Add-an-option-for-spigot-item-merging-mechanism.patch rename to patches/server/0021-KeYi-Add-an-option-for-spigot-item-merging-mechanism.patch index 695323824..62f44d0ac 100644 --- a/patches/server/0020-KeYi-Add-an-option-for-spigot-item-merging-mechanism.patch +++ b/patches/server/0021-KeYi-Add-an-option-for-spigot-item-merging-mechanism.patch @@ -19,23 +19,30 @@ index 3ff5c2a4a2cca5c9a89ceafd9fddeb1012344433..90ab4610026b23ac84e6c941c6581d13 ItemEntity.merge(this, itemstack, other, itemstack1); } else { ItemEntity.merge(other, itemstack1, this, itemstack); -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index 39c81e23a260aba4cbe70fb68adb5ebbceafefd4..dc492bc4aab5f5e305282f4ebb239341fe8f30f0 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -199,6 +199,7 @@ public class LeafConfig { - public static int activationDistanceMod = 8; - public static boolean throttleInactiveGoalSelectorTick = true; - public static Map projectileTimeouts; -+ public static boolean useSpigotItemMergingMechanism = true; - private static void performance() { - boolean asyncMobSpawning = getBoolean("performance.enable-async-mob-spawning", enableAsyncMobSpawning, - "Whether or not asynchronous mob spawning should be enabled.", -@@ -247,6 +248,7 @@ public class LeafConfig { - String type = EntityType.getKey(entityType).getPath().toUpperCase(Locale.ROOT); - entityType.ttl = config.getInt("entity_timeouts." + type, -1); - } -+ useSpigotItemMergingMechanism = getBoolean("performance.use-spigot-item-merging-mechanism", useSpigotItemMergingMechanism); - } - - private static void network() { +diff --git a/src/main/java/org/dreeam/leaf/config/modules/gameplay/UseSpigotItemMergingMech.java b/src/main/java/org/dreeam/leaf/config/modules/gameplay/UseSpigotItemMergingMech.java +new file mode 100644 +index 0000000000000000000000000000000000000000..839ced49d6da8b52669052f57b80e9d2a547aa5d +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/gameplay/UseSpigotItemMergingMech.java +@@ -0,0 +1,21 @@ ++package org.dreeam.leaf.config.modules.gameplay; ++ ++import org.dreeam.leaf.config.ConfigInfo; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ ++public class UseSpigotItemMergingMech implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.GAMEPLAY; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "use-spigot-item-merging-mechanism"; ++ } ++ ++ @ConfigInfo(baseName = "enabled") ++ public static boolean enabled = true; ++} diff --git a/patches/server/0021-Carpet-Fixes-Optimized-getBiome-method.patch b/patches/server/0022-Carpet-Fixes-Optimized-getBiome-method.patch similarity index 100% rename from patches/server/0021-Carpet-Fixes-Optimized-getBiome-method.patch rename to patches/server/0022-Carpet-Fixes-Optimized-getBiome-method.patch diff --git a/patches/server/0022-Carpet-Fixes-Use-optimized-RecipeManager.patch b/patches/server/0023-Carpet-Fixes-Use-optimized-RecipeManager.patch similarity index 100% rename from patches/server/0022-Carpet-Fixes-Use-optimized-RecipeManager.patch rename to patches/server/0023-Carpet-Fixes-Use-optimized-RecipeManager.patch diff --git a/patches/server/0024-Rail-Optimization-optimized-PoweredRailBlock-logic.patch b/patches/server/0024-Rail-Optimization-optimized-PoweredRailBlock-logic.patch new file mode 100644 index 000000000..6dd6238a0 --- /dev/null +++ b/patches/server/0024-Rail-Optimization-optimized-PoweredRailBlock-logic.patch @@ -0,0 +1,408 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Sat, 17 Feb 2024 17:57:08 -0500 +Subject: [PATCH] Rail Optimization: optimized PoweredRailBlock logic + +Original project: https://github.com/FxMorin/RailOptimization + +Full Rewrite of the powered rail iteration logic +that makes powered/activator rails turning on/off up to 4x faster. +This rewrite brings a massive performance boost while keeping the vanilla order. This is achieved by running all the +powered rail logic from a single rail instead of each block iterating separately. Which was not only very +expensive but also completely unnecessary and with a lot of massive overhead + +diff --git a/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java b/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java +index 40893e71fe8447b695350273bef9623bd5accdcd..417fe6e48db4d6e9b7b4e9f5b0085f63a8969a57 100644 +--- a/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/PoweredRailBlock.java +@@ -22,7 +22,7 @@ public class PoweredRailBlock extends BaseRailBlock { + this.registerDefaultState((BlockState) ((BlockState) ((BlockState) ((BlockState) this.stateDefinition.any()).setValue(PoweredRailBlock.SHAPE, RailShape.NORTH_SOUTH)).setValue(PoweredRailBlock.POWERED, false)).setValue(PoweredRailBlock.WATERLOGGED, false)); + } + +- protected boolean findPoweredRailSignal(Level world, BlockPos pos, BlockState state, boolean flag, int distance) { ++ public boolean findPoweredRailSignal(Level world, BlockPos pos, BlockState state, boolean flag, int distance) { // Leaf - Rail Optimization - protected -> public + if (distance >= world.purpurConfig.railActivationRange) { // Purpur + return false; + } else { +@@ -110,6 +110,12 @@ public class PoweredRailBlock extends BaseRailBlock { + + @Override + protected void updateState(BlockState state, Level world, BlockPos pos, Block neighbor) { ++ // Leaf start - Rail Optimization ++ if (org.dreeam.leaf.config.modules.opt.OptimizedPoweredRails.enabled) { ++ org.dreeam.leaf.optimize.OptimizedPoweredRails.customUpdateState(this, state, world, pos); ++ return; ++ } ++ // Leaf end - Rail Optimization + boolean flag = (Boolean) state.getValue(PoweredRailBlock.POWERED); + boolean flag1 = world.hasNeighborSignal(pos) || this.findPoweredRailSignal(world, pos, state, true, 0) || this.findPoweredRailSignal(world, pos, state, false, 0); + +diff --git a/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizedPoweredRails.java b/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizedPoweredRails.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a4ce1f3e5ab0d71493cef59154058ebf46db3e4c +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizedPoweredRails.java +@@ -0,0 +1,21 @@ ++package org.dreeam.leaf.config.modules.opt; ++ ++import org.dreeam.leaf.config.ConfigInfo; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ ++public class OptimizedPoweredRails implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.PERFORMANCE; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "optimized_powered_rails"; ++ } ++ ++ @ConfigInfo(baseName = "enabled") ++ public static boolean enabled = true; ++} +diff --git a/src/main/java/org/dreeam/leaf/optimize/OptimizedPoweredRails.java b/src/main/java/org/dreeam/leaf/optimize/OptimizedPoweredRails.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9e8bc27585e204aa2df77a90418bbe9e00bcd040 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/optimize/OptimizedPoweredRails.java +@@ -0,0 +1,335 @@ ++package org.dreeam.leaf.optimize; ++ ++import net.minecraft.core.BlockPos; ++import net.minecraft.core.Direction; ++import net.minecraft.world.level.Level; ++import net.minecraft.world.level.block.Block; ++import net.minecraft.world.level.block.PoweredRailBlock; ++import net.minecraft.world.level.block.state.BlockState; ++import net.minecraft.world.level.block.state.properties.RailShape; ++ ++import java.util.HashMap; ++ ++import static net.minecraft.world.level.block.Block.*; ++import static net.minecraft.world.level.block.PoweredRailBlock.POWERED; ++import static net.minecraft.world.level.block.PoweredRailBlock.SHAPE; ++ ++public class OptimizedPoweredRails { ++ ++ private static final Direction[] EAST_WEST_DIR = new Direction[]{Direction.WEST, Direction.EAST}; ++ private static final Direction[] NORTH_SOUTH_DIR = new Direction[]{Direction.SOUTH, Direction.NORTH}; ++ ++ private static final int UPDATE_FORCE_PLACE = UPDATE_MOVE_BY_PISTON | UPDATE_KNOWN_SHAPE | UPDATE_CLIENTS; ++ ++ public static int RAIL_POWER_LIMIT = 8; ++ ++ public static void giveShapeUpdate(Level level, BlockState state, BlockPos pos, BlockPos fromPos, Direction direction) { ++ BlockState oldState = level.getBlockState(pos); ++ Block.updateOrDestroy( ++ oldState, ++ oldState.updateShape(direction.getOpposite(), state, level, pos, fromPos), ++ level, ++ pos, ++ UPDATE_CLIENTS & -34, ++ 0 ++ ); ++ } ++ ++ public static void setRailPowerLimit(int powerLimit) { ++ RAIL_POWER_LIMIT = powerLimit; ++ } ++ ++ public static void customUpdateState(PoweredRailBlock self, BlockState state, Level level, BlockPos pos) { ++ boolean shouldBePowered = level.hasNeighborSignal(pos) || ++ self.findPoweredRailSignal(level, pos, state, true, 0) || ++ self.findPoweredRailSignal(level, pos, state, false, 0); ++ if (shouldBePowered != state.getValue(POWERED)) { ++ RailShape railShape = state.getValue(SHAPE); ++ if (railShape.isAscending()) { ++ level.setBlock(pos, state.setValue(POWERED, shouldBePowered), 3); ++ level.updateNeighborsAtExceptFromFacing(pos.below(), self, Direction.UP); ++ level.updateNeighborsAtExceptFromFacing(pos.above(), self, Direction.DOWN); //isAscending ++ } else if (shouldBePowered) { ++ powerLane(self, level, pos, state, railShape); ++ } else { ++ dePowerLane(self, level, pos, state, railShape); ++ } ++ } ++ } ++ ++ public static boolean findPoweredRailSignalFaster(PoweredRailBlock self, Level world, BlockPos pos, ++ boolean bl, int distance, RailShape shape, ++ HashMap checkedPos) { ++ BlockState blockState = world.getBlockState(pos); ++ boolean speedCheck = checkedPos.containsKey(pos) && checkedPos.get(pos); ++ if (speedCheck) { ++ return world.hasNeighborSignal(pos) || ++ findPoweredRailSignalFaster(self, world, pos, blockState, bl, distance + 1, checkedPos); ++ } else { ++ if (blockState.is(self)) { ++ RailShape railShape = blockState.getValue(SHAPE); ++ if (shape == RailShape.EAST_WEST && ( ++ railShape == RailShape.NORTH_SOUTH || ++ railShape == RailShape.ASCENDING_NORTH || ++ railShape == RailShape.ASCENDING_SOUTH ++ ) || shape == RailShape.NORTH_SOUTH && ( ++ railShape == RailShape.EAST_WEST || ++ railShape == RailShape.ASCENDING_EAST || ++ railShape == RailShape.ASCENDING_WEST ++ )) { ++ return false; ++ } else if (blockState.getValue(POWERED)) { ++ return world.hasNeighborSignal(pos) || ++ findPoweredRailSignalFaster(self, world, pos, blockState, bl, distance + 1, checkedPos); ++ } else { ++ return false; ++ } ++ } ++ return false; ++ } ++ } ++ ++ public static boolean findPoweredRailSignalFaster(PoweredRailBlock self, Level level, ++ BlockPos pos, BlockState state, boolean bl, int distance, ++ HashMap checkedPos) { ++ if (distance >= RAIL_POWER_LIMIT - 1) return false; ++ int i = pos.getX(); ++ int j = pos.getY(); ++ int k = pos.getZ(); ++ boolean bl2 = true; ++ RailShape railShape = state.getValue(SHAPE); ++ switch (railShape.ordinal()) { ++ case 0 -> { ++ if (bl) ++k; ++ else --k; ++ } ++ case 1 -> { ++ if (bl) --i; ++ else ++i; ++ } ++ case 2 -> { ++ if (bl) { ++ --i; ++ } else { ++ ++i; ++ ++j; ++ bl2 = false; ++ } ++ railShape = RailShape.EAST_WEST; ++ } ++ case 3 -> { ++ if (bl) { ++ --i; ++ ++j; ++ bl2 = false; ++ } else { ++ ++i; ++ } ++ railShape = RailShape.EAST_WEST; ++ } ++ case 4 -> { ++ if (bl) { ++ ++k; ++ } else { ++ --k; ++ ++j; ++ bl2 = false; ++ } ++ railShape = RailShape.NORTH_SOUTH; ++ } ++ case 5 -> { ++ if (bl) { ++ ++k; ++ ++j; ++ bl2 = false; ++ } else { ++ --k; ++ } ++ railShape = RailShape.NORTH_SOUTH; ++ } ++ } ++ return findPoweredRailSignalFaster( ++ self, level, new BlockPos(i, j, k), ++ bl, distance, railShape, checkedPos ++ ) || ++ (bl2 && findPoweredRailSignalFaster( ++ self, level, new BlockPos(i, j - 1, k), ++ bl, distance, railShape, checkedPos ++ )); ++ } ++ ++ public static void powerLane(PoweredRailBlock self, Level world, BlockPos pos, ++ BlockState mainState, RailShape railShape) { ++ world.setBlock(pos, mainState.setValue(POWERED, true), UPDATE_FORCE_PLACE); ++ HashMap checkedPos = new HashMap<>(); ++ checkedPos.put(pos, true); ++ int[] count = new int[2]; ++ if (railShape == RailShape.NORTH_SOUTH) { //Order: +z, -z ++ for (int i = 0; i < NORTH_SOUTH_DIR.length; ++i) { ++ setRailPositionsPower(self, world, pos, checkedPos, count, i, NORTH_SOUTH_DIR[i]); ++ } ++ updateRails(self, false, world, pos, mainState, count); ++ } else if (railShape == RailShape.EAST_WEST) { //Order: -x, +x ++ for (int i = 0; i < EAST_WEST_DIR.length; ++i) { ++ setRailPositionsPower(self, world, pos, checkedPos, count, i, EAST_WEST_DIR[i]); ++ } ++ updateRails(self, true, world, pos, mainState, count); ++ } ++ } ++ ++ public static void dePowerLane(PoweredRailBlock self, Level world, BlockPos pos, ++ BlockState mainState, RailShape railShape) { ++ world.setBlock(pos, mainState.setValue(POWERED, false), UPDATE_FORCE_PLACE); ++ int[] count = new int[2]; ++ if (railShape == RailShape.NORTH_SOUTH) { //Order: +z, -z ++ for (int i = 0; i < NORTH_SOUTH_DIR.length; ++i) { ++ setRailPositionsDePower(self, world, pos, count, i, NORTH_SOUTH_DIR[i]); ++ } ++ updateRails(self, false, world, pos, mainState, count); ++ } else if (railShape == RailShape.EAST_WEST) { //Order: -x, +x ++ for (int i = 0; i < EAST_WEST_DIR.length; ++i) { ++ setRailPositionsDePower(self, world, pos, count, i, EAST_WEST_DIR[i]); ++ } ++ updateRails(self, true, world, pos, mainState, count); ++ } ++ } ++ ++ private static void setRailPositionsPower(PoweredRailBlock self, Level world, BlockPos pos, ++ HashMap checkedPos, int[] count, int i, Direction dir) { ++ for (int z = 1; z < RAIL_POWER_LIMIT; z++) { ++ BlockPos newPos = pos.relative(dir, z); ++ BlockState state = world.getBlockState(newPos); ++ if (checkedPos.containsKey(newPos)) { ++ if (!checkedPos.get(newPos)) break; ++ count[i]++; ++ } else if (!state.is(self) || state.getValue(POWERED) || !( ++ world.hasNeighborSignal(newPos) || ++ findPoweredRailSignalFaster(self, world, newPos, state, true, 0, checkedPos) || ++ findPoweredRailSignalFaster(self, world, newPos, state, false, 0, checkedPos) ++ )) { ++ checkedPos.put(newPos, false); ++ break; ++ } else { ++ checkedPos.put(newPos, true); ++ world.setBlock(newPos, state.setValue(POWERED, true), UPDATE_FORCE_PLACE); ++ count[i]++; ++ } ++ } ++ } ++ ++ private static void setRailPositionsDePower(PoweredRailBlock self, Level world, BlockPos pos, ++ int[] count, int i, Direction dir) { ++ for (int z = 1; z < RAIL_POWER_LIMIT; z++) { ++ BlockPos newPos = pos.relative(dir, z); ++ BlockState state = world.getBlockState(newPos); ++ if (!state.is(self) || !state.getValue(POWERED) || world.hasNeighborSignal(newPos) || ++ self.findPoweredRailSignal(world, newPos, state, true, 0) || ++ self.findPoweredRailSignal(world, newPos, state, false, 0)) break; ++ world.setBlock(newPos, state.setValue(POWERED, false), UPDATE_FORCE_PLACE); ++ count[i]++; ++ } ++ } ++ ++ private static void shapeUpdateEnd(PoweredRailBlock self, Level world, BlockPos pos, BlockState mainState, ++ int endPos, Direction direction, int currentPos, BlockPos blockPos) { ++ if (currentPos == endPos) { ++ BlockPos newPos = pos.relative(direction, currentPos + 1); ++ OptimizedPoweredRails.giveShapeUpdate(world, mainState, newPos, pos, direction); ++ BlockState state = world.getBlockState(blockPos); ++ if (state.is(self) && state.getValue(SHAPE).isAscending()) ++ OptimizedPoweredRails.giveShapeUpdate(world, mainState, newPos.above(), pos, direction); ++ } ++ } ++ ++ private static void neighborUpdateEnd(PoweredRailBlock self, Level world, BlockPos pos, int endPos, ++ Direction direction, Block block, int currentPos, BlockPos blockPos) { ++ if (currentPos == endPos) { ++ BlockPos newPos = pos.relative(direction, currentPos + 1); ++ world.neighborChanged(newPos, block, pos); ++ BlockState state = world.getBlockState(blockPos); ++ if (state.is(self) && state.getValue(SHAPE).isAscending()) ++ world.neighborChanged(newPos.above(), block, blockPos); ++ } ++ } ++ ++ private static void updateRailsSectionEastWestShape(PoweredRailBlock self, Level world, BlockPos pos, ++ int c, BlockState mainState, Direction dir, ++ int[] count, int countAmt) { ++ BlockPos pos1 = pos.relative(dir, c); ++ if (c == 0 && count[1] == 0) ++ giveShapeUpdate(world, mainState, pos1.relative(dir.getOpposite()), pos, dir.getOpposite()); ++ shapeUpdateEnd(self, world, pos, mainState, countAmt, dir, c, pos1); ++ giveShapeUpdate(world, mainState, pos1.below(), pos, Direction.DOWN); ++ giveShapeUpdate(world, mainState, pos1.above(), pos, Direction.UP); ++ giveShapeUpdate(world, mainState, pos1.north(), pos, Direction.NORTH); ++ giveShapeUpdate(world, mainState, pos1.south(), pos, Direction.SOUTH); ++ } ++ ++ private static void updateRailsSectionNorthSouthShape(PoweredRailBlock self, Level world, BlockPos pos, ++ int c, BlockState mainState, Direction dir, ++ int[] count, int countAmt) { ++ BlockPos pos1 = pos.relative(dir, c); ++ giveShapeUpdate(world, mainState, pos1.west(), pos, Direction.WEST); ++ giveShapeUpdate(world, mainState, pos1.east(), pos, Direction.EAST); ++ giveShapeUpdate(world, mainState, pos1.below(), pos, Direction.DOWN); ++ giveShapeUpdate(world, mainState, pos1.above(), pos, Direction.UP); ++ shapeUpdateEnd(self, world, pos, mainState, countAmt, dir, c, pos1); ++ if (c == 0 && count[1] == 0) ++ giveShapeUpdate(world, mainState, pos1.relative(dir.getOpposite()), pos, dir.getOpposite()); ++ } ++ ++ private static void updateRails(PoweredRailBlock self, boolean eastWest, Level world, ++ BlockPos pos, BlockState mainState, int[] count) { ++ if (eastWest) { ++ for (int i = 0; i < EAST_WEST_DIR.length; ++i) { ++ int countAmt = count[i]; ++ if (i == 1 && countAmt == 0) continue; ++ Direction dir = EAST_WEST_DIR[i]; ++ Block block = mainState.getBlock(); ++ for (int c = countAmt; c >= i; c--) { ++ BlockPos p = pos.relative(dir, c); ++ if (c == 0 && count[1] == 0) world.neighborChanged(p.relative(dir.getOpposite()), block, pos); ++ neighborUpdateEnd(self, world, pos, countAmt, dir, block, c, p); ++ world.neighborChanged(p.below(), block, pos); ++ world.neighborChanged(p.above(), block, pos); ++ world.neighborChanged(p.north(), block, pos); ++ world.neighborChanged(p.south(), block, pos); ++ BlockPos pos2 = pos.relative(dir, c).below(); ++ world.neighborChanged(pos2.below(), block, pos); ++ world.neighborChanged(pos2.north(), block, pos); ++ world.neighborChanged(pos2.south(), block, pos); ++ if (c == countAmt) world.neighborChanged(pos.relative(dir, c + 1).below(), block, pos); ++ if (c == 0 && count[1] == 0) ++ world.neighborChanged(p.relative(dir.getOpposite()).below(), block, pos); ++ } ++ for (int c = countAmt; c >= i; c--) ++ updateRailsSectionEastWestShape(self, world, pos, c, mainState, dir, count, countAmt); ++ } ++ } else { ++ for (int i = 0; i < NORTH_SOUTH_DIR.length; ++i) { ++ int countAmt = count[i]; ++ if (i == 1 && countAmt == 0) continue; ++ Direction dir = NORTH_SOUTH_DIR[i]; ++ Block block = mainState.getBlock(); ++ for (int c = countAmt; c >= i; c--) { ++ BlockPos p = pos.relative(dir, c); ++ world.neighborChanged(p.west(), block, pos); ++ world.neighborChanged(p.east(), block, pos); ++ world.neighborChanged(p.below(), block, pos); ++ world.neighborChanged(p.above(), block, pos); ++ neighborUpdateEnd(self, world, pos, countAmt, dir, block, c, p); ++ if (c == 0 && count[1] == 0) world.neighborChanged(p.relative(dir.getOpposite()), block, pos); ++ BlockPos pos2 = pos.relative(dir, c).below(); ++ world.neighborChanged(pos2.west(), block, pos); ++ world.neighborChanged(pos2.east(), block, pos); ++ world.neighborChanged(pos2.below(), block, pos); ++ if (c == countAmt) world.neighborChanged(pos.relative(dir, c + 1).below(), block, pos); ++ if (c == 0 && count[1] == 0) ++ world.neighborChanged(p.relative(dir.getOpposite()).below(), block, pos); ++ } ++ for (int c = countAmt; c >= i; c--) ++ updateRailsSectionNorthSouthShape(self, world, pos, c, mainState, dir, count, countAmt); ++ } ++ } ++ } ++} +\ No newline at end of file diff --git a/patches/server/0023-Akarin-Save-Json-list-asynchronously.patch b/patches/server/0025-Akarin-Save-Json-list-asynchronously.patch similarity index 100% rename from patches/server/0023-Akarin-Save-Json-list-asynchronously.patch rename to patches/server/0025-Akarin-Save-Json-list-asynchronously.patch diff --git a/patches/server/0024-Slice-Smooth-Teleports.patch b/patches/server/0026-Slice-Smooth-Teleports.patch similarity index 96% rename from patches/server/0024-Slice-Smooth-Teleports.patch rename to patches/server/0026-Slice-Smooth-Teleports.patch index 9a7186e94..b63aa7618 100644 --- a/patches/server/0024-Slice-Smooth-Teleports.patch +++ b/patches/server/0026-Slice-Smooth-Teleports.patch @@ -38,10 +38,10 @@ index e3d1e030063a8746dbdaea14c6de7d900bc1ad0f..ec1dd7c0046ac9091d5b6b2cd7032378 // entityplayer1.connection.teleport(entityplayer1.getX(), entityplayer1.getY(), entityplayer1.getZ(), entityplayer1.getYRot(), entityplayer1.getXRot()); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 27203a4f30f719652dab80103eb3bd4c2104a9e3..41d67b3c57f55c6b5f685d8b00ca98ae1cd82881 100644 +index c1ac66ca7354081a55b63ad43285cae4488c619c..7f350eeeb7698e1f98070b1512e7966c66a63505 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -1265,6 +1265,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -1259,6 +1259,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player { // Paper end } diff --git a/patches/server/0025-Parchment-Make-FixLight-use-action-bar.patch b/patches/server/0027-Parchment-Make-FixLight-use-action-bar.patch similarity index 100% rename from patches/server/0025-Parchment-Make-FixLight-use-action-bar.patch rename to patches/server/0027-Parchment-Make-FixLight-use-action-bar.patch diff --git a/patches/server/0026-Leaves-Server-Utils.patch b/patches/server/0028-Leaves-Server-Utils.patch similarity index 100% rename from patches/server/0026-Leaves-Server-Utils.patch rename to patches/server/0028-Leaves-Server-Utils.patch diff --git a/patches/server/0027-Leaves-Jade-Protocol.patch b/patches/server/0029-Leaves-Jade-Protocol.patch similarity index 97% rename from patches/server/0027-Leaves-Jade-Protocol.patch rename to patches/server/0029-Leaves-Jade-Protocol.patch index 5a2452c23..747d0528d 100644 --- a/patches/server/0027-Leaves-Jade-Protocol.patch +++ b/patches/server/0029-Leaves-Jade-Protocol.patch @@ -80,20 +80,6 @@ index 62e4e6e840dd1d5dcbab14d2f5708839a08a8d4d..5c7264f1f1147d27b22164c905ebfaa1 for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index dc492bc4aab5f5e305282f4ebb239341fe8f30f0..46ab8b1b5edad18c1283db012efe25cada186630 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -251,7 +251,9 @@ public class LeafConfig { - useSpigotItemMergingMechanism = getBoolean("performance.use-spigot-item-merging-mechanism", useSpigotItemMergingMechanism); - } - -+ public static boolean jadeProtocol = false; - private static void network() { -+ jadeProtocol = getBoolean("network.protocol.jade-protocol", jadeProtocol); - } - - public static String sentryDsn = ""; diff --git a/src/main/java/top/leavesmc/leaves/protocol/JadeProtocol.java b/src/main/java/top/leavesmc/leaves/protocol/JadeProtocol.java new file mode 100644 index 0000000000000000000000000000000000000000..b5c99fa4902628a06a153c164e8143ac8fe19a56 diff --git a/patches/server/0028-Leaves-Appleskin-Protocol.patch b/patches/server/0030-Leaves-Appleskin-Protocol.patch similarity index 91% rename from patches/server/0028-Leaves-Appleskin-Protocol.patch rename to patches/server/0030-Leaves-Appleskin-Protocol.patch index 8f81de570..2044e3144 100644 --- a/patches/server/0028-Leaves-Appleskin-Protocol.patch +++ b/patches/server/0030-Leaves-Appleskin-Protocol.patch @@ -56,21 +56,6 @@ index 5c7264f1f1147d27b22164c905ebfaa1de438faf..164f07df76b10521b0f946258d4667ac for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index 46ab8b1b5edad18c1283db012efe25cada186630..1de68bd11291f7ccb38365212904ef5874b8e492 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -252,8 +252,10 @@ public class LeafConfig { - } - - public static boolean jadeProtocol = false; -+ public static boolean appleskinProtocol = false; - private static void network() { - jadeProtocol = getBoolean("network.protocol.jade-protocol", jadeProtocol); -+ appleskinProtocol = getBoolean("network.protocol.appleskin-protocol", appleskinProtocol); - } - - public static String sentryDsn = ""; diff --git a/src/main/java/top/leavesmc/leaves/protocol/AppleSkinProtocol.java b/src/main/java/top/leavesmc/leaves/protocol/AppleSkinProtocol.java new file mode 100644 index 0000000000000000000000000000000000000000..e471fb844c34294886b6668b04ec50986c47f9ca diff --git a/patches/server/0029-Leaves-Xaero-Map-Protocol.patch b/patches/server/0031-Leaves-Xaero-Map-Protocol.patch similarity index 72% rename from patches/server/0029-Leaves-Xaero-Map-Protocol.patch rename to patches/server/0031-Leaves-Xaero-Map-Protocol.patch index 64f6df480..118fe8a5a 100644 --- a/patches/server/0029-Leaves-Xaero-Map-Protocol.patch +++ b/patches/server/0031-Leaves-Xaero-Map-Protocol.patch @@ -18,32 +18,6 @@ index 7637dbf519568693fd1b707490d4cd8a2776c3a7..605da20aa58e5fbe9b306078c458d932 if (world.isRaining()) { // CraftBukkit start - handle player weather // entityplayer.connection.send(new PacketPlayOutGameStateChange(PacketPlayOutGameStateChange.START_RAINING, 0.0F)); -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index 1de68bd11291f7ccb38365212904ef5874b8e492..7725cadfdae94c88b77bb1eb51d66609f20adc60 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -19,6 +19,7 @@ import java.util.Collections; - import java.util.List; - import java.util.Locale; - import java.util.Map; -+import java.util.Random; - - public class LeafConfig { - -@@ -253,9 +254,13 @@ public class LeafConfig { - - public static boolean jadeProtocol = false; - public static boolean appleskinProtocol = false; -+ public static boolean xaeroMapProtocol = false; -+ public static int xaeroMapServerID = new Random().nextInt(); - private static void network() { - jadeProtocol = getBoolean("network.protocol.jade-protocol", jadeProtocol); - appleskinProtocol = getBoolean("network.protocol.appleskin-protocol", appleskinProtocol); -+ xaeroMapProtocol = getBoolean("network.protocol.xaero-map-protocol", xaeroMapProtocol); -+ xaeroMapServerID = getInt("network.protocol.xaero-map-server-id", xaeroMapServerID); - } - - public static String sentryDsn = ""; diff --git a/src/main/java/top/leavesmc/leaves/protocol/XaeroMapProtocol.java b/src/main/java/top/leavesmc/leaves/protocol/XaeroMapProtocol.java new file mode 100644 index 0000000000000000000000000000000000000000..461f9f947f14c4eb86f80b644628cebbe6fb3619 diff --git a/patches/server/0032-Leaves-Disable-moved-wrongly-threshold.patch b/patches/server/0032-Leaves-Disable-moved-wrongly-threshold.patch new file mode 100644 index 000000000..adcb830d7 --- /dev/null +++ b/patches/server/0032-Leaves-Disable-moved-wrongly-threshold.patch @@ -0,0 +1,102 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: violetc <58360096+s-yh-china@users.noreply.github.com> +Date: Thu, 28 Sep 2023 20:30:46 +0800 +Subject: [PATCH] Leaves: Disable moved wrongly threshold + +Original license: GPLv3 +Original project: https://github.com/LeavesMC/Leaves + +Commit: 87bfa2d2bbc597c8351ec8776b14c5a6166ed01c + +diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +index 019d504909d6492500cca77fae0a1e675aa53c88..e64070d10078e14f053422581786eb2d174f8a7b 100644 +--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java +@@ -5,6 +5,7 @@ import com.google.common.primitives.Floats; + import com.mojang.brigadier.ParseResults; + import com.mojang.brigadier.StringReader; + import com.mojang.logging.LogUtils; ++import io.papermc.paper.event.player.PlayerFailMoveEvent; + import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry; + import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; + import it.unimi.dsi.fastutil.longs.LongArrayList; +@@ -703,7 +704,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + } + // Paper end + +- if (d10 - d9 > Math.max(100.0D, Math.pow((double) (org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed), 2)) && !this.isSingleplayerOwner()) { ++ if (!org.dreeam.leaf.config.modules.gameplay.DisableMovedWronglyThreshold.enabled && d10 - d9 > Math.max(100.0D, Math.pow((double) (org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed), 2)) && !this.isSingleplayerOwner()) { // Leaves - disable can + // CraftBukkit end + ServerGamePacketListenerImpl.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", new Object[]{entity.getName().getString(), this.player.getName().getString(), d6, d7, d8}); + this.connection.send(new ClientboundMoveVehiclePacket(entity)); +@@ -739,7 +740,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + d10 = d6 * d6 + d7 * d7 + d8 * d8; + boolean flag2 = false; + +- if (d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot ++ if (!org.dreeam.leaf.config.modules.gameplay.DisableMovedWronglyThreshold.enabled && d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot // Leaves - disable can + 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)}); + } +@@ -1588,7 +1589,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + 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 (!org.dreeam.leaf.config.modules.gameplay.DisableMovedWronglyThreshold.enabled && event.getLogWarning()) // Leaves - disable can + 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; +@@ -1657,7 +1658,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + d10 = d6 * d6 + d7 * d7 + d8 * d8; + boolean flag2 = false; + +- if (!this.player.isChangingDimension() && d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot ++ if (!org.dreeam.leaf.config.modules.gameplay.DisableMovedWronglyThreshold.enabled && !this.player.isChangingDimension() && d10 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !this.player.isSleeping() && !this.player.gameMode.isCreative() && this.player.gameMode.getGameModeForPlayer() != GameType.SPECTATOR) { // Spigot // Leaves - disable can + // 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); +@@ -1785,6 +1786,10 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic + } + } + ++ private boolean isaBoolean(PlayerFailMoveEvent event) { ++ return !event.isAl; ++ } ++ + // Paper start - Add fail move event + private io.papermc.paper.event.player.PlayerFailMoveEvent fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason failReason, + double toX, double toY, double toZ, float toYaw, float toPitch, boolean logWarning) { +diff --git a/src/main/java/org/dreeam/leaf/config/modules/gameplay/DisableMovedWronglyThreshold.java b/src/main/java/org/dreeam/leaf/config/modules/gameplay/DisableMovedWronglyThreshold.java +new file mode 100644 +index 0000000000000000000000000000000000000000..c5afec3c21be35cb69905069a82acfc208a80e92 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/gameplay/DisableMovedWronglyThreshold.java +@@ -0,0 +1,27 @@ ++package org.dreeam.leaf.config.modules.gameplay; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import org.dreeam.leaf.config.ConfigInfo; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ ++public class DisableMovedWronglyThreshold implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.GAMEPLAY; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "disable_moved_wrongly_threshold"; ++ } ++ ++ @ConfigInfo(baseName = "enabled") ++ public static boolean enabled = false; ++ ++ @Override ++ public void onLoaded(CommentedFileConfig config) { ++ config.setComment("gameplay.disable_moved_wrongly_threshold", "Disable moved quickly/wrongly checks"); ++ } ++} diff --git a/patches/server/0033-Leaves-Fix-vehicle-teleport-by-end-gateway.patch b/patches/server/0033-Leaves-Fix-vehicle-teleport-by-end-gateway.patch new file mode 100644 index 000000000..b2a731e9c --- /dev/null +++ b/patches/server/0033-Leaves-Fix-vehicle-teleport-by-end-gateway.patch @@ -0,0 +1,56 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: violetc <58360096+s-yh-china@users.noreply.github.com> +Date: Tue, 15 Aug 2023 22:40:42 +0800 +Subject: [PATCH] Leaves: Fix vehicle teleport by end gateway + +Original license: GPLv3 +Original project: https://github.com/LeavesMC/Leaves + +Commit: 87bfa2d2bbc597c8351ec8776b14c5a6166ed01c + +diff --git a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java +index 6569efea988243729af2c0df8d15edc848e95c13..67e3bb394f30a4b4563b196642b7920b6e738f51 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java +@@ -107,7 +107,7 @@ public class TheEndGatewayBlockEntity extends TheEndPortalBlockEntity { + if (!list.isEmpty()) { + // Paper start + for (Entity entity : list) { +- if (entity.canChangeDimensions()) { ++ if (org.dreeam.leaf.config.modules.gameplay.UseVanillaEndTeleport.enabled || entity.canChangeDimensions()) { // Leaf - Leaves - vanilla + TheEndGatewayBlockEntity.teleportEntity(world, pos, state, entity, blockEntity); + break; + } +diff --git a/src/main/java/org/dreeam/leaf/config/modules/gameplay/UseVanillaEndTeleport.java b/src/main/java/org/dreeam/leaf/config/modules/gameplay/UseVanillaEndTeleport.java +new file mode 100644 +index 0000000000000000000000000000000000000000..778f428eaaf22236e52aa4b670f5ec34996ea862 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/gameplay/UseVanillaEndTeleport.java +@@ -0,0 +1,27 @@ ++package org.dreeam.leaf.config.modules.gameplay; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import org.dreeam.leaf.config.ConfigInfo; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ ++public class UseVanillaEndTeleport implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.GAMEPLAY; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "use_vanilla_end_teleport"; ++ } ++ ++ @ConfigInfo(baseName = "enabled") ++ public static boolean enabled = false; ++ ++ @Override ++ public void onLoaded(CommentedFileConfig config) { ++ config.setComment("gameplay.use_vanilla_end_teleport", "Enable to Use Vanilla End Gateway Teleport"); ++ } ++} diff --git a/patches/server/0034-Faster-Random-for-xaeroMapServerID-generation.patch b/patches/server/0034-Faster-Random-for-xaeroMapServerID-generation.patch new file mode 100644 index 000000000..58685f9f6 --- /dev/null +++ b/patches/server/0034-Faster-Random-for-xaeroMapServerID-generation.patch @@ -0,0 +1,308 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Mon, 9 Oct 2023 21:33:08 -0400 +Subject: [PATCH] Faster Random for xaeroMapServerID generation + + +diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java +new file mode 100644 +index 0000000000000000000000000000000000000000..4974ba174afe8cfd30e4dbbd59d4e88f62f45380 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/LeafConfig.java +@@ -0,0 +1,296 @@ ++package org.dreeam.leaf; ++ ++import com.google.common.collect.ImmutableMap; ++import net.minecraft.core.registries.BuiltInRegistries; ++import java.util.concurrent.ThreadLocalRandom; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.world.entity.EntityType; ++import org.bukkit.Bukkit; ++import org.bukkit.configuration.ConfigurationSection; ++import org.bukkit.configuration.MemoryConfiguration; ++import org.jetbrains.annotations.Nullable; ++import org.simpleyaml.configuration.comments.CommentType; ++import org.simpleyaml.configuration.file.YamlFile; ++import org.simpleyaml.exceptions.InvalidConfigurationException; ++ ++import java.io.File; ++import java.io.IOException; ++import java.lang.reflect.Method; ++import java.lang.reflect.Modifier; ++import java.util.Collections; ++import java.util.List; ++import java.util.Locale; ++import java.util.Map; ++import java.util.Random; ++import java.util.logging.Level; ++ ++public class LeafConfig { ++ ++ private static final YamlFile config = new YamlFile(); ++ private static int updates = 0; ++ ++ private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) { ++ ConfigurationSection newSection = new MemoryConfiguration(); ++ for (String key : section.getKeys(false)) { ++ if (section.isConfigurationSection(key)) { ++ newSection.set(key, convertToBukkit(section.getConfigurationSection(key))); ++ } else { ++ newSection.set(key, section.get(key)); ++ } ++ } ++ return newSection; ++ } ++ ++ public static ConfigurationSection getConfigCopy() { ++ return convertToBukkit(config); ++ } ++ ++ public static int getUpdates() { ++ return updates; ++ } ++ ++ public static void load() throws IOException { ++ File configFile = new File("leaf.yml"); ++ ++ if (configFile.exists()) { ++ try { ++ config.load(configFile); ++ } catch (InvalidConfigurationException e) { ++ throw new IOException(e); ++ } ++ } ++ ++ getString("info.version", "1.2"); ++ setComment("info", ++ "Leaf Config", ++ "Github Repo: https://github.com/Winds-Studio/Leaf", ++ "Discord: dreeam___ | QQ: 2682173972"); ++ ++ for (Method method : LeafConfig.class.getDeclaredMethods()) { ++ if (Modifier.isStatic(method.getModifiers()) && Modifier.isPrivate(method.getModifiers()) && method.getParameterCount() == 0 && ++ method.getReturnType() == Void.TYPE && !method.getName().startsWith("lambda")) { ++ method.setAccessible(true); ++ try { ++ method.invoke(null); ++ } catch (Throwable t) { ++ MinecraftServer.LOGGER.warn("Failed to load configuration option from " + method.getName(), t); ++ } ++ } ++ } ++ ++ updates++; ++ ++ config.save(configFile); ++ ++ } ++ ++ private static void setComment(String key, String... comment) { ++ if (config.contains(key)) { ++ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); ++ } ++ } ++ ++ private static void ensureDefault(String key, Object defaultValue, String... comment) { ++ if (!config.contains(key)) { ++ config.set(key, defaultValue); ++ config.setComment(key, String.join("\n", comment), CommentType.BLOCK); ++ } ++ } ++ ++ private static void set(String key, Object defaultValue) { ++ config.addDefault(key, defaultValue); ++ config.set(key, defaultValue); ++ } ++ ++ private static boolean getBoolean(String key, boolean defaultValue, String... comment) { ++ return getBoolean(key, null, defaultValue, comment); ++ } ++ ++ private static boolean getBoolean(String key, @Nullable String oldKey, boolean defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getBoolean(key, defaultValue); ++ } ++ ++ private static int getInt(String key, int defaultValue, String... comment) { ++ return getInt(key, null, defaultValue, comment); ++ } ++ ++ private static int getInt(String key, @Nullable String oldKey, int defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getInt(key, defaultValue); ++ } ++ ++ private static double getDouble(String key, double defaultValue, String... comment) { ++ return getDouble(key, null, defaultValue, comment); ++ } ++ ++ private static double getDouble(String key, @Nullable String oldKey, double defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getDouble(key, defaultValue); ++ } ++ ++ private static String getString(String key, String defaultValue, String... comment) { ++ return getOldString(key, null, defaultValue, comment); ++ } ++ ++ private static String getOldString(String key, @Nullable String oldKey, String defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getString(key, defaultValue); ++ } ++ ++ private static List getStringList(String key, List defaultValue, String... comment) { ++ return getStringList(key, null, defaultValue, comment); ++ } ++ ++ private static List getStringList(String key, @Nullable String oldKey, List defaultValue, String... comment) { ++ ensureDefault(key, defaultValue, comment); ++ return config.getStringList(key); ++ } ++ ++ static Map getMap(String key, Map defaultValue) { ++ if (defaultValue != null && getConfigCopy().getConfigurationSection(key) == null) { ++ config.addDefault(key, defaultValue); ++ return defaultValue; ++ } ++ return toMap(getConfigCopy().getConfigurationSection(key)); ++ } ++ ++ 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 String serverModName = "Leaf"; ++ private static void serverModName() { ++ serverModName = getString("server-mod-name", serverModName); ++ } ++ ++ public static String serverGUIName = "Leaf Console"; ++ private static void serverGUIName() { ++ serverGUIName = getString("server-gui-name", serverGUIName); ++ } ++ ++ public static boolean removeMojangUsernameCheck = true; ++ public static boolean removeSpigotCheckBungeeConfig = true; ++ public static boolean removeUseItemOnPacketTooFar = false; ++ public static double maxUseItemDistance = 1.0000001D; ++ private static void removal() { ++ removeMojangUsernameCheck = getBoolean("remove-Mojang-username-check", removeMojangUsernameCheck, ++ "Remove username check of Mojang", ++ "enabling all characters as username"); ++ removeSpigotCheckBungeeConfig = getBoolean("remove-Spigot-check-bungee-config", removeSpigotCheckBungeeConfig, ++ "Enable player enter backend server through proxy", ++ "without backend server enabling its bungee mode"); ++ removeUseItemOnPacketTooFar = getBoolean("remove-UseItemOnPacket-too-far-check", removeUseItemOnPacketTooFar, ++ "To enable this, players can use some packet modules with hack clients and the NoCom Exploit!!"); ++ maxUseItemDistance = getDouble("max-UseItem-distance", maxUseItemDistance, "The max distance of UseItem for players"); ++ } ++ ++ public static boolean enableAsyncMobSpawning = true; ++ public static boolean asyncMobSpawningInitialized; ++ public static boolean dabEnabled = true; ++ public static int startDistance = 12; ++ public static int startDistanceSquared; ++ public static int maximumActivationPrio = 20; ++ public static int activationDistanceMod = 8; ++ public static boolean throttleInactiveGoalSelectorTick = true; ++ public static Map projectileTimeouts; ++ public static boolean useSpigotItemMergingMechanism = true; ++ public static boolean asyncPathfinding = false; ++ public static int asyncPathfindingMaxThreads = 0; ++ public static int asyncPathfindingKeepalive = 60; ++ public static boolean cacheMinecartCollision = false; ++ private static void performance() { ++ boolean asyncMobSpawning = getBoolean("performance.enable-async-mob-spawning", enableAsyncMobSpawning, ++ "Whether or not asynchronous mob spawning should be enabled.", ++ "On servers with many entities, this can improve performance by up to 15%. You must have", ++ "paper's per-player-mob-spawns setting set to true for this to work.", ++ "One quick note - this does not actually spawn mobs async (that would be very unsafe).", ++ "This just offloads some expensive calculations that are required for mob spawning."); ++ // This prevents us from changing the value during a reload. ++ if (!asyncMobSpawningInitialized) { ++ asyncMobSpawningInitialized = true; ++ enableAsyncMobSpawning = asyncMobSpawning; ++ } ++ dabEnabled = getBoolean("performance.dab.enabled", "dab.enabled", dabEnabled); ++ startDistance = getInt("performance.dab.start-distance", "dab.start-distance", startDistance, ++ "This value determines how far away an entity has to be", ++ "from the player to start being effected by DEAR."); ++ startDistanceSquared = startDistance * startDistance; ++ maximumActivationPrio = getInt("performance.dab.max-tick-freq", "dab.max-tick-freq", maximumActivationPrio, ++ "This value defines how often in ticks, the furthest entity", ++ "will get their pathfinders and behaviors ticked. 20 = 1s"); ++ activationDistanceMod = getInt("performance.dab.activation-dist-mod", "dab.activation-dist-mod", activationDistanceMod, ++ "This value defines how much distance modifies an entity's", ++ "tick frequency. freq = (distanceToPlayer^2) / (2^value)", ++ "If you want further away entities to tick less often, use 7.", ++ "If you want further away entities to tick more often, try 9."); ++ for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { ++ entityType.dabEnabled = true; // reset all, before setting the ones to true ++ } ++ getStringList("performance.dab.blacklisted-entities", "dab.blacklisted-entities", Collections.emptyList(), "A list of entities to ignore for activation") ++ .forEach(name -> EntityType.byString(name).ifPresentOrElse(entityType -> { ++ entityType.dabEnabled = false; ++ }, () -> MinecraftServer.LOGGER.warn("Unknown entity \"" + name + "\""))); ++ setComment("performance.dab", "Optimizes entity brains when", "they're far away from the player"); ++ throttleInactiveGoalSelectorTick = getBoolean("performance.inactive-goal-selector-throttle", "inactive-goal-selector-throttle", throttleInactiveGoalSelectorTick, ++ "Throttles the AI goal selector in entity inactive ticks.", ++ "This can improve performance by a few percent, but has minor gameplay implications."); ++ // Set some defaults ++ getInt("performance.entity_timeouts.SNOWBALL", -1); ++ getInt("performance.entity_timeouts.LLAMA_SPIT", -1); ++ setComment("entity_timeouts", ++ "These values define a entity's maximum lifespan. If an", ++ "entity is in this list and it has survived for longer than", ++ "that number of ticks, then it will be removed. Setting a value to", ++ "-1 disables this feature."); ++ for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { ++ String type = EntityType.getKey(entityType).getPath().toUpperCase(Locale.ROOT); ++ entityType.ttl = config.getInt("entity_timeouts." + type, -1); ++ } ++ useSpigotItemMergingMechanism = getBoolean("performance.use-spigot-item-merging-mechanism", useSpigotItemMergingMechanism); ++ asyncPathfinding = getBoolean("performance.async-pathfinding.enable", asyncPathfinding); ++ asyncPathfindingMaxThreads = getInt("performance.async-pathfinding.max-threads", asyncPathfindingMaxThreads); ++ asyncPathfindingKeepalive = getInt("performance.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"); ++ cacheMinecartCollision = getBoolean("performance.cache-minecart-collision", cacheMinecartCollision, ++ "Cache the minecart collision result to prevent massive stacked minecart lag the server.", ++ "The known issue: entity can't enter the minecart after enabling this!"); ++ } ++ ++ public static boolean jadeProtocol = false; ++ public static boolean appleskinProtocol = false; ++ public static boolean xaeroMapProtocol = false; ++ public static int xaeroMapServerID = ThreadLocalRandom.current().nextInt(); // Leaf - Faster Random ++ private static void network() { ++ jadeProtocol = getBoolean("network.protocol.jade-protocol", jadeProtocol); ++ appleskinProtocol = getBoolean("network.protocol.appleskin-protocol", appleskinProtocol); ++ xaeroMapProtocol = getBoolean("network.protocol.xaero-map-protocol", xaeroMapProtocol); ++ xaeroMapServerID = getInt("network.protocol.xaero-map-server-id", xaeroMapServerID); ++ } ++ ++ public static String sentryDsn = ""; ++ private static void sentryDsn() { ++ String sentryEnvironment = System.getenv("SENTRY_DSN"); ++ String sentryConfig = getString("sentry-dsn", sentryDsn, "Sentry DSN for improved error logging, leave blank to disable", "Obtain from https://sentry.io/welcome/"); ++ sentryDsn = sentryEnvironment == null ? sentryConfig : sentryEnvironment; ++ if (sentryDsn != null && !sentryDsn.isBlank()) { ++ gg.pufferfish.pufferfish.sentry.SentryManager.init(); ++ } ++ } ++} diff --git a/patches/server/0030-Fix-Make-log4j-compatible-with-future-release.patch b/patches/server/0035-Fix-Make-log4j-compatible-with-future-release.patch similarity index 100% rename from patches/server/0030-Fix-Make-log4j-compatible-with-future-release.patch rename to patches/server/0035-Fix-Make-log4j-compatible-with-future-release.patch diff --git a/patches/server/0035-Fix-tracker-NPE.patch b/patches/server/0035-Fix-tracker-NPE.patch deleted file mode 100644 index b0b97c7f3..000000000 --- a/patches/server/0035-Fix-tracker-NPE.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> -Date: Wed, 2 Aug 2023 16:14:12 +0800 -Subject: [PATCH] Fix tracker NPE - - -diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java -index d6c089722bc4b40b9042607c465fe8e7e0a6683f..760ac820d7981e0c215b2ddb246cfab3ef5e59f3 100644 ---- a/src/main/java/net/minecraft/server/level/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java -@@ -114,8 +114,6 @@ import org.bukkit.craftbukkit.generator.CustomChunkGenerator; - import org.bukkit.entity.Player; - // CraftBukkit end - --import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper -- - public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider { - - private static final byte CHUNK_TYPE_REPLACEABLE = -1; -@@ -153,7 +151,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - private final Queue unloadQueue; - int viewDistance; - public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobDistanceMap; // Paper -- public final ReferenceOpenHashSet needsChangeBroadcasting = new ReferenceOpenHashSet<>(); -+ public final Set needsChangeBroadcasting = Sets.newConcurrentHashSet(); // Leaf - - // Paper - rewrite chunk system - // Paper start - optimise checkDespawn -@@ -1427,7 +1425,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider - final Entity entity; - private final int range; - SectionPos lastSectionPos; -- public final Set seenBy = new ReferenceOpenHashSet<>(); // Paper - optimise map impl -+ public final Set seenBy = Sets.newConcurrentHashSet(); // Paper - optimise map impl // Leaf - fix tracker NPE - - public TrackedEntity(Entity entity, int i, int j, boolean flag) { - this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit -diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -index 30b243ca777a5b362b50dd4ad1ea22545c038162..10a1c2ccf7cf74dc052eb9a8abf41225a69d5bc0 100644 ---- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java -+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java -@@ -10,6 +10,7 @@ import java.util.Arrays; - import java.util.Collections; - import java.util.Iterator; - import java.util.List; -+import java.util.ArrayList; // Leaf - import java.util.Objects; - import java.util.Optional; - import java.util.concurrent.CompletableFuture; -@@ -47,7 +48,6 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp - import net.minecraft.world.level.storage.DimensionDataStorage; - import net.minecraft.world.level.storage.LevelData; - import net.minecraft.world.level.storage.LevelStorageSource; --import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; // Paper - - public class ServerChunkCache extends ChunkSource { - -@@ -631,7 +631,7 @@ public class ServerChunkCache extends ChunkSource { - } - // Paper start - use set of chunks requiring updates, rather than iterating every single one loaded - if (!this.chunkMap.needsChangeBroadcasting.isEmpty()) { -- ReferenceOpenHashSet copy = this.chunkMap.needsChangeBroadcasting.clone(); -+ List copy = new ArrayList<>(this.chunkMap.needsChangeBroadcasting); // Leaf - this.chunkMap.needsChangeBroadcasting.clear(); - for (ChunkHolder holder : copy) { - holder.broadcastChanges(holder.getFullChunkNowUnchecked()); // LevelChunks are NEVER unloaded -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 00790f8c0c7ba5132b51cdaa58e4d3419aac23ab..edd8581e282836fa3331db957489059917ed6c48 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 -@@ -1,6 +1,7 @@ - package net.minecraft.world.entity.ai.attributes; - - import com.google.common.collect.Multimap; -+import com.google.common.collect.Sets; // Leaf - import com.mojang.logging.LogUtils; - import java.util.Collection; - import java.util.Map; -@@ -10,7 +11,6 @@ import java.util.stream.Collectors; - import javax.annotation.Nullable; - - import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; --import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet; - import net.minecraft.Util; - import net.minecraft.core.Holder; - import net.minecraft.core.registries.BuiltInRegistries; -@@ -23,7 +23,7 @@ public class AttributeMap { - private static final Logger LOGGER = LogUtils.getLogger(); - // Gale start - Lithium - replace AI attributes with optimized collections - private final Map attributes = new Reference2ReferenceOpenHashMap<>(0); -- private final Set dirtyAttributes = new ReferenceOpenHashSet<>(0); -+ private final Set dirtyAttributes = Sets.newConcurrentHashSet(); // Leaf - // Gale end - Lithium - replace AI attributes with optimized collections - private final AttributeSupplier supplier; - private final java.util.function.Function createInstance; // Gale - Airplane - reduce entity allocations diff --git a/patches/server/0036-Cache-minecart-vehicle-collision-results.patch b/patches/server/0036-Cache-minecart-vehicle-collision-results.patch deleted file mode 100644 index cef0bebfa..000000000 --- a/patches/server/0036-Cache-minecart-vehicle-collision-results.patch +++ /dev/null @@ -1,87 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MrHua269 -Date: Wed, 23 Aug 2023 14:21:16 -0400 -Subject: [PATCH] Cache minecart vehicle collision results - -Co-authored-by: MrHua269 - -Cache minecart vehicle collision results to prevent lag causing by massive stacked minecart -The known issue: entity can't enter the minecart after enabling this! - -diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -index c9b7b12470afac45b0132858407aacb8f91aac68..c27ffbc08a206a1e270b6419d38a7ad1763e8bae 100644 ---- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java -@@ -5,6 +5,8 @@ import com.google.common.collect.ImmutableMap; - import com.google.common.collect.Maps; - import com.google.common.collect.UnmodifiableIterator; - import com.mojang.datafixers.util.Pair; -+ -+import java.util.ArrayList; - import java.util.Iterator; - import java.util.List; - import java.util.Map; -@@ -335,6 +337,21 @@ public abstract class AbstractMinecart extends Entity { - return this.flipped ? this.getDirection().getOpposite().getClockWise() : this.getDirection().getClockWise(); - } - -+ // Leaf start - Cache minecart vehicle collision results -+ private List lastCollideCache = new ArrayList<>(); -+ private List lastCollideCache2 = new ArrayList<>(); -+ -+ private void checkAndUpdateCache() { -+ if (this.getId() + this.tickCount % 10 == 0) { -+ if (this.getMinecartType() == AbstractMinecart.Type.RIDEABLE && this.getDeltaMovement().horizontalDistanceSqr() > 0.01D) { -+ this.lastCollideCache = this.level().getEntities((Entity) this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D), EntitySelector.pushableBy(this)); -+ } else { -+ this.lastCollideCache2 = this.level().getEntities(this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D)); -+ } -+ } -+ } -+ // Leaf end -+ - @Override - public void tick() { - // Purpur start -@@ -439,8 +456,9 @@ public abstract class AbstractMinecart extends Entity { - this.level().getCraftServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleMoveEvent(vehicle, from, to)); - } - // CraftBukkit end -+ if (org.dreeam.leaf.LeafConfig.cacheMinecartCollision) this.checkAndUpdateCache(); // Leaf - Cache minecart vehicle collision results - if (this.getMinecartType() == AbstractMinecart.Type.RIDEABLE && this.getDeltaMovement().horizontalDistanceSqr() > 0.01D) { -- List list = this.level().getEntities((Entity) this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D), EntitySelector.pushableBy(this)); -+ List list = org.dreeam.leaf.LeafConfig.cacheMinecartCollision ? this.lastCollideCache : this.level().getEntities((Entity) this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D), EntitySelector.pushableBy(this)); // Leaf - Cache minecart vehicle collcollisionide results - - if (!list.isEmpty()) { - for (int l = 0; l < list.size(); ++l) { -@@ -472,7 +490,7 @@ public abstract class AbstractMinecart extends Entity { - } - } - } else { -- Iterator iterator = this.level().getEntities(this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D)).iterator(); -+ Iterator iterator = org.dreeam.leaf.LeafConfig.cacheMinecartCollision ? this.lastCollideCache2.iterator() : this.level().getEntities(this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D)).iterator(); // Leaf - Cache minecart vehicle collision results - - while (iterator.hasNext()) { - Entity entity1 = (Entity) iterator.next(); -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index 13c2e89a84905dd988b4d296158b9985b960aad7..3113970b5cb5d9b1a18c7f82b44c29afe89a0730 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -206,6 +206,7 @@ public class LeafConfig { - public static boolean asyncPathfinding = false; - public static int asyncPathfindingMaxThreads = 0; - public static int asyncPathfindingKeepalive = 60; -+ public static boolean cacheMinecartCollision = false; - private static void performance() { - boolean asyncMobSpawning = getBoolean("performance.enable-async-mob-spawning", enableAsyncMobSpawning, - "Whether or not asynchronous mob spawning should be enabled.", -@@ -266,6 +267,9 @@ public class LeafConfig { - asyncPathfindingMaxThreads = 0; - else - Bukkit.getLogger().log(Level.INFO, "Using " + asyncPathfindingMaxThreads + " threads for Async Pathfinding"); -+ cacheMinecartCollision = getBoolean("performance.cache-minecart-collision", cacheMinecartCollision, -+ "Cache the minecart collision result to prevent massive stacked minecart lag the server.", -+ "The known issue: entity can't enter the minecart after enabling this!"); - } - - public static boolean jadeProtocol = false; diff --git a/patches/server/0031-Fix-compile-error.patch b/patches/server/0036-Fix-compile-error.patch similarity index 100% rename from patches/server/0031-Fix-compile-error.patch rename to patches/server/0036-Fix-compile-error.patch diff --git a/patches/server/0032-sync-with-Gale-s-Optimize-villager-data-storage.patc.patch b/patches/server/0037-sync-with-Gale-s-Optimize-villager-data-storage.patc.patch similarity index 100% rename from patches/server/0032-sync-with-Gale-s-Optimize-villager-data-storage.patc.patch rename to patches/server/0037-sync-with-Gale-s-Optimize-villager-data-storage.patc.patch diff --git a/patches/server/0033-Revert-Purpur-Fire-Immunity-API.patch b/patches/server/0038-Revert-Purpur-Fire-Immunity-API.patch similarity index 100% rename from patches/server/0033-Revert-Purpur-Fire-Immunity-API.patch rename to patches/server/0038-Revert-Purpur-Fire-Immunity-API.patch diff --git a/patches/server/0039-Faster-Random-for-xaeroMapServerID-generation.patch b/patches/server/0039-Faster-Random-for-xaeroMapServerID-generation.patch deleted file mode 100644 index 92ff84bf2..000000000 --- a/patches/server/0039-Faster-Random-for-xaeroMapServerID-generation.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> -Date: Mon, 9 Oct 2023 21:33:08 -0400 -Subject: [PATCH] Faster Random for xaeroMapServerID generation - - -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index 3113970b5cb5d9b1a18c7f82b44c29afe89a0730..4974ba174afe8cfd30e4dbbd59d4e88f62f45380 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -2,6 +2,7 @@ package org.dreeam.leaf; - - import com.google.common.collect.ImmutableMap; - import net.minecraft.core.registries.BuiltInRegistries; -+import java.util.concurrent.ThreadLocalRandom; - import net.minecraft.server.MinecraftServer; - import net.minecraft.world.entity.EntityType; - import org.bukkit.Bukkit; -@@ -275,7 +276,7 @@ public class LeafConfig { - public static boolean jadeProtocol = false; - public static boolean appleskinProtocol = false; - public static boolean xaeroMapProtocol = false; -- public static int xaeroMapServerID = new Random().nextInt(); -+ public static int xaeroMapServerID = ThreadLocalRandom.current().nextInt(); // Leaf - Faster Random - private static void network() { - jadeProtocol = getBoolean("network.protocol.jade-protocol", jadeProtocol); - appleskinProtocol = getBoolean("network.protocol.appleskin-protocol", appleskinProtocol); diff --git a/patches/server/0034-Petal-Async-Pathfinding.patch b/patches/server/0039-Petal-Async-Pathfinding.patch similarity index 96% rename from patches/server/0034-Petal-Async-Pathfinding.patch rename to patches/server/0039-Petal-Async-Pathfinding.patch index 31251ec7b..72f2647f7 100644 --- a/patches/server/0034-Petal-Async-Pathfinding.patch +++ b/patches/server/0039-Petal-Async-Pathfinding.patch @@ -942,54 +942,52 @@ index 0e2b14e7dfedf209d63279c81723fd7955122d78..079b278e2e262af433bb5bd0c12b3d8d private final Long2ObjectMap pathTypesByPosCache = new Long2ObjectOpenHashMap<>(); public SwimNodeEvaluator(boolean canJumpOutOfWater) { -diff --git a/src/main/java/org/dreeam/leaf/LeafConfig.java b/src/main/java/org/dreeam/leaf/LeafConfig.java -index 7725cadfdae94c88b77bb1eb51d66609f20adc60..13c2e89a84905dd988b4d296158b9985b960aad7 100644 ---- a/src/main/java/org/dreeam/leaf/LeafConfig.java -+++ b/src/main/java/org/dreeam/leaf/LeafConfig.java -@@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap; - import net.minecraft.core.registries.BuiltInRegistries; - import net.minecraft.server.MinecraftServer; - import net.minecraft.world.entity.EntityType; -+import org.bukkit.Bukkit; - import org.bukkit.configuration.ConfigurationSection; - import org.bukkit.configuration.MemoryConfiguration; - import org.jetbrains.annotations.Nullable; -@@ -20,6 +21,7 @@ import java.util.List; - import java.util.Locale; - import java.util.Map; - import java.util.Random; -+import java.util.logging.Level; - - public class LeafConfig { - -@@ -201,6 +203,9 @@ public class LeafConfig { - public static boolean throttleInactiveGoalSelectorTick = true; - public static Map projectileTimeouts; - public static boolean useSpigotItemMergingMechanism = true; -+ public static boolean asyncPathfinding = false; +diff --git a/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPathfinding.java b/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPathfinding.java +new file mode 100644 +index 0000000000000000000000000000000000000000..1664214e713138c3040ec58b0a55212d78c83de4 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/async/AsyncPathfinding.java +@@ -0,0 +1,39 @@ ++package org.dreeam.leaf.config.modules.async; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import net.minecraft.server.MinecraftServer; ++import org.dreeam.leaf.config.ConfigInfo; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ ++public class AsyncPathfinding implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.ASYNC; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "async_pathfinding"; ++ } ++ ++ @ConfigInfo(baseName = "enabled") ++ public static boolean enabled = false; ++ @ConfigInfo(baseName = "max-threads") + public static int asyncPathfindingMaxThreads = 0; ++ @ConfigInfo(baseName = "keepalive") + public static int asyncPathfindingKeepalive = 60; - private static void performance() { - boolean asyncMobSpawning = getBoolean("performance.enable-async-mob-spawning", enableAsyncMobSpawning, - "Whether or not asynchronous mob spawning should be enabled.", -@@ -250,6 +255,17 @@ public class LeafConfig { - entityType.ttl = config.getInt("entity_timeouts." + type, -1); - } - useSpigotItemMergingMechanism = getBoolean("performance.use-spigot-item-merging-mechanism", useSpigotItemMergingMechanism); -+ asyncPathfinding = getBoolean("performance.async-pathfinding.enable", asyncPathfinding); -+ asyncPathfindingMaxThreads = getInt("performance.async-pathfinding.max-threads", asyncPathfindingMaxThreads); -+ asyncPathfindingKeepalive = getInt("performance.async-pathfinding.keepalive", asyncPathfindingKeepalive); ++ ++ @Override ++ public void onLoaded(CommentedFileConfig config) { + 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) ++ if (!enabled) + asyncPathfindingMaxThreads = 0; + else -+ Bukkit.getLogger().log(Level.INFO, "Using " + asyncPathfindingMaxThreads + " threads for Async Pathfinding"); - } - - public static boolean jadeProtocol = false; ++ MinecraftServer.LOGGER.info("Using {} threads for Async Pathfinding", asyncPathfindingMaxThreads); ++ } ++} +\ No newline at end of file diff --git a/src/main/java/org/dreeam/leaf/path/AsyncPath.java b/src/main/java/org/dreeam/leaf/path/AsyncPath.java new file mode 100644 index 0000000000000000000000000000000000000000..d0123bae825b88bc5cf71bf2b1f21aa0069c44a8 diff --git a/patches/server/0040-Petal-Multithreaded-Tracker.patch b/patches/server/0040-Petal-Multithreaded-Tracker.patch new file mode 100644 index 000000000..c132e0724 --- /dev/null +++ b/patches/server/0040-Petal-Multithreaded-Tracker.patch @@ -0,0 +1,479 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: peaches94 +Date: Sat, 2 Jul 2022 00:35:56 -0500 +Subject: [PATCH] Petal: Multithreaded Tracker + +TODO - Dreeam: Waiting someone to refactor or fix. +And issues waiting to check: +https://github.com/Bloom-host/Petal/issues/26 +https://github.com/Bloom-host/Petal/issues/23 +https://github.com/Bloom-host/Petal/issues/12 +https://github.com/Bloom-host/Petal/issues/11 +https://github.com/Bloom-host/Petal/issues/5 +https://github.com/Bloom-host/Petal/issues/3 + +Original code by Bloom-host, licensed under GPL v3 +You can find the original code on https://github.com/Bloom-host/Petal + +This patch was ported downstream from the Petal fork, and is derived from +the Airplane fork by Paul Sauve + +Based off the Airplane multithreaded tracker, this patch properly handles +concurrent accesses everywhere, as well as being much simpler to maintain + +Some things are too unsafe to run off the main thread so we don't attempt to do +that. This multithreaded tracker remains accurate, non-breaking and fast. + +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 06dfd0b27ac0006a2be07f54a0702519a691c6ec..9be6ddce778f47ce2c8371757ab8d30978b96888 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 (isAsync && onPrimaryThread) { + throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously."); + } else if (!isAsync && !onPrimaryThread && !this.server.isStopping()) { ++ // Leaf start - petal ++ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { ++ net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(event::callEvent); ++ return; ++ } ++ // Leaf end - petal + throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously."); + } + // KTP stop - Optimise spigot event bus +diff --git a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java +index 0fd814f1d65c111266a2b20f86561839a4cef755..3fbf6eeb3e835269217a381f00b54a7ec43d5bd2 100644 +--- a/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java ++++ b/src/main/java/io/papermc/paper/util/maplist/IteratorSafeOrderedReferenceSet.java +@@ -15,7 +15,7 @@ public final class IteratorSafeOrderedReferenceSet { + + /* list impl */ + protected E[] listElements; +- protected int listSize; ++ protected int listSize; public int getListSize() { return this.listSize; } // Leaf - petal - expose listSize + + protected final double maxFragFactor; + +diff --git a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +index 66721a27cc9a373a12dffb72c4a403473377eff6..2fbab1b4d20c3ead0ed1a117b29679fae6776f22 100644 +--- a/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java ++++ b/src/main/java/io/papermc/paper/world/ChunkEntitySlices.java +@@ -36,7 +36,7 @@ public final class ChunkEntitySlices { + protected final EntityCollectionBySection allEntities; + protected final EntityCollectionBySection hardCollidingEntities; + protected final Reference2ObjectOpenHashMap, EntityCollectionBySection> entitiesByClass; +- protected final EntityList entities = new EntityList(); ++ public final EntityList entities = new EntityList(); // Leaf - petal - protected -> public + + public FullChunkStatus status; + +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index d6c089722bc4b40b9042607c465fe8e7e0a6683f..654cd1ccb5fdc32d7bedd8fd22a15e67bafca839 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -1259,8 +1259,35 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + entity.tracker = null; // Paper - We're no longer tracked + } + ++ // Leaf start - petal - multithreaded tracker ++ private @Nullable org.dreeam.leaf.async.tracker.MultithreadedTracker multithreadedTracker; ++ private final java.util.concurrent.ConcurrentLinkedQueue trackerMainThreadTasks = new java.util.concurrent.ConcurrentLinkedQueue<>(); ++ private boolean tracking = false; ++ ++ public void runOnTrackerMainThread(final Runnable runnable) { ++ if (this.tracking) { ++ this.trackerMainThreadTasks.add(runnable); ++ } else { ++ runnable.run(); ++ } ++ } ++ + // Paper start - optimised tracker + private final void processTrackQueue() { ++ if (org.dreeam.leaf.config.modules.async.MultithreadedTracker.enabled) { ++ if (this.multithreadedTracker == null) { ++ this.multithreadedTracker = new org.dreeam.leaf.async.tracker.MultithreadedTracker(this.level.chunkSource.entityTickingChunks, this.trackerMainThreadTasks); ++ } ++ ++ this.tracking = true; ++ try { ++ this.multithreadedTracker.processTrackQueue(); ++ } finally { ++ this.tracking = false; ++ } ++ return; ++ } ++ // Leaf end - petal + for (TrackedEntity tracker : this.entityMap.values()) { + // update tracker entry + tracker.updatePlayers(tracker.entity.getPlayersInTrackRange()); +@@ -1424,10 +1451,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + public class TrackedEntity { + + public final ServerEntity serverEntity; +- final Entity entity; ++ public final Entity entity; // Leaf - petal - public + private final int range; + SectionPos lastSectionPos; +- public final Set seenBy = new ReferenceOpenHashSet<>(); // Paper - optimise map impl ++ public final Set seenBy = Sets.newConcurrentHashSet(); // Paper - Perf: optimise map impl // Leaf - Fix tracker NPE + + public TrackedEntity(Entity entity, int i, int j, boolean flag) { + this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, this.seenBy); // CraftBukkit +@@ -1439,7 +1466,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Paper start - use distance map to optimise tracker + com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet lastTrackerCandidates; + +- final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newTrackerCandidates) { ++ public final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet newTrackerCandidates) { // Leaf - petal - public + com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet oldTrackerCandidates = this.lastTrackerCandidates; + this.lastTrackerCandidates = newTrackerCandidates; + +@@ -1481,14 +1508,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(); +- ++ // Leaf start - petal - avoid NPE ++ for (ServerPlayerConnection serverplayerconnection : this.seenBy.toArray(new ServerPlayerConnection[0])) { + serverplayerconnection.send(packet); + } +- ++ // Leaf end - petal + } + + public void broadcastAndSend(Packet packet) { +@@ -1500,18 +1524,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(); +- ++ // Leaf start - petal - avoid NPE ++ for (ServerPlayerConnection serverplayerconnection : this.seenBy.toArray(new ServerPlayerConnection[0])) { + this.serverEntity.removePairing(serverplayerconnection.getPlayer()); + } +- ++ // Leaf end - petal + } + + public void removePlayer(ServerPlayer player) { +- org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot ++ //org.spigotmc.AsyncCatcher.catchOp("player tracker clear"); // Spigot // Leaf - petal - We can remove async too + if (this.seenBy.remove(player.connection)) { + this.serverEntity.removePairing(player); + } +@@ -1519,7 +1540,7 @@ 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 // Leaf - petal - We can update async + if (player != this.entity) { + // Paper start - remove allocation of Vec3D here + // Vec3 vec3d = player.position().subtract(this.entity.position()); +diff --git a/src/main/java/net/minecraft/server/level/ServerBossEvent.java b/src/main/java/net/minecraft/server/level/ServerBossEvent.java +index ca42c2642a729b90d22b968af7258f3aee72e14b..fe03f0f55de978721a9d3d8446bdead2ec82f70d 100644 +--- a/src/main/java/net/minecraft/server/level/ServerBossEvent.java ++++ b/src/main/java/net/minecraft/server/level/ServerBossEvent.java +@@ -13,7 +13,7 @@ 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 = Sets.newConcurrentHashSet(); // Leaf - petal - 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 00e522a7db9ddfd0602e8423b2793f0bbd727b35..943c27f7942459f58fe2466a043dd51772dd0237 100644 +--- a/src/main/java/net/minecraft/server/level/ServerEntity.java ++++ b/src/main/java/net/minecraft/server/level/ServerEntity.java +@@ -277,7 +277,11 @@ public class ServerEntity { + + public void removePairing(ServerPlayer player) { + this.entity.stopSeenByPlayer(player); +- player.connection.send(new ClientboundRemoveEntitiesPacket(new int[]{this.entity.getId()})); ++ // Leaf start - petal - ensure main thread ++ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> ++ player.connection.send(new ClientboundRemoveEntitiesPacket(this.entity.getId())) ++ ); ++ // Leaf end - petal + } + + public void addPairing(ServerPlayer player) { +@@ -285,7 +289,7 @@ public class ServerEntity { + + Objects.requireNonNull(list); + this.sendPairingData(player, list::add); +- player.connection.send(new ClientboundBundlePacket(list)); ++ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> player.connection.send(new ClientboundBundlePacket(list))); // Leaf - petal - Main thread + this.entity.startSeenByPlayer(player); + } + +@@ -383,19 +387,29 @@ public class ServerEntity { + + if (list != null) { + this.trackedDataValues = datawatcher.getNonDefaultValues(); +- this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list)); ++ // Leaf start - petal - sync ++ ((ServerLevel) this.entity.level()).chunkSource.chunkMap.runOnTrackerMainThread(() -> ++ this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list)) ++ ); ++ // Leaf end - petal + } + + if (this.entity instanceof LivingEntity) { + Set set = ((LivingEntity) this.entity).getAttributes().getDirtyAttributes(); + + if (!set.isEmpty()) { ++ // Leaf start - petal - sync ++ 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)); ++ ++ }); ++ // Leaf end - petal + } + + set.clear(); +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index be7c1ecd78a169d2dc9f642fcddbf47ce6905439..1067077250bf942e3d8bd505cc7c3a47b505449c 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -2575,7 +2575,7 @@ public class ServerLevel extends Level implements WorldGenLevel { + + @Override + public LevelEntityGetter getEntities() { +- org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot ++ //org.spigotmc.AsyncCatcher.catchOp("Chunk getEntities call"); // Spigot // Leaf - petal + return this.entityLookup; // Paper - rewrite chunk system + } + +diff --git a/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java b/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java +new file mode 100644 +index 0000000000000000000000000000000000000000..13ae86d295f22ce0c373ae1246fc7cad8ff7662d +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/async/tracker/MultithreadedTracker.java +@@ -0,0 +1,156 @@ ++package org.dreeam.leaf.async.tracker; ++ ++import com.google.common.util.concurrent.ThreadFactoryBuilder; ++import io.papermc.paper.util.maplist.IteratorSafeOrderedReferenceSet; ++import io.papermc.paper.world.ChunkEntitySlices; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.level.ChunkMap; ++import net.minecraft.world.entity.Entity; ++import net.minecraft.world.level.chunk.LevelChunk; ++ ++import java.util.concurrent.ConcurrentLinkedQueue; ++import java.util.concurrent.Executor; ++import java.util.concurrent.LinkedBlockingQueue; ++import java.util.concurrent.ThreadPoolExecutor; ++import java.util.concurrent.TimeUnit; ++import java.util.concurrent.atomic.AtomicInteger; ++ ++public class MultithreadedTracker { ++ ++ private enum TrackerStage { ++ UPDATE_PLAYERS, ++ SEND_CHANGES ++ } ++ ++ private static final Executor trackerExecutor = new ThreadPoolExecutor( ++ 1, ++ org.dreeam.leaf.config.modules.async.MultithreadedTracker.asyncEntityTrackerMaxThreads, ++ org.dreeam.leaf.config.modules.async.MultithreadedTracker.asyncEntityTrackerKeepalive, TimeUnit.SECONDS, ++ new LinkedBlockingQueue<>(), ++ new ThreadFactoryBuilder() ++ .setNameFormat("petal-async-tracker-thread-%d") ++ .setPriority(Thread.NORM_PRIORITY - 2) ++ .build()); ++ ++ private final IteratorSafeOrderedReferenceSet entityTickingChunks; ++ private final AtomicInteger taskIndex = new AtomicInteger(); ++ ++ private final ConcurrentLinkedQueue mainThreadTasks; ++ private final AtomicInteger finishedTasks = new AtomicInteger(); ++ ++ public MultithreadedTracker(IteratorSafeOrderedReferenceSet entityTickingChunks, ConcurrentLinkedQueue mainThreadTasks) { ++ this.entityTickingChunks = entityTickingChunks; ++ this.mainThreadTasks = mainThreadTasks; ++ } ++ ++ public void processTrackQueue() { ++ int iterator = this.entityTickingChunks.createRawIterator(); ++ ++ if (iterator == -1) { ++ return; ++ } ++ ++ // start with updating players ++ try { ++ this.taskIndex.set(iterator); ++ this.finishedTasks.set(0); ++ ++ for (int i = 0; i < org.dreeam.leaf.config.modules.async.MultithreadedTracker.asyncEntityTrackerMaxThreads; i++) { ++ trackerExecutor.execute(this::runUpdatePlayers); ++ } ++ ++ while (this.taskIndex.get() < this.entityTickingChunks.getListSize()) { ++ this.runMainThreadTasks(); ++ this.handleChunkUpdates(5); // assist ++ } ++ ++ while (this.finishedTasks.get() != org.dreeam.leaf.config.modules.async.MultithreadedTracker.asyncEntityTrackerMaxThreads) { ++ this.runMainThreadTasks(); ++ } ++ ++ this.runMainThreadTasks(); // finish any remaining tasks ++ } finally { ++ this.entityTickingChunks.finishRawIterator(); ++ } ++ ++ // then send changes ++ iterator = this.entityTickingChunks.createRawIterator(); ++ ++ if (iterator == -1) { ++ return; ++ } ++ ++ try { ++ do { ++ LevelChunk chunk = this.entityTickingChunks.rawGet(iterator); ++ ++ if (chunk != null) { ++ this.updateChunkEntities(chunk, TrackerStage.SEND_CHANGES); ++ } ++ } while (++iterator < this.entityTickingChunks.getListSize()); ++ } finally { ++ this.entityTickingChunks.finishRawIterator(); ++ } ++ } ++ ++ private void runMainThreadTasks() { ++ try { ++ Runnable task; ++ while ((task = this.mainThreadTasks.poll()) != null) { ++ task.run(); ++ } ++ } catch (Throwable throwable) { ++ MinecraftServer.LOGGER.warn("Tasks failed while ticking track queue", throwable); ++ } ++ } ++ ++ private void runUpdatePlayers() { ++ try { ++ while (handleChunkUpdates(10)) ; ++ } finally { ++ this.finishedTasks.incrementAndGet(); ++ } ++ } ++ ++ private boolean handleChunkUpdates(int tasks) { ++ int index = this.taskIndex.getAndAdd(tasks); ++ ++ for (int i = index; i < index + tasks && i < this.entityTickingChunks.getListSize(); i++) { ++ LevelChunk chunk = this.entityTickingChunks.rawGet(i); ++ if (chunk != null) { ++ try { ++ this.updateChunkEntities(chunk, TrackerStage.UPDATE_PLAYERS); ++ } catch (Throwable throwable) { ++ MinecraftServer.LOGGER.warn("Ticking tracker failed", throwable); ++ } ++ ++ } ++ } ++ ++ return index < this.entityTickingChunks.getListSize(); ++ } ++ ++ private void updateChunkEntities(LevelChunk chunk, TrackerStage trackerStage) { ++ final ChunkEntitySlices entitySlices = chunk.level.getEntityLookup().getChunk(chunk.locX, chunk.locZ); ++ if (entitySlices == null) { ++ return; ++ } ++ ++ final Entity[] rawEntities = entitySlices.entities.getRawData(); ++ final ChunkMap chunkMap = chunk.level.chunkSource.chunkMap; ++ ++ for (Entity entity : rawEntities) { ++ if (entity != null) { ++ ChunkMap.TrackedEntity entityTracker = chunkMap.entityMap.get(entity.getId()); ++ if (entityTracker != null) { ++ if (trackerStage == TrackerStage.SEND_CHANGES) { ++ entityTracker.serverEntity.sendChanges(); ++ } else if (trackerStage == TrackerStage.UPDATE_PLAYERS) { ++ entityTracker.updatePlayers(entityTracker.entity.getPlayersInTrackRange()); ++ } ++ } ++ } ++ } ++ } ++ ++} +\ No newline at end of file +diff --git a/src/main/java/org/dreeam/leaf/config/modules/async/MultithreadedTracker.java b/src/main/java/org/dreeam/leaf/config/modules/async/MultithreadedTracker.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d4e63c8f6bf6610e655048176f58a4d5a9040c90 +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/async/MultithreadedTracker.java +@@ -0,0 +1,44 @@ ++package org.dreeam.leaf.config.modules.async; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import net.minecraft.server.MinecraftServer; ++import org.dreeam.leaf.config.ConfigInfo; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ ++public class MultithreadedTracker implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.ASYNC; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "async_entity_tracker"; ++ } ++ ++ @ConfigInfo(baseName = "enabled") ++ public static boolean enabled = false; ++ @ConfigInfo(baseName = "max-threads") ++ public static int asyncEntityTrackerMaxThreads = 0; ++ @ConfigInfo(baseName = "keepalive") ++ public static int asyncEntityTrackerKeepalive = 60; ++ ++ @Override ++ public void onLoaded(CommentedFileConfig config) { ++ config.setComment("async.async_entity_tracker", """ ++ WARNING! Enabling async entity tracker is not recommend currently. ++ Whether or not async entity tracking should be enabled. ++ You may encounter issues with NPCs"""); ++ ++ if (asyncEntityTrackerMaxThreads < 0) ++ asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncEntityTrackerMaxThreads, 1); ++ else if (asyncEntityTrackerMaxThreads == 0) ++ asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1); ++ if (!enabled) ++ asyncEntityTrackerMaxThreads = 0; ++ else ++ MinecraftServer.LOGGER.info("Using {} threads for Async Entity Tracker", asyncEntityTrackerMaxThreads); ++ } ++} diff --git a/patches/server/0041-Petal-reduce-work-done-by-game-event-system.patch b/patches/server/0041-Petal-reduce-work-done-by-game-event-system.patch new file mode 100644 index 000000000..052e92185 --- /dev/null +++ b/patches/server/0041-Petal-reduce-work-done-by-game-event-system.patch @@ -0,0 +1,195 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: peaches94 +Date: Sun, 10 Jul 2022 13:29:20 -0500 +Subject: [PATCH] Petal: reduce work done by game event system + +Original license: GPL v3 +Original project: https://github.com/Bloom-host/Petal + +1. going into game event dispatching can be expensive so run the checks before dispatching + +2. EuclideanGameEventListenerRegistry is not used concurrently so we ban that usage for improved performance with allays + +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 6c378e3485fef4e8b8906c33a45a23e6f77e9cdd..68a2a31477f9ad6ec854b5b464070baa7e059ecf 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 +@@ -58,7 +58,7 @@ public class SculkCatalystBlockEntity extends BlockEntity implements GameEventLi + return this.catalystListener; + } + +- public static class CatalystListener implements GameEventListener { ++ public class CatalystListener implements GameEventListener { // Leaf - petal + + public static final int PULSE_TICKS = 8; + final SculkSpreader sculkSpreader; +@@ -128,6 +128,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); + } + ++ // Leaf start - petal ++ @Override ++ public boolean listensToEvent(GameEvent gameEvent, GameEvent.Context context) { ++ return !SculkCatalystBlockEntity.this.isRemoved() && gameEvent == GameEvent.ENTITY_DIE.value() && context.sourceEntity() instanceof LivingEntity; ++ } ++ // Leaf end - petal ++ + 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 b0016f7ccc6225257f2bc893ffb1565c6c2da2f1..3035e3b9a1e57ffbf0f6889f7941f4aab218f870 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -80,7 +80,18 @@ public class LevelChunk extends ChunkAccess { + private Supplier fullStatus; + @Nullable + private LevelChunk.PostLoadProcessor postLoad; +- private final Int2ObjectMap gameEventListenerRegistrySections; ++ // Leaf start - petal ++ private final GameEventListenerRegistry[] gameEventListenerRegistrySections; ++ private static final int GAME_EVENT_DISPATCHER_RADIUS = 2; ++ ++ private static int getGameEventSectionIndex(int sectionIndex) { ++ return sectionIndex + GAME_EVENT_DISPATCHER_RADIUS; ++ } ++ ++ private static int getGameEventSectionLength(int sectionCount) { ++ return sectionCount + (GAME_EVENT_DISPATCHER_RADIUS * 2); ++ } ++ // Leaf end - petal + private final LevelChunkTicks blockTicks; + private final LevelChunkTicks fluidTicks; + +@@ -108,7 +119,7 @@ public class LevelChunk extends ChunkAccess { + // Paper end - rewrite light engine + this.tickersInLevel = Maps.newHashMap(); + this.level = (ServerLevel) world; // CraftBukkit - type +- this.gameEventListenerRegistrySections = new Int2ObjectOpenHashMap(); ++ this.gameEventListenerRegistrySections = new GameEventListenerRegistry[getGameEventSectionLength(this.getSectionsCount())]; // Leaf - petal + Heightmap.Types[] aheightmap_type = Heightmap.Types.values(); + int j = aheightmap_type.length; + +@@ -398,9 +409,23 @@ public class LevelChunk extends ChunkAccess { + if (world instanceof ServerLevel) { + ServerLevel worldserver = (ServerLevel) world; + +- return (GameEventListenerRegistry) this.gameEventListenerRegistrySections.computeIfAbsent(ySectionCoord, (j) -> { +- return new EuclideanGameEventListenerRegistry(worldserver, ySectionCoord, this::removeGameEventListenerRegistry); +- }); ++ // Leaf start - petal ++ int sectionIndex = getGameEventSectionIndex(this.getSectionIndexFromSectionY(ySectionCoord)); ++ ++ // drop game events that are too far away (32 blocks) from loaded sections ++ // this matches the highest radius of game events in the game ++ if (sectionIndex < 0 || sectionIndex >= this.gameEventListenerRegistrySections.length) { ++ return GameEventListenerRegistry.NOOP; ++ } ++ ++ var dispatcher = this.gameEventListenerRegistrySections[sectionIndex]; ++ ++ if (dispatcher == null) { ++ dispatcher = this.gameEventListenerRegistrySections[sectionIndex] = new EuclideanGameEventListenerRegistry(worldserver, ySectionCoord, this::removeGameEventListenerRegistry); ++ } ++ ++ return dispatcher; ++ // Leaf end - petal + } else { + return super.getListenerRegistry(ySectionCoord); + } +@@ -774,7 +799,7 @@ public class LevelChunk extends ChunkAccess { + } + + private void removeGameEventListenerRegistry(int ySectionCoord) { +- this.gameEventListenerRegistrySections.remove(ySectionCoord); ++ this.gameEventListenerRegistrySections[getGameEventSectionIndex(this.getSectionIndexFromSectionY(ySectionCoord))] = null; // Leaf - petal + } + + 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 48132c44ddc147eea0f751c1fc568388730104c5..e344b6c56e3ea7ec26e4c5bf374aeef3ce175666 100644 +--- a/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java ++++ b/src/main/java/net/minecraft/world/level/gameevent/EuclideanGameEventListenerRegistry.java +@@ -12,8 +12,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(); // Leaf - petal - Not necessary ++ //private final List listenersToAdd = Lists.newArrayList(); // Leaf - petal + private boolean processing; + private final ServerLevel level; + private final int sectionY; +@@ -33,7 +33,7 @@ public class EuclideanGameEventListenerRegistry implements GameEventListenerRegi + @Override + public void register(GameEventListener listener) { + if (this.processing) { +- this.listenersToAdd.add(listener); ++ throw new java.util.ConcurrentModificationException(); // Leaf - petal - Disallow concurrent modification + } else { + this.listeners.add(listener); + } +@@ -44,7 +44,7 @@ public class EuclideanGameEventListenerRegistry implements GameEventListenerRegi + @Override + public void unregister(GameEventListener listener) { + if (this.processing) { +- this.listenersToRemove.add(listener); ++ throw new java.util.ConcurrentModificationException(); // Leaf - petal - Disallow concurrent modification + } else { + this.listeners.remove(listener); + } +@@ -65,7 +65,7 @@ public class EuclideanGameEventListenerRegistry implements GameEventListenerRegi + + while(iterator.hasNext()) { + GameEventListener gameEventListener = iterator.next(); +- if (this.listenersToRemove.remove(gameEventListener)) { ++ if (false) { // Leaf - petal - Disallow concurrent modification + iterator.remove(); + } else { + Optional optional = getPostableListenerPosition(this.level, pos, gameEventListener); +@@ -79,6 +79,8 @@ public class EuclideanGameEventListenerRegistry implements GameEventListenerRegi + this.processing = false; + } + ++ // Leaf start - petal ++ /* + if (!this.listenersToAdd.isEmpty()) { + this.listeners.addAll(this.listenersToAdd); + this.listenersToAdd.clear(); +@@ -88,6 +90,8 @@ public class EuclideanGameEventListenerRegistry implements GameEventListenerRegi + this.listeners.removeAll(this.listenersToRemove); + this.listenersToRemove.clear(); + } ++ */ ++ // Leaf end - petal + + return bl; + } +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 744160405680babbb83c99abcbe2dc89bf312398..fd81025bc40c85cca3242a1c6bbde94336d701aa 100644 +--- a/src/main/java/net/minecraft/world/level/gameevent/GameEventDispatcher.java ++++ b/src/main/java/net/minecraft/world/level/gameevent/GameEventDispatcher.java +@@ -44,6 +44,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; // Leaf - petal - If they don't listen, ignore + if (gameeventlistener.getDeliveryMode() == GameEventListener.DeliveryMode.BY_DISTANCE) { + list.add(new GameEvent.ListenerInfo(event, emitterPos, emitter, gameeventlistener, vec3d1)); + } 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 a4148dd326b10b0eb2bc7884691d79dcda60e010..8e235b254aa53a8fc69ecca16700dfc50a341a30 100644 +--- a/src/main/java/net/minecraft/world/level/gameevent/GameEventListener.java ++++ b/src/main/java/net/minecraft/world/level/gameevent/GameEventListener.java +@@ -22,4 +22,10 @@ public interface GameEventListener { + public interface Holder { + T getListener(); + } ++ ++ // Leaf start - petal - Add check for seeing if this listener cares about an event ++ default boolean listensToEvent(GameEvent gameEvent, GameEvent.Context context) { ++ return true; ++ } ++ // Leaf end - petal - Add check for seeing if this listener cares about an event + } diff --git a/patches/server/0042-Optimize-Minecart-collisions.patch b/patches/server/0042-Optimize-Minecart-collisions.patch new file mode 100644 index 000000000..5e151e7b0 --- /dev/null +++ b/patches/server/0042-Optimize-Minecart-collisions.patch @@ -0,0 +1,130 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Dreeam <61569423+Dreeam-qwq@users.noreply.github.com> +Date: Sat, 6 Apr 2024 22:57:41 -0400 +Subject: [PATCH] Optimize Minecart collisions + +Co-authored-by: MrHua269 + +Skip tick collisions to to prevent lag causing by massive stacked Minecart +Useful for anarchy server. + +diff --git a/src/main/java/net/minecraft/world/entity/EntitySelector.java b/src/main/java/net/minecraft/world/entity/EntitySelector.java +index cfa9607241c3e69777ffc317206996c2f783437a..fa8aaa5c30e53aa98aab15a924e27df92af41f2d 100644 +--- a/src/main/java/net/minecraft/world/entity/EntitySelector.java ++++ b/src/main/java/net/minecraft/world/entity/EntitySelector.java +@@ -63,6 +63,13 @@ public final class EntitySelector { + + public static Predicate pushable(Entity entity, boolean ignoreClimbing) { + // Paper end ++ ++ // Leaf start - Optimize Minecart collisions ++ if (entity instanceof net.minecraft.world.entity.vehicle.AbstractMinecart) { ++ return x -> true; ++ } ++ // Leaf end - Optimize Minecart collisions ++ + Team scoreboardteambase = entity.getTeam(); + Team.CollisionRule scoreboardteambase_enumteampush = scoreboardteambase == null ? Team.CollisionRule.ALWAYS : scoreboardteambase.getCollisionRule(); + +diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java +index c9b7b12470afac45b0132858407aacb8f91aac68..86df284cd88a20779a8aec2daec02a9dd1dc69d8 100644 +--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java ++++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractMinecart.java +@@ -439,13 +439,15 @@ public abstract class AbstractMinecart extends Entity { + this.level().getCraftServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleMoveEvent(vehicle, from, to)); + } + // CraftBukkit end +- if (this.getMinecartType() == AbstractMinecart.Type.RIDEABLE && this.getDeltaMovement().horizontalDistanceSqr() > 0.01D) { ++ // Leaf start - Optimize Minecart collision handling ++ // The logic below is used to get list of entities around Minecart ++ // and handle behaviors for their collisions with each other ++ if (!org.dreeam.leaf.config.modules.opt.OptimizeMinecart.enabled || this.tickCount % org.dreeam.leaf.config.modules.opt.OptimizeMinecart.skipTickCount == 0) { ++ if (this.getMinecartType() == AbstractMinecart.Type.RIDEABLE && (org.dreeam.leaf.config.modules.opt.OptimizeMinecart.enabled || this.getDeltaMovement().horizontalDistanceSqr() > 0.01D)) { + List list = this.level().getEntities((Entity) this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D), EntitySelector.pushableBy(this)); + + if (!list.isEmpty()) { +- for (int l = 0; l < list.size(); ++l) { +- Entity entity = (Entity) list.get(l); +- ++ for (Entity entity : list) { + if (!(entity instanceof Player) && !(entity instanceof IronGolem) && !(entity instanceof AbstractMinecart) && !this.isVehicle() && !entity.isPassenger()) { + // CraftBukkit start + VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, entity.getBukkitEntity()); +@@ -472,11 +474,7 @@ public abstract class AbstractMinecart extends Entity { + } + } + } else { +- Iterator iterator = this.level().getEntities(this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D)).iterator(); +- +- while (iterator.hasNext()) { +- Entity entity1 = (Entity) iterator.next(); +- ++ for (Entity entity1 : this.level().getEntities(this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D))) { + if (!this.hasPassenger(entity1) && entity1.isPushable() && entity1 instanceof AbstractMinecart) { + // CraftBukkit start + VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, entity1.getBukkitEntity()); +@@ -490,6 +488,8 @@ public abstract class AbstractMinecart extends Entity { + } + } + } ++ } ++ // Leaf end + + this.updateInWaterStateAndDoFluidPushing(); + if (this.isInLava()) { +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 78f2bea7ae34be7a2152d7c31e90a524eac1ddfe..00b661973b73d7a75277511cdf8e7efe1311ef42 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -1238,12 +1238,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + return this.getChunk(chunkX, chunkZ, ChunkStatus.FULL, false); + } + ++ // Leaf start - Optimize predicate call + @Override +- public List getEntities(@Nullable Entity except, AABB box, Predicate predicate) { ++ public List getEntities(@Nullable Entity except, AABB box, @Nullable Predicate predicate) { + List list = Lists.newArrayList(); +- ((ServerLevel)this).getEntityLookup().getEntities(except, box, list, predicate); // Paper - optimise this call +- return list; ++ ((ServerLevel) this).getEntityLookup().getEntities(except, box, list, null); // Paper - optimise this call ++ return predicate != null ? list.stream().filter(predicate).toList() : list; + } ++ // Leaf end + + @Override + public List getEntities(EntityTypeTest filter, AABB box, Predicate predicate) { +diff --git a/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeMinecart.java b/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeMinecart.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a891454eb9b83d50ef3e8914efbb013f88a6b3fd +--- /dev/null ++++ b/src/main/java/org/dreeam/leaf/config/modules/opt/OptimizeMinecart.java +@@ -0,0 +1,28 @@ ++package org.dreeam.leaf.config.modules.opt; ++ ++import org.dreeam.leaf.config.ConfigInfo; ++import org.dreeam.leaf.config.EnumConfigCategory; ++import org.dreeam.leaf.config.IConfigModule; ++ ++public class OptimizeMinecart implements IConfigModule { ++ ++ @Override ++ public EnumConfigCategory getCategory() { ++ return EnumConfigCategory.PERFORMANCE; ++ } ++ ++ @Override ++ public String getBaseName() { ++ return "optimize_minecart"; ++ } ++ ++ @ConfigInfo(baseName = "enabled", comments = """ ++ Enable this feature to handle large amount of stacked Minecart better. ++ By skipping tick collisions to reduce expense getting entities list ++ and bukkit event calls, useful for the anarchy server. ++ """) ++ public static boolean enabled = false; ++ ++ @ConfigInfo(baseName = "skip-tick-count") ++ public static int skipTickCount = 30; ++} diff --git a/patches/server/0037-Reduce-canSee-work.patch b/patches/server/0043-Reduce-canSee-work.patch similarity index 90% rename from patches/server/0037-Reduce-canSee-work.patch rename to patches/server/0043-Reduce-canSee-work.patch index fd1705905..767be7d22 100644 --- a/patches/server/0037-Reduce-canSee-work.patch +++ b/patches/server/0043-Reduce-canSee-work.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Reduce canSee work Credit by: Martijn Muijsers , MachineBreaker diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 78f2bea7ae34be7a2152d7c31e90a524eac1ddfe..9ce1676e9a51ae7fd59bc4ef29efdcb160a86657 100644 +index 00b661973b73d7a75277511cdf8e7efe1311ef42..fa4dc08048aad25905310f3e3f032c63de7d2a7a 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -442,11 +442,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { @@ -35,10 +35,10 @@ index 78f2bea7ae34be7a2152d7c31e90a524eac1ddfe..9ce1676e9a51ae7fd59bc4ef29efdcb1 return false; } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 41d67b3c57f55c6b5f685d8b00ca98ae1cd82881..b988261eebba51f4205df8ffe8661e7539c72c9c 100644 +index 7f350eeeb7698e1f98070b1512e7966c66a63505..0663f03f5439d1d4527df169a5e54fe1cd56cffe 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -578,12 +578,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -572,12 +572,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player { this.getHandle().connection.send(packet); } @@ -53,7 +53,7 @@ index 41d67b3c57f55c6b5f685d8b00ca98ae1cd82881..b988261eebba51f4205df8ffe8661e75 if ((this.getUniqueId() == null) || (other.getUniqueId() == null)) { return false; } -@@ -2100,6 +2102,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -2094,6 +2096,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { return this.canSee((org.bukkit.entity.Entity) player); } diff --git a/patches/server/0038-Faster-Natural-Spawning.patch b/patches/server/0044-Faster-Natural-Spawning.patch similarity index 98% rename from patches/server/0038-Faster-Natural-Spawning.patch rename to patches/server/0044-Faster-Natural-Spawning.patch index 4c6eff74c..63c6c2330 100644 --- a/patches/server/0038-Faster-Natural-Spawning.patch +++ b/patches/server/0044-Faster-Natural-Spawning.patch @@ -22,7 +22,7 @@ index ff1ad1024419182f7f3de578442c1c033d4c9ebb..5e51a1c79811291a740ad4d5e79a7d67 @Deprecated static RandomSource createThreadSafe() { diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java -index 9ce1676e9a51ae7fd59bc4ef29efdcb160a86657..4f7839ab19922d47fbeb41063bedd730054075fd 100644 +index fa4dc08048aad25905310f3e3f032c63de7d2a7a..a1822d05cb868628d3cd27a0a4efa85288437e3d 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -125,6 +125,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable { diff --git a/patches/server/0040-Paper-Add-another-slot-sanity-check.patch b/patches/server/0045-Paper-Add-another-slot-sanity-check.patch similarity index 100% rename from patches/server/0040-Paper-Add-another-slot-sanity-check.patch rename to patches/server/0045-Paper-Add-another-slot-sanity-check.patch diff --git a/patches/server/0041-Configurable-fix-tripwire-dupe.patch b/patches/server/0046-Configurable-fix-tripwire-dupe.patch similarity index 100% rename from patches/server/0041-Configurable-fix-tripwire-dupe.patch rename to patches/server/0046-Configurable-fix-tripwire-dupe.patch diff --git a/patches/server/0042-Fix-MC-249136-lag-when-attempting-to-locate-a-buried.patch b/patches/server/0047-Fix-MC-249136-lag-when-attempting-to-locate-a-buried.patch similarity index 100% rename from patches/server/0042-Fix-MC-249136-lag-when-attempting-to-locate-a-buried.patch rename to patches/server/0047-Fix-MC-249136-lag-when-attempting-to-locate-a-buried.patch diff --git a/patches/server/0043-Fix-MC-172047.patch b/patches/server/0048-Fix-MC-172047.patch similarity index 100% rename from patches/server/0043-Fix-MC-172047.patch rename to patches/server/0048-Fix-MC-172047.patch diff --git a/patches/server/0044-Configurable-bamboo-collision.patch b/patches/server/0049-Configurable-bamboo-collision.patch similarity index 100% rename from patches/server/0044-Configurable-bamboo-collision.patch rename to patches/server/0049-Configurable-bamboo-collision.patch diff --git a/patches/server/0045-Use-a-shadow-fork-that-supports-Java-21.patch b/patches/server/0050-Use-a-shadow-fork-that-supports-Java-21.patch similarity index 85% rename from patches/server/0045-Use-a-shadow-fork-that-supports-Java-21.patch rename to patches/server/0050-Use-a-shadow-fork-that-supports-Java-21.patch index b50a50428..2c24d36cd 100644 --- a/patches/server/0045-Use-a-shadow-fork-that-supports-Java-21.patch +++ b/patches/server/0050-Use-a-shadow-fork-that-supports-Java-21.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Use a shadow fork that supports Java 21 diff --git a/build.gradle.kts b/build.gradle.kts -index 99cf0bebf6bd1579c1f347e0c1750c7b13f74566..09dd1b8b9471b3e742b102e8ddf205f9be66e412 100644 +index b6ee7cde5d0575a20e0400ee595d669ddacbf4ad..1e539e22469bf9c1b140501391057410f0ba9af3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ import io.papermc.paperweight.util.*