diff --git a/build.gradle.kts b/build.gradle.kts index 5de19ae..a36d2ea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { java `maven-publish` - id("io.papermc.paperweight.patcher") version "1.7.1" + id("io.papermc.paperweight.patcher") version "1.7.2-SNAPSHOT" } val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/" diff --git a/patches/api/0002-Purpur-Remove-timings.patch b/patches/api/0002-Purpur-Remove-timings.patch new file mode 100644 index 0000000..73e0b5e --- /dev/null +++ b/patches/api/0002-Purpur-Remove-timings.patch @@ -0,0 +1,2326 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 15:58:36 +0400 +Subject: [PATCH] Purpur-Remove-timings + + +diff --git a/src/main/java/co/aikar/timings/FullServerTickHandler.java b/src/main/java/co/aikar/timings/FullServerTickHandler.java +deleted file mode 100644 +index 3e747abde6fefae90f1c15cb00158bc5303cbe50..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/FullServerTickHandler.java ++++ /dev/null +@@ -1,89 +0,0 @@ +-package co.aikar.timings; +- +-import static co.aikar.timings.TimingsManager.*; +- +-import org.bukkit.Bukkit; +-import org.jetbrains.annotations.NotNull; +- +-/** +- * @deprecated Timings will be removed in the future +- */ +-@Deprecated(forRemoval = true) +-public class FullServerTickHandler extends TimingHandler { +- private static final TimingIdentifier IDENTITY = new TimingIdentifier("Minecraft", "Full Server Tick", null); +- final TimingData minuteData; +- double avgFreeMemory = -1D; +- double avgUsedMemory = -1D; +- FullServerTickHandler() { +- super(IDENTITY); +- minuteData = new TimingData(id); +- +- TIMING_MAP.put(IDENTITY, this); +- } +- +- @NotNull +- @Override +- public Timing startTiming() { +- if (TimingsManager.needsFullReset) { +- TimingsManager.resetTimings(); +- } else if (TimingsManager.needsRecheckEnabled) { +- TimingsManager.recheckEnabled(); +- } +- return super.startTiming(); +- } +- +- @Override +- public void stopTiming() { +- super.stopTiming(); +- if (!isEnabled()) { +- return; +- } +- if (TimingHistory.timedTicks % 20 == 0) { +- final Runtime runtime = Runtime.getRuntime(); +- double usedMemory = runtime.totalMemory() - runtime.freeMemory(); +- double freeMemory = runtime.maxMemory() - usedMemory; +- if (this.avgFreeMemory == -1) { +- this.avgFreeMemory = freeMemory; +- } else { +- this.avgFreeMemory = (this.avgFreeMemory * (59 / 60D)) + (freeMemory * (1 / 60D)); +- } +- +- if (this.avgUsedMemory == -1) { +- this.avgUsedMemory = usedMemory; +- } else { +- this.avgUsedMemory = (this.avgUsedMemory * (59 / 60D)) + (usedMemory * (1 / 60D)); +- } +- } +- +- long start = System.nanoTime(); +- TimingsManager.tick(); +- long diff = System.nanoTime() - start; +- TIMINGS_TICK.addDiff(diff, null); +- // addDiff for TIMINGS_TICK incremented this, bring it back down to 1 per tick. +- record.setCurTickCount(record.getCurTickCount()-1); +- +- minuteData.setCurTickTotal(record.getCurTickTotal()); +- minuteData.setCurTickCount(1); +- +- boolean violated = isViolated(); +- minuteData.processTick(violated); +- TIMINGS_TICK.processTick(violated); +- processTick(violated); +- +- +- if (TimingHistory.timedTicks % 1200 == 0) { +- MINUTE_REPORTS.add(new TimingHistory.MinuteReport()); +- TimingHistory.resetTicks(false); +- minuteData.reset(); +- } +- if (TimingHistory.timedTicks % Timings.getHistoryInterval() == 0) { +- TimingsManager.HISTORY.add(new TimingHistory()); +- TimingsManager.resetTimings(); +- } +- Bukkit.getUnsafe().reportTimings(); +- } +- +- boolean isViolated() { +- return record.getCurTickTotal() > 50000000; +- } +-} +diff --git a/src/main/java/co/aikar/timings/NullTimingHandler.java b/src/main/java/co/aikar/timings/NullTimingHandler.java +deleted file mode 100644 +index 42e7e712403676171d34d5f2be27e48e7a071ebd..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/NullTimingHandler.java ++++ /dev/null +@@ -1,72 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import org.jetbrains.annotations.NotNull; +-import org.jetbrains.annotations.Nullable; +- +-/** +- * @deprecated Timings will be removed in the future +- */ +-@Deprecated(forRemoval = true) +-public final class NullTimingHandler implements Timing { +- public static final Timing NULL = new NullTimingHandler(); +- @NotNull +- @Override +- public Timing startTiming() { +- return this; +- } +- +- @Override +- public void stopTiming() { +- +- } +- +- @NotNull +- @Override +- public Timing startTimingIfSync() { +- return this; +- } +- +- @Override +- public void stopTimingIfSync() { +- +- } +- +- @Override +- public void abort() { +- +- } +- +- @Nullable +- @Override +- public TimingHandler getTimingHandler() { +- return null; +- } +- +- @Override +- public void close() { +- +- } +-} +diff --git a/src/main/java/co/aikar/timings/TimedEventExecutor.java b/src/main/java/co/aikar/timings/TimedEventExecutor.java +deleted file mode 100644 +index 157617933a772451f6c073d97afaf305769b4d40..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/TimedEventExecutor.java ++++ /dev/null +@@ -1,93 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import org.bukkit.Bukkit; +-import org.bukkit.event.Event; +-import org.bukkit.event.EventException; +-import org.bukkit.event.Listener; +-import org.bukkit.plugin.EventExecutor; +-import org.bukkit.plugin.Plugin; +- +-import java.lang.reflect.Method; +-import org.jetbrains.annotations.NotNull; +-import org.jetbrains.annotations.Nullable; +- +-/** +- * @deprecated Timings will be removed in the future +- */ +-@Deprecated(forRemoval = true) +-public class TimedEventExecutor implements EventExecutor { +- +- private final EventExecutor executor; +- private final Timing timings; +- +- /** +- * Wraps an event executor and associates a timing handler to it. +- * +- * @param executor Executor to wrap +- * @param plugin Owning plugin +- * @param method EventHandler method +- * @param eventClass Owning class +- */ +- public TimedEventExecutor(@NotNull EventExecutor executor, @NotNull Plugin plugin, @Nullable Method method, @NotNull Class eventClass) { +- this.executor = executor; +- String id; +- +- if (method == null) { +- if (executor.getClass().getEnclosingClass() != null) { // Oh Skript, how we love you +- method = executor.getClass().getEnclosingMethod(); +- } +- } +- +- if (method != null) { +- id = method.getDeclaringClass().getName(); +- } else { +- id = executor.getClass().getName(); +- } +- +- +- final String eventName = eventClass.getSimpleName(); +- boolean verbose = "BlockPhysicsEvent".equals(eventName); +- this.timings = Timings.ofSafe(plugin, (verbose ? "## " : "") + +- "Event: " + id + " (" + eventName + ")"); +- } +- +- @Override +- public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException { +- if (event.isAsynchronous() || !Timings.timingsEnabled || !Bukkit.isPrimaryThread()) { +- executor.execute(listener, event); +- return; +- } +- try (Timing ignored = timings.startTiming()){ +- executor.execute(listener, event); +- } +- } +- +- @Override +- @NotNull +- public String toString() { +- return "TimedEventExecutor['" + this.executor.toString() + "']"; +- } +-} +diff --git a/src/main/java/co/aikar/timings/Timing.java b/src/main/java/co/aikar/timings/Timing.java +deleted file mode 100644 +index 4195efcfe044618052bb03dea34a4fb2ca7c44f0..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/Timing.java ++++ /dev/null +@@ -1,86 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import org.jetbrains.annotations.NotNull; +-import org.jetbrains.annotations.Nullable; +- +-/** +- * Provides an ability to time sections of code within the Minecraft Server +- * +- * @deprecated Timings will be removed in the future +- */ +-@Deprecated(forRemoval = true) +-public interface Timing extends AutoCloseable { +- /** +- * Starts timing the execution until {@link #stopTiming()} is called. +- * +- * @return Timing +- */ +- @NotNull +- Timing startTiming(); +- +- /** +- *

Stops timing and records the data. Propagates the data up to group handlers.

+- * +- * Will automatically be called when this Timing is used with try-with-resources +- */ +- void stopTiming(); +- +- /** +- * Starts timing the execution until {@link #stopTiming()} is called. +- * +- * But only if we are on the primary thread. +- * +- * @return Timing +- */ +- @NotNull +- Timing startTimingIfSync(); +- +- /** +- *

Stops timing and records the data. Propagates the data up to group handlers.

+- * +- *

Will automatically be called when this Timing is used with try-with-resources

+- * +- * But only if we are on the primary thread. +- */ +- void stopTimingIfSync(); +- +- /** +- * @deprecated Doesn't do anything - Removed +- */ +- @Deprecated +- void abort(); +- +- /** +- * Used internally to get the actual backing Handler in the case of delegated Handlers +- * +- * @return TimingHandler +- */ +- @Nullable +- TimingHandler getTimingHandler(); +- +- @Override +- void close(); +-} +diff --git a/src/main/java/co/aikar/timings/TimingData.java b/src/main/java/co/aikar/timings/TimingData.java +deleted file mode 100644 +index a5d13a1e44edb861f45c83a9b4309fbf799d407d..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/TimingData.java ++++ /dev/null +@@ -1,122 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import java.util.List; +-import org.jetbrains.annotations.NotNull; +- +-import static co.aikar.util.JSONUtil.toArray; +- +-/** +- *

Lightweight object for tracking timing data

+- * +- * This is broken out to reduce memory usage +- */ +-class TimingData { +- private final int id; +- private int count = 0; +- private int lagCount = 0; +- private long totalTime = 0; +- private long lagTotalTime = 0; +- private int curTickCount = 0; +- private long curTickTotal = 0; +- +- TimingData(int id) { +- this.id = id; +- } +- +- private TimingData(TimingData data) { +- this.id = data.id; +- this.totalTime = data.totalTime; +- this.lagTotalTime = data.lagTotalTime; +- this.count = data.count; +- this.lagCount = data.lagCount; +- } +- +- void add(long diff) { +- ++curTickCount; +- curTickTotal += diff; +- } +- +- void processTick(boolean violated) { +- totalTime += curTickTotal; +- count += curTickCount; +- if (violated) { +- lagTotalTime += curTickTotal; +- lagCount += curTickCount; +- } +- curTickTotal = 0; +- curTickCount = 0; +- } +- +- void reset() { +- count = 0; +- lagCount = 0; +- curTickTotal = 0; +- curTickCount = 0; +- totalTime = 0; +- lagTotalTime = 0; +- } +- +- protected TimingData clone() { +- return new TimingData(this); +- } +- +- @NotNull +- List export() { +- List list = toArray( +- id, +- count, +- totalTime); +- if (lagCount > 0) { +- list.add(lagCount); +- list.add(lagTotalTime); +- } +- return list; +- } +- +- boolean hasData() { +- return count > 0; +- } +- +- long getTotalTime() { +- return totalTime; +- } +- +- int getCurTickCount() { +- return curTickCount; +- } +- +- void setCurTickCount(int curTickCount) { +- this.curTickCount = curTickCount; +- } +- +- long getCurTickTotal() { +- return curTickTotal; +- } +- +- void setCurTickTotal(long curTickTotal) { +- this.curTickTotal = curTickTotal; +- } +-} +diff --git a/src/main/java/co/aikar/timings/TimingHandler.java b/src/main/java/co/aikar/timings/TimingHandler.java +deleted file mode 100644 +index 199789d56d22fcb1b77ebd56805cc28aa5a5ab0a..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/TimingHandler.java ++++ /dev/null +@@ -1,226 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import co.aikar.util.LoadingIntMap; +-import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +- +-import java.util.ArrayDeque; +-import java.util.Deque; +-import java.util.concurrent.atomic.AtomicInteger; +-import java.util.logging.Level; +-import java.util.logging.Logger; +- +-import org.bukkit.Bukkit; +-import org.jetbrains.annotations.NotNull; +-import org.jetbrains.annotations.Nullable; +- +-class TimingHandler implements Timing { +- +- private static AtomicInteger idPool = new AtomicInteger(1); +- private static Deque TIMING_STACK = new ArrayDeque<>(); +- final int id = idPool.getAndIncrement(); +- +- final TimingIdentifier identifier; +- private final boolean verbose; +- +- private final Int2ObjectOpenHashMap children = new LoadingIntMap<>(TimingData::new); +- +- final TimingData record; +- private TimingHandler startParent; +- private final TimingHandler groupHandler; +- +- private long start = 0; +- private int timingDepth = 0; +- private boolean added; +- private boolean timed; +- private boolean enabled; +- +- TimingHandler(@NotNull TimingIdentifier id) { +- this.identifier = id; +- this.verbose = id.name.startsWith("##"); +- this.record = new TimingData(this.id); +- this.groupHandler = id.groupHandler; +- +- TimingIdentifier.getGroup(id.group).handlers.add(this); +- checkEnabled(); +- } +- +- final void checkEnabled() { +- enabled = Timings.timingsEnabled && (!verbose || Timings.verboseEnabled); +- } +- +- void processTick(boolean violated) { +- if (timingDepth != 0 || record.getCurTickCount() == 0) { +- timingDepth = 0; +- start = 0; +- return; +- } +- +- record.processTick(violated); +- for (TimingData handler : children.values()) { +- handler.processTick(violated); +- } +- } +- +- @NotNull +- @Override +- public Timing startTimingIfSync() { +- startTiming(); +- return this; +- } +- +- @Override +- public void stopTimingIfSync() { +- stopTiming(); +- } +- +- @NotNull +- public Timing startTiming() { +- if (!enabled || !Bukkit.isPrimaryThread()) { +- return this; +- } +- if (++timingDepth == 1) { +- startParent = TIMING_STACK.peekLast(); +- start = System.nanoTime(); +- } +- TIMING_STACK.addLast(this); +- return this; +- } +- +- public void stopTiming() { +- if (!enabled || timingDepth <= 0 || start == 0 || !Bukkit.isPrimaryThread()) { +- return; +- } +- +- popTimingStack(); +- if (--timingDepth == 0) { +- addDiff(System.nanoTime() - start, startParent); +- startParent = null; +- start = 0; +- } +- } +- +- private void popTimingStack() { +- TimingHandler last; +- while ((last = TIMING_STACK.removeLast()) != this) { +- last.timingDepth = 0; +- if ("Minecraft".equalsIgnoreCase(last.identifier.group)) { +- Logger.getGlobal().log(Level.SEVERE, "TIMING_STACK_CORRUPTION - Look above this for any errors and report this to Paper unless it has a plugin in the stack trace (" + last.identifier + " did not stopTiming)"); +- } else { +- Logger.getGlobal().log(Level.SEVERE, "TIMING_STACK_CORRUPTION - Report this to the plugin " + last.identifier.group + " (Look for errors above this in the logs) (" + last.identifier + " did not stopTiming)", new Throwable()); +- } +- +- boolean found = TIMING_STACK.contains(this); +- if (!found) { +- // We aren't even in the stack... Don't pop everything +- TIMING_STACK.addLast(last); +- break; +- } +- } +- } +- +- @Override +- public final void abort() { +- +- } +- +- void addDiff(long diff, @Nullable TimingHandler parent) { +- if (parent != null) { +- parent.children.get(id).add(diff); +- } +- +- record.add(diff); +- if (!added) { +- added = true; +- timed = true; +- TimingsManager.HANDLERS.add(this); +- } +- if (groupHandler != null) { +- groupHandler.addDiff(diff, parent); +- groupHandler.children.get(id).add(diff); +- } +- } +- +- /** +- * Reset this timer, setting all values to zero. +- */ +- void reset(boolean full) { +- record.reset(); +- if (full) { +- timed = false; +- } +- start = 0; +- timingDepth = 0; +- added = false; +- children.clear(); +- checkEnabled(); +- } +- +- @NotNull +- @Override +- public TimingHandler getTimingHandler() { +- return this; +- } +- +- @Override +- public boolean equals(Object o) { +- return (this == o); +- } +- +- @Override +- public int hashCode() { +- return id; +- } +- +- /** +- * This is simply for the Closeable interface so it can be used with try-with-resources () +- */ +- @Override +- public void close() { +- stopTimingIfSync(); +- } +- +- public boolean isSpecial() { +- return this == TimingsManager.FULL_SERVER_TICK || this == TimingsManager.TIMINGS_TICK; +- } +- +- boolean isTimed() { +- return timed; +- } +- +- public boolean isEnabled() { +- return enabled; +- } +- +- @NotNull +- TimingData[] cloneChildren() { +- final TimingData[] clonedChildren = new TimingData[children.size()]; +- int i = 0; +- for (TimingData child : children.values()) { +- clonedChildren[i++] = child.clone(); +- } +- return clonedChildren; +- } +-} +diff --git a/src/main/java/co/aikar/timings/TimingHistory.java b/src/main/java/co/aikar/timings/TimingHistory.java +deleted file mode 100644 +index 6f6eb1a2e6c8d49014a7ae44540ee282bae5200e..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/TimingHistory.java ++++ /dev/null +@@ -1,357 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import co.aikar.timings.TimingHistory.RegionData.RegionId; +-import com.google.common.base.Function; +-import com.google.common.collect.Sets; +-import org.bukkit.Bukkit; +-import org.bukkit.Chunk; +-import org.bukkit.Material; +-import org.bukkit.World; +-import org.bukkit.block.BlockState; +-import org.bukkit.entity.Entity; +-import org.bukkit.entity.EntityType; +-import org.bukkit.entity.Player; +-import co.aikar.util.LoadingMap; +-import co.aikar.util.MRUMapCache; +- +-import java.lang.management.ManagementFactory; +-import java.util.Collection; +-import java.util.EnumMap; +-import java.util.List; +-import java.util.Map; +-import java.util.Set; +-import org.jetbrains.annotations.NotNull; +-import org.jetbrains.annotations.Nullable; +- +-import static co.aikar.timings.TimingsManager.FULL_SERVER_TICK; +-import static co.aikar.timings.TimingsManager.MINUTE_REPORTS; +-import static co.aikar.util.JSONUtil.*; +- +-/** +- * Internal. +- * +- * @deprecated Timings will be removed in the future +- */ +-@Deprecated(forRemoval = true) +-@SuppressWarnings({"deprecation", "SuppressionAnnotation", "Convert2Lambda", "Anonymous2MethodRef"}) +-public class TimingHistory { +- public static long lastMinuteTime; +- public static long timedTicks; +- public static long playerTicks; +- public static long entityTicks; +- public static long tileEntityTicks; +- public static long activatedEntityTicks; +- private static int worldIdPool = 1; +- static Map worldMap = LoadingMap.newHashMap(new Function() { +- @NotNull +- @Override +- public Integer apply(@Nullable String input) { +- return worldIdPool++; +- } +- }); +- private final long endTime; +- private final long startTime; +- private final long totalTicks; +- private final long totalTime; // Represents all time spent running the server this history +- private final MinuteReport[] minuteReports; +- +- private final TimingHistoryEntry[] entries; +- final Set tileEntityTypeSet = Sets.newHashSet(); +- final Set entityTypeSet = Sets.newHashSet(); +- private final Map worlds; +- +- TimingHistory() { +- this.endTime = System.currentTimeMillis() / 1000; +- this.startTime = TimingsManager.historyStart / 1000; +- if (timedTicks % 1200 != 0 || MINUTE_REPORTS.isEmpty()) { +- this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size() + 1]); +- this.minuteReports[this.minuteReports.length - 1] = new MinuteReport(); +- } else { +- this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size()]); +- } +- long ticks = 0; +- for (MinuteReport mp : this.minuteReports) { +- ticks += mp.ticksRecord.timed; +- } +- this.totalTicks = ticks; +- this.totalTime = FULL_SERVER_TICK.record.getTotalTime(); +- this.entries = new TimingHistoryEntry[TimingsManager.HANDLERS.size()]; +- +- int i = 0; +- for (TimingHandler handler : TimingsManager.HANDLERS) { +- entries[i++] = new TimingHistoryEntry(handler); +- } +- +- // Information about all loaded chunks/entities +- //noinspection unchecked +- this.worlds = toObjectMapper(Bukkit.getWorlds(), new Function() { +- @NotNull +- @Override +- public JSONPair apply(World world) { +- Map regions = LoadingMap.newHashMap(RegionData.LOADER); +- +- for (Chunk chunk : world.getLoadedChunks()) { +- RegionData data = regions.get(new RegionId(chunk.getX(), chunk.getZ())); +- +- for (Entity entity : chunk.getEntities()) { +- if (entity == null) { +- Bukkit.getLogger().warning("Null entity detected in chunk at position x: " + chunk.getX() + ", z: " + chunk.getZ()); +- continue; +- } +- +- data.entityCounts.get(entity.getType()).increment(); +- } +- +- for (BlockState tileEntity : chunk.getTileEntities(false)) { +- if (tileEntity == null) { +- Bukkit.getLogger().warning("Null tileentity detected in chunk at position x: " + chunk.getX() + ", z: " + chunk.getZ()); +- continue; +- } +- +- data.tileEntityCounts.get(tileEntity.getBlock().getType()).increment(); +- } +- } +- return pair( +- worldMap.get(world.getName()), +- toArrayMapper(regions.values(),new Function() { +- @NotNull +- @Override +- public Object apply(RegionData input) { +- return toArray( +- input.regionId.x, +- input.regionId.z, +- toObjectMapper(input.entityCounts.entrySet(), +- new Function, JSONPair>() { +- @NotNull +- @Override +- public JSONPair apply(Map.Entry entry) { +- entityTypeSet.add(entry.getKey()); +- return pair( +- String.valueOf(entry.getKey().ordinal()), +- entry.getValue().count() +- ); +- } +- } +- ), +- toObjectMapper( +- input.tileEntityCounts.entrySet(), +- entry -> { +- tileEntityTypeSet.add(entry.getKey()); +- return pair( +- String.valueOf(entry.getKey().ordinal()), +- entry.getValue().count() +- ); +- } +- ) +- ); +- } +- }) +- ); +- } +- }); +- } +- static class RegionData { +- final RegionId regionId; +- @SuppressWarnings("Guava") +- static Function LOADER = new Function() { +- @NotNull +- @Override +- public RegionData apply(@NotNull RegionId id) { +- return new RegionData(id); +- } +- }; +- RegionData(@NotNull RegionId id) { +- this.regionId = id; +- } +- +- @Override +- public boolean equals(Object o) { +- if (this == o) { +- return true; +- } +- if (o == null || getClass() != o.getClass()) { +- return false; +- } +- +- RegionData that = (RegionData) o; +- +- return regionId.equals(that.regionId); +- +- } +- +- @Override +- public int hashCode() { +- return regionId.hashCode(); +- } +- +- @SuppressWarnings("unchecked") +- final Map entityCounts = MRUMapCache.of(LoadingMap.of( +- new EnumMap(EntityType.class), k -> new Counter() +- )); +- @SuppressWarnings("unchecked") +- final Map tileEntityCounts = MRUMapCache.of(LoadingMap.of( +- new EnumMap(Material.class), k -> new Counter() +- )); +- +- static class RegionId { +- final int x, z; +- final long regionId; +- RegionId(int x, int z) { +- this.x = x >> 5 << 5; +- this.z = z >> 5 << 5; +- this.regionId = ((long) (this.x) << 32) + (this.z >> 5 << 5) - Integer.MIN_VALUE; +- } +- +- @Override +- public boolean equals(Object o) { +- if (this == o) return true; +- if (o == null || getClass() != o.getClass()) return false; +- +- RegionId regionId1 = (RegionId) o; +- +- return regionId == regionId1.regionId; +- +- } +- +- @Override +- public int hashCode() { +- return (int) (regionId ^ (regionId >>> 32)); +- } +- } +- } +- static void resetTicks(boolean fullReset) { +- if (fullReset) { +- // Non full is simply for 1 minute reports +- timedTicks = 0; +- } +- lastMinuteTime = System.nanoTime(); +- playerTicks = 0; +- tileEntityTicks = 0; +- entityTicks = 0; +- activatedEntityTicks = 0; +- } +- +- @NotNull +- Object export() { +- return createObject( +- pair("s", startTime), +- pair("e", endTime), +- pair("tk", totalTicks), +- pair("tm", totalTime), +- pair("w", worlds), +- pair("h", toArrayMapper(entries, new Function() { +- @Nullable +- @Override +- public Object apply(TimingHistoryEntry entry) { +- TimingData record = entry.data; +- if (!record.hasData()) { +- return null; +- } +- return entry.export(); +- } +- })), +- pair("mp", toArrayMapper(minuteReports, new Function() { +- @NotNull +- @Override +- public Object apply(MinuteReport input) { +- return input.export(); +- } +- })) +- ); +- } +- +- static class MinuteReport { +- final long time = System.currentTimeMillis() / 1000; +- +- final TicksRecord ticksRecord = new TicksRecord(); +- final PingRecord pingRecord = new PingRecord(); +- final TimingData fst = TimingsManager.FULL_SERVER_TICK.minuteData.clone(); +- final double tps = 1E9 / ( System.nanoTime() - lastMinuteTime ) * ticksRecord.timed; +- final double usedMemory = TimingsManager.FULL_SERVER_TICK.avgUsedMemory; +- final double freeMemory = TimingsManager.FULL_SERVER_TICK.avgFreeMemory; +- final double loadAvg = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage(); +- +- @NotNull +- List export() { +- return toArray( +- time, +- Math.round(tps * 100D) / 100D, +- Math.round(pingRecord.avg * 100D) / 100D, +- fst.export(), +- toArray(ticksRecord.timed, +- ticksRecord.player, +- ticksRecord.entity, +- ticksRecord.activatedEntity, +- ticksRecord.tileEntity +- ), +- usedMemory, +- freeMemory, +- loadAvg +- ); +- } +- } +- +- private static class TicksRecord { +- final long timed; +- final long player; +- final long entity; +- final long tileEntity; +- final long activatedEntity; +- +- TicksRecord() { +- timed = timedTicks - (TimingsManager.MINUTE_REPORTS.size() * 1200); +- player = playerTicks; +- entity = entityTicks; +- tileEntity = tileEntityTicks; +- activatedEntity = activatedEntityTicks; +- } +- +- } +- +- private static class PingRecord { +- final double avg; +- +- PingRecord() { +- final Collection onlinePlayers = Bukkit.getOnlinePlayers(); +- int totalPing = 0; +- for (Player player : onlinePlayers) { +- totalPing += player.spigot().getPing(); +- } +- avg = onlinePlayers.isEmpty() ? 0 : totalPing / onlinePlayers.size(); +- } +- } +- +- +- private static class Counter { +- private int count = 0; +- public int increment() { +- return ++count; +- } +- public int count() { +- return count; +- } +- } +-} +diff --git a/src/main/java/co/aikar/timings/TimingHistoryEntry.java b/src/main/java/co/aikar/timings/TimingHistoryEntry.java +deleted file mode 100644 +index 86d5ac6bd0d7d0003688761aceb3f3343575319f..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/TimingHistoryEntry.java ++++ /dev/null +@@ -1,58 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import com.google.common.base.Function; +- +-import java.util.List; +-import org.jetbrains.annotations.NotNull; +- +-import static co.aikar.util.JSONUtil.toArrayMapper; +- +-class TimingHistoryEntry { +- final TimingData data; +- private final TimingData[] children; +- +- TimingHistoryEntry(@NotNull TimingHandler handler) { +- this.data = handler.record.clone(); +- children = handler.cloneChildren(); +- } +- +- @NotNull +- List export() { +- List result = data.export(); +- if (children.length > 0) { +- result.add( +- toArrayMapper(children, new Function() { +- @NotNull +- @Override +- public Object apply(TimingData child) { +- return child.export(); +- } +- }) +- ); +- } +- return result; +- } +-} +diff --git a/src/main/java/co/aikar/timings/TimingIdentifier.java b/src/main/java/co/aikar/timings/TimingIdentifier.java +deleted file mode 100644 +index df142a89b8c43acb81eb383eac0ef048a1f49a6e..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/TimingIdentifier.java ++++ /dev/null +@@ -1,116 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import co.aikar.util.LoadingMap; +- +-import java.util.ArrayList; +-import java.util.Collections; +-import java.util.List; +-import java.util.Map; +-import java.util.Objects; +-import java.util.concurrent.ConcurrentHashMap; +-import java.util.concurrent.atomic.AtomicInteger; +-import org.jetbrains.annotations.NotNull; +-import org.jetbrains.annotations.Nullable; +- +-/** +- *

Used as a basis for fast HashMap key comparisons for the Timing Map.

+- * +- * This class uses interned strings giving us the ability to do an identity check instead of equals() on the strings +- */ +-final class TimingIdentifier { +- /** +- * Holds all groups. Autoloads on request for a group by name. +- */ +- static final Map GROUP_MAP = LoadingMap.of(new ConcurrentHashMap<>(64, .5F), TimingGroup::new); +- private static final TimingGroup DEFAULT_GROUP = getGroup("Minecraft"); +- final String group; +- final String name; +- final TimingHandler groupHandler; +- private final int hashCode; +- +- TimingIdentifier(@Nullable String group, @NotNull String name, @Nullable Timing groupHandler) { +- this.group = group != null ? group: DEFAULT_GROUP.name; +- this.name = name; +- this.groupHandler = groupHandler != null ? groupHandler.getTimingHandler() : null; +- this.hashCode = (31 * this.group.hashCode()) + this.name.hashCode(); +- } +- +- @NotNull +- static TimingGroup getGroup(@Nullable String groupName) { +- if (groupName == null) { +- //noinspection ConstantConditions +- return DEFAULT_GROUP; +- } +- +- return GROUP_MAP.get(groupName); +- } +- +- @Override +- public boolean equals(Object o) { +- if (o == null) { +- return false; +- } +- +- TimingIdentifier that = (TimingIdentifier) o; +- return Objects.equals(group, that.group) && Objects.equals(name, that.name); +- } +- +- @Override +- public int hashCode() { +- return hashCode; +- } +- +- @Override +- public String toString() { +- return "TimingIdentifier{id=" + group + ":" + name +'}'; +- } +- +- static class TimingGroup { +- +- private static AtomicInteger idPool = new AtomicInteger(1); +- final int id = idPool.getAndIncrement(); +- +- final String name; +- final List handlers = Collections.synchronizedList(new ArrayList<>(64)); +- +- private TimingGroup(String name) { +- this.name = name; +- } +- +- @Override +- public boolean equals(Object o) { +- if (this == o) return true; +- if (o == null || getClass() != o.getClass()) return false; +- TimingGroup that = (TimingGroup) o; +- return id == that.id; +- } +- +- @Override +- public int hashCode() { +- return id; +- } +- } +-} +diff --git a/src/main/java/co/aikar/timings/Timings.java b/src/main/java/co/aikar/timings/Timings.java +deleted file mode 100644 +index 7760c779e97a0e95d5c3d255b25d949ac20df235..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/Timings.java ++++ /dev/null +@@ -1,332 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import com.google.common.base.Preconditions; +-import com.google.common.collect.EvictingQueue; +-import com.google.common.collect.Lists; +-import net.kyori.adventure.text.Component; +-import net.kyori.adventure.text.event.ClickEvent; +-import net.kyori.adventure.text.format.TextColor; +-import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +-import org.bukkit.Bukkit; +-import org.bukkit.command.CommandSender; +-import org.bukkit.plugin.Plugin; +- +-import java.util.List; +-import java.util.Queue; +-import java.util.logging.Level; +-import org.jetbrains.annotations.NotNull; +-import org.jetbrains.annotations.Nullable; +- +-/** +- * @deprecated Timings will be removed in the future +- */ +-@Deprecated(forRemoval = true) +-@SuppressWarnings({"UnusedDeclaration", "WeakerAccess", "SameParameterValue"}) +-public final class Timings { +- +- final static List requestingReport = Lists.newArrayList(); +- private static final int MAX_HISTORY_FRAMES = 12; +- public static final Timing NULL_HANDLER = new NullTimingHandler(); +- static boolean timingsEnabled = false; +- static boolean verboseEnabled = false; +- private static int historyInterval = -1; +- private static int historyLength = -1; +- private static boolean warnedAboutDeprecationOnEnable; +- +- private Timings() {} +- +- /** +- * Returns a Timing for a plugin corresponding to a name. +- * +- * @param plugin Plugin to own the Timing +- * @param name Name of Timing +- * @return Handler +- */ +- @NotNull +- public static Timing of(@NotNull Plugin plugin, @NotNull String name) { +- Timing pluginHandler = null; +- if (plugin != null) { +- pluginHandler = ofSafe(plugin.getName(), "Combined Total", TimingsManager.PLUGIN_GROUP_HANDLER); +- } +- return of(plugin, name, pluginHandler); +- } +- +- /** +- *

Returns a handler that has a groupHandler timer handler. Parent timers should not have their +- * start/stop methods called directly, as the children will call it for you.

+- * +- * Parent Timers are used to group multiple subsections together and get a summary of them combined +- * Parent Handler can not be changed after first call +- * +- * @param plugin Plugin to own the Timing +- * @param name Name of Timing +- * @param groupHandler Parent handler to mirror .start/stop calls to +- * @return Timing Handler +- */ +- @NotNull +- public static Timing of(@NotNull Plugin plugin, @NotNull String name, @Nullable Timing groupHandler) { +- Preconditions.checkNotNull(plugin, "Plugin can not be null"); +- Bukkit.getLogger().warning(String.format("Plugin '%s' is creating timing '%s' - this is deprecated behavior, please report it to the authors: %s", plugin.getName(), name, String.join(", ", plugin.getDescription().getAuthors()))); +- return TimingsManager.getHandler(plugin.getName(), name, groupHandler); +- } +- +- /** +- * Returns a Timing object after starting it, useful for Java7 try-with-resources. +- * +- * try (Timing ignored = Timings.ofStart(plugin, someName)) { +- * // timed section +- * } +- * +- * @param plugin Plugin to own the Timing +- * @param name Name of Timing +- * @return Timing Handler +- */ +- @NotNull +- public static Timing ofStart(@NotNull Plugin plugin, @NotNull String name) { +- return ofStart(plugin, name, null); +- } +- +- /** +- * Returns a Timing object after starting it, useful for Java7 try-with-resources. +- * +- * try (Timing ignored = Timings.ofStart(plugin, someName, groupHandler)) { +- * // timed section +- * } +- * +- * @param plugin Plugin to own the Timing +- * @param name Name of Timing +- * @param groupHandler Parent handler to mirror .start/stop calls to +- * @return Timing Handler +- */ +- @NotNull +- public static Timing ofStart(@NotNull Plugin plugin, @NotNull String name, @Nullable Timing groupHandler) { +- Timing timing = of(plugin, name, groupHandler); +- timing.startTiming(); +- return timing; +- } +- +- /** +- * Gets whether or not the Spigot Timings system is enabled +- * +- * @return Enabled or not +- */ +- public static boolean isTimingsEnabled() { +- return timingsEnabled; +- } +- +- /** +- *

Sets whether or not the Spigot Timings system should be enabled

+- * +- * Calling this will reset timing data. +- * +- * @param enabled Should timings be reported +- */ +- public static void setTimingsEnabled(boolean enabled) { +- enabled = false; // Folia - region threading - disable timings +- timingsEnabled = enabled; +- warnAboutDeprecationOnEnable(); +- reset(); +- } +- +- private static void warnAboutDeprecationOnEnable() { +- if ((true || timingsEnabled) && !warnedAboutDeprecationOnEnable) { // Folia +- Bukkit.getLogger().warning(PlainTextComponentSerializer.plainText().serialize(deprecationMessage())); +- warnedAboutDeprecationOnEnable = true; +- } +- } +- +- public static Component deprecationMessage() { +- return Component.text() +- .color(TextColor.color(0xf39195)) // Folia +- .append(Component.text("[!] The timings system cannot be enabled on Folia, and has been scheduled for removal from Paper in the future.")) // Folia +- .append(Component.newline()) +- .append(Component.text(" We recommend migrating to the spark profiler.")) +- .append(Component.newline()) +- .append( +- Component.text(" For more information please visit: ") +- .append( +- Component.text() +- .content("https://github.com/PaperMC/Paper/discussions/10565") +- .clickEvent(ClickEvent.openUrl("https://github.com/PaperMC/Paper/discussions/10565"))) +- ) +- .build(); +- } +- +- /** +- *

Sets whether or not the Timings should monitor at Verbose level.

+- * +- *

When Verbose is disabled, high-frequency timings will not be available.

+- * +- * @return Enabled or not +- */ +- public static boolean isVerboseTimingsEnabled() { +- return verboseEnabled; +- } +- +- /** +- *

Sets whether or not the Timings should monitor at Verbose level.

+- * +- * When Verbose is disabled, high-frequency timings will not be available. +- * Calling this will reset timing data. +- * +- * @param enabled Should high-frequency timings be reported +- */ +- public static void setVerboseTimingsEnabled(boolean enabled) { +- verboseEnabled = enabled; +- TimingsManager.needsRecheckEnabled = true; +- } +- +- /** +- *

Gets the interval between Timing History report generation.

+- * +- * Defaults to 5 minutes (6000 ticks) +- * +- * @return Interval in ticks +- */ +- public static int getHistoryInterval() { +- return historyInterval; +- } +- +- /** +- *

Sets the interval between Timing History report generations.

+- * +- *

Defaults to 5 minutes (6000 ticks)

+- * +- * This will recheck your history length, so lowering this value will lower your +- * history length if you need more than 60 history windows. +- * +- * @param interval Interval in ticks +- */ +- public static void setHistoryInterval(int interval) { +- historyInterval = Math.max(20*60, interval); +- // Recheck the history length with the new Interval +- if (historyLength != -1) { +- setHistoryLength(historyLength); +- } +- } +- +- /** +- * Gets how long in ticks Timings history is kept for the server. +- * +- * Defaults to 1 hour (72000 ticks) +- * +- * @return Duration in Ticks +- */ +- public static int getHistoryLength() { +- return historyLength; +- } +- +- /** +- * Sets how long Timing History reports are kept for the server. +- * +- * Defaults to 1 hours(72000 ticks) +- * +- * This value is capped at a maximum of getHistoryInterval() * MAX_HISTORY_FRAMES (12) +- * +- * Will not reset Timing Data but may truncate old history if the new length is less than old length. +- * +- * @param length Duration in ticks +- */ +- public static void setHistoryLength(int length) { +- // Cap at 12 History Frames, 1 hour at 5 minute frames. +- int maxLength = historyInterval * MAX_HISTORY_FRAMES; +- // For special cases of servers with special permission to bypass the max. +- // This max helps keep data file sizes reasonable for processing on Aikar's Timing parser side. +- // Setting this will not help you bypass the max unless Aikar has added an exception on the API side. +- if (System.getProperty("timings.bypassMax") != null) { +- maxLength = Integer.MAX_VALUE; +- } +- historyLength = Math.max(Math.min(maxLength, length), historyInterval); +- Queue oldQueue = TimingsManager.HISTORY; +- int frames = (getHistoryLength() / getHistoryInterval()); +- if (length > maxLength) { +- Bukkit.getLogger().log(Level.WARNING, "Timings Length too high. Requested " + length + ", max is " + maxLength + ". To get longer history, you must increase your interval. Set Interval to " + Math.ceil(length / MAX_HISTORY_FRAMES) + " to achieve this length."); +- } +- TimingsManager.HISTORY = EvictingQueue.create(frames); +- TimingsManager.HISTORY.addAll(oldQueue); +- } +- +- /** +- * Resets all Timing Data +- */ +- public static void reset() { +- TimingsManager.reset(); +- } +- +- /** +- * Generates a report and sends it to the specified command sender. +- * +- * If sender is null, ConsoleCommandSender will be used. +- * @param sender The sender to send to, or null to use the ConsoleCommandSender +- */ +- public static void generateReport(@Nullable CommandSender sender) { +- if (sender == null) { +- sender = Bukkit.getConsoleSender(); +- } +- requestingReport.add(sender); +- } +- +- /** +- * Generates a report and sends it to the specified listener. +- * Use with {@link org.bukkit.command.BufferedCommandSender} to get full response when done! +- * @param sender The listener to send responses too. +- */ +- public static void generateReport(@NotNull TimingsReportListener sender) { +- Preconditions.checkNotNull(sender); +- requestingReport.add(sender); +- } +- +- /* +- ================= +- Protected API: These are for internal use only in Bukkit/CraftBukkit +- These do not have isPrimaryThread() checks in the startTiming/stopTiming +- ================= +- */ +- @NotNull +- static TimingHandler ofSafe(@NotNull String name) { +- return ofSafe(null, name, null); +- } +- +- @NotNull +- static Timing ofSafe(@Nullable Plugin plugin, @NotNull String name) { +- Timing pluginHandler = null; +- if (plugin != null) { +- pluginHandler = ofSafe(plugin.getName(), "Combined Total", TimingsManager.PLUGIN_GROUP_HANDLER); +- } +- return ofSafe(plugin != null ? plugin.getName() : "Minecraft - Invalid Plugin", name, pluginHandler); +- } +- +- @NotNull +- static TimingHandler ofSafe(@NotNull String name, @Nullable Timing groupHandler) { +- return ofSafe(null, name, groupHandler); +- } +- +- @NotNull +- static TimingHandler ofSafe(@Nullable String groupName, @NotNull String name, @Nullable Timing groupHandler) { +- return TimingsManager.getHandler(groupName, name, groupHandler); +- } +-} +- +diff --git a/src/main/java/co/aikar/timings/TimingsCommand.java b/src/main/java/co/aikar/timings/TimingsCommand.java +deleted file mode 100644 +index b83e5ff7ada8771fdf27ba9807c77ba6a4ce12da..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/TimingsCommand.java ++++ /dev/null +@@ -1,127 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import com.google.common.base.Preconditions; +-import com.google.common.collect.ImmutableList; +-import net.kyori.adventure.text.format.NamedTextColor; +-import org.bukkit.command.CommandSender; +-import org.bukkit.command.defaults.BukkitCommand; +-import org.bukkit.util.StringUtil; +- +-import java.util.ArrayList; +-import java.util.List; +-import org.jetbrains.annotations.NotNull; +- +-import static net.kyori.adventure.text.Component.text; +- +-/** +- * @deprecated Timings will be removed in the future +- */ +-@Deprecated(forRemoval = true) +-public class TimingsCommand extends BukkitCommand { +- private static final List TIMINGS_SUBCOMMANDS = ImmutableList.of("report", "reset", "on", "off", "paste", "verbon", "verboff"); +- private long lastResetAttempt = 0; +- +- public TimingsCommand(@NotNull String name) { +- super(name); +- this.description = "Manages Spigot Timings data to see performance of the server."; +- this.usageMessage = "/timings "; +- this.setPermission("bukkit.command.timings"); +- } +- +- @Override +- public boolean execute(@NotNull CommandSender sender, @NotNull String currentAlias, @NotNull String[] args) { +- if (!testPermission(sender)) { +- return true; +- } +- if (true) { +- sender.sendMessage(Timings.deprecationMessage()); +- return true; +- } +- if (args.length < 1) { +- sender.sendMessage(text("Usage: " + this.usageMessage, NamedTextColor.RED)); +- return true; +- } +- final String arg = args[0]; +- if ("on".equalsIgnoreCase(arg)) { +- Timings.setTimingsEnabled(true); +- sender.sendMessage(text("Enabled Timings & Reset")); +- return true; +- } else if ("off".equalsIgnoreCase(arg)) { +- Timings.setTimingsEnabled(false); +- sender.sendMessage(text("Disabled Timings")); +- return true; +- } +- +- if (!Timings.isTimingsEnabled()) { +- sender.sendMessage(text("Please enable timings by typing /timings on")); +- return true; +- } +- +- long now = System.currentTimeMillis(); +- if ("verbon".equalsIgnoreCase(arg)) { +- Timings.setVerboseTimingsEnabled(true); +- sender.sendMessage(text("Enabled Verbose Timings")); +- return true; +- } else if ("verboff".equalsIgnoreCase(arg)) { +- Timings.setVerboseTimingsEnabled(false); +- sender.sendMessage(text("Disabled Verbose Timings")); +- return true; +- } else if ("reset".equalsIgnoreCase(arg)) { +- if (now - lastResetAttempt < 30000) { +- TimingsManager.reset(); +- sender.sendMessage(text("Timings reset. Please wait 5-10 minutes before using /timings report.", NamedTextColor.RED)); +- } else { +- lastResetAttempt = now; +- sender.sendMessage(text("WARNING: Timings v2 should not be reset. If you are experiencing lag, please wait 3 minutes and then issue a report. The best timings will include 10+ minutes, with data before and after your lag period. If you really want to reset, run this command again within 30 seconds.", NamedTextColor.RED)); +- } +- } else if ( +- "paste".equalsIgnoreCase(arg) || +- "report".equalsIgnoreCase(arg) || +- "get".equalsIgnoreCase(arg) || +- "merged".equalsIgnoreCase(arg) || +- "separate".equalsIgnoreCase(arg) +- ) { +- Timings.generateReport(sender); +- } else { +- sender.sendMessage(text("Usage: " + this.usageMessage, NamedTextColor.RED)); +- } +- return true; +- } +- +- @NotNull +- @Override +- public List tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) { +- Preconditions.checkNotNull(sender, "Sender cannot be null"); +- Preconditions.checkNotNull(args, "Arguments cannot be null"); +- Preconditions.checkNotNull(alias, "Alias cannot be null"); +- +- if (args.length == 1) { +- return StringUtil.copyPartialMatches(args[0], TIMINGS_SUBCOMMANDS, +- new ArrayList(TIMINGS_SUBCOMMANDS.size())); +- } +- return ImmutableList.of(); +- } +-} +diff --git a/src/main/java/co/aikar/timings/TimingsManager.java b/src/main/java/co/aikar/timings/TimingsManager.java +deleted file mode 100644 +index e72ad05abada04426e32a73d02b21cb69079d268..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/TimingsManager.java ++++ /dev/null +@@ -1,192 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import co.aikar.util.LoadingMap; +-import com.google.common.collect.EvictingQueue; +-import org.bukkit.Bukkit; +-import org.bukkit.Server; +-import org.bukkit.command.Command; +-import org.bukkit.plugin.Plugin; +-import org.bukkit.plugin.java.PluginClassLoader; +- +-import java.util.ArrayList; +-import java.util.List; +-import java.util.Map; +-import java.util.concurrent.ConcurrentHashMap; +-import java.util.logging.Level; +-import org.jetbrains.annotations.NotNull; +-import org.jetbrains.annotations.Nullable; +- +-/** +- * @deprecated Timings will be removed in the future +- */ +-@Deprecated(forRemoval = true) +-public final class TimingsManager { +- static final Map TIMING_MAP = LoadingMap.of( +- new ConcurrentHashMap<>(4096, .5F), TimingHandler::new +- ); +- public static final FullServerTickHandler FULL_SERVER_TICK = new FullServerTickHandler(); +- public static final TimingHandler TIMINGS_TICK = Timings.ofSafe("Timings Tick", FULL_SERVER_TICK); +- public static final Timing PLUGIN_GROUP_HANDLER = Timings.ofSafe("Plugins"); +- public static String url = "https://timings.aikar.co/"; +- public static List hiddenConfigs = new ArrayList(); +- public static boolean privacy = false; +- +- static final List HANDLERS = new ArrayList<>(1024); +- static final List MINUTE_REPORTS = new ArrayList<>(64); +- +- static EvictingQueue HISTORY = EvictingQueue.create(12); +- static long timingStart = 0; +- static long historyStart = 0; +- static boolean needsFullReset = false; +- static boolean needsRecheckEnabled = false; +- +- private TimingsManager() {} +- +- /** +- * Resets all timing data on the next tick +- */ +- static void reset() { +- needsFullReset = true; +- } +- +- /** +- * Ticked every tick by CraftBukkit to count the number of times a timer +- * caused TPS loss. +- */ +- static void tick() { +- if (Timings.timingsEnabled) { +- boolean violated = FULL_SERVER_TICK.isViolated(); +- +- for (TimingHandler handler : HANDLERS) { +- if (handler.isSpecial()) { +- // We manually call this +- continue; +- } +- handler.processTick(violated); +- } +- +- TimingHistory.playerTicks += Bukkit.getOnlinePlayers().size(); +- TimingHistory.timedTicks++; +- // Generate TPS/Ping/Tick reports every minute +- } +- } +- static void stopServer() { +- Timings.timingsEnabled = false; +- recheckEnabled(); +- } +- static void recheckEnabled() { +- synchronized (TIMING_MAP) { +- for (TimingHandler timings : TIMING_MAP.values()) { +- timings.checkEnabled(); +- } +- } +- needsRecheckEnabled = false; +- } +- static void resetTimings() { +- if (needsFullReset) { +- // Full resets need to re-check every handlers enabled state +- // Timing map can be modified from async so we must sync on it. +- synchronized (TIMING_MAP) { +- for (TimingHandler timings : TIMING_MAP.values()) { +- timings.reset(true); +- } +- } +- Bukkit.getLogger().log(Level.INFO, "Timings Reset"); +- HISTORY.clear(); +- needsFullReset = false; +- needsRecheckEnabled = false; +- timingStart = System.currentTimeMillis(); +- } else { +- // Soft resets only need to act on timings that have done something +- // Handlers can only be modified on main thread. +- for (TimingHandler timings : HANDLERS) { +- timings.reset(false); +- } +- } +- +- HANDLERS.clear(); +- MINUTE_REPORTS.clear(); +- +- TimingHistory.resetTicks(true); +- historyStart = System.currentTimeMillis(); +- } +- +- @NotNull +- static TimingHandler getHandler(@Nullable String group, @NotNull String name, @Nullable Timing parent) { +- return TIMING_MAP.get(new TimingIdentifier(group, name, parent)); +- } +- +- +- /** +- *

Due to access restrictions, we need a helper method to get a Command TimingHandler with String group

+- * +- * Plugins should never call this +- * +- * @param pluginName Plugin this command is associated with +- * @param command Command to get timings for +- * @return TimingHandler +- */ +- @NotNull +- public static Timing getCommandTiming(@Nullable String pluginName, @NotNull Command command) { +- Plugin plugin = null; +- final Server server = Bukkit.getServer(); +- if (!( server == null || pluginName == null || +- "minecraft".equals(pluginName) || "bukkit".equals(pluginName) || +- "spigot".equalsIgnoreCase(pluginName) || "paper".equals(pluginName) +- )) { +- plugin = server.getPluginManager().getPlugin(pluginName); +- } +- if (plugin == null) { +- // Plugin is passing custom fallback prefix, try to look up by class loader +- plugin = getPluginByClassloader(command.getClass()); +- } +- if (plugin == null) { +- return Timings.ofSafe("Command: " + pluginName + ":" + command.getTimingName()); +- } +- +- return Timings.ofSafe(plugin, "Command: " + pluginName + ":" + command.getTimingName()); +- } +- +- /** +- * Looks up the class loader for the specified class, and if it is a PluginClassLoader, return the +- * Plugin that created this class. +- * +- * @param clazz Class to check +- * @return Plugin if created by a plugin +- */ +- @Nullable +- public static Plugin getPluginByClassloader(@Nullable Class clazz) { +- if (clazz == null) { +- return null; +- } +- final ClassLoader classLoader = clazz.getClassLoader(); +- if (classLoader instanceof PluginClassLoader) { +- PluginClassLoader pluginClassLoader = (PluginClassLoader) classLoader; +- return pluginClassLoader.getPlugin(); +- } +- return null; +- } +-} +diff --git a/src/main/java/co/aikar/timings/TimingsReportListener.java b/src/main/java/co/aikar/timings/TimingsReportListener.java +deleted file mode 100644 +index df066d6f8d55afbc0c1897c486d638657a5f8df9..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/TimingsReportListener.java ++++ /dev/null +@@ -1,90 +0,0 @@ +-package co.aikar.timings; +- +-import com.google.common.base.Preconditions; +-import com.google.common.collect.Lists; +-import org.bukkit.Bukkit; +-import org.bukkit.command.CommandSender; +-import org.bukkit.command.ConsoleCommandSender; +-import org.bukkit.command.MessageCommandSender; +-import org.bukkit.command.RemoteConsoleCommandSender; +-import org.jetbrains.annotations.NotNull; +-import org.jetbrains.annotations.Nullable; +- +-import java.util.List; +- +-/** +- * @deprecated Timings will be removed in the future +- */ +-@Deprecated(forRemoval = true) +-@SuppressWarnings("WeakerAccess") +-public class TimingsReportListener implements net.kyori.adventure.audience.ForwardingAudience, MessageCommandSender { +- private final List senders; +- private final Runnable onDone; +- private String timingsURL; +- +- public TimingsReportListener(@NotNull CommandSender senders) { +- this(senders, null); +- } +- public TimingsReportListener(@NotNull CommandSender sender, @Nullable Runnable onDone) { +- this(Lists.newArrayList(sender), onDone); +- } +- public TimingsReportListener(@NotNull List senders) { +- this(senders, null); +- } +- public TimingsReportListener(@NotNull List senders, @Nullable Runnable onDone) { +- Preconditions.checkNotNull(senders); +- Preconditions.checkArgument(!senders.isEmpty(), "senders is empty"); +- +- this.senders = Lists.newArrayList(senders); +- this.onDone = onDone; +- } +- +- @Nullable +- public String getTimingsURL() { +- return timingsURL; +- } +- +- public void done() { +- done(null); +- } +- +- public void done(@Nullable String url) { +- this.timingsURL = url; +- if (onDone != null) { +- onDone.run(); +- } +- for (CommandSender sender : senders) { +- if (sender instanceof TimingsReportListener) { +- ((TimingsReportListener) sender).done(); +- } +- } +- } +- +- @Override +- public void sendMessage(final @NotNull net.kyori.adventure.identity.Identity source, final @NotNull net.kyori.adventure.text.Component message, final @NotNull net.kyori.adventure.audience.MessageType type) { +- net.kyori.adventure.audience.ForwardingAudience.super.sendMessage(source, message, type); +- } +- +- @NotNull +- @Override +- public Iterable audiences() { +- return this.senders; +- } +- +- @Override +- public void sendMessage(@NotNull String message) { +- senders.forEach((sender) -> sender.sendMessage(message)); +- } +- +- public void addConsoleIfNeeded() { +- boolean hasConsole = false; +- for (CommandSender sender : this.senders) { +- if (sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) { +- hasConsole = true; +- } +- } +- if (!hasConsole) { +- this.senders.add(Bukkit.getConsoleSender()); +- } +- } +-} +diff --git a/src/main/java/co/aikar/timings/UnsafeTimingHandler.java b/src/main/java/co/aikar/timings/UnsafeTimingHandler.java +deleted file mode 100644 +index 632c4961515f5052551f841cfa840e60bba7a257..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/UnsafeTimingHandler.java ++++ /dev/null +@@ -1,53 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import org.bukkit.Bukkit; +-import org.jetbrains.annotations.NotNull; +- +-class UnsafeTimingHandler extends TimingHandler { +- +- UnsafeTimingHandler(@NotNull TimingIdentifier id) { +- super(id); +- } +- +- private static void checkThread() { +- if (!Bukkit.isPrimaryThread()) { +- throw new IllegalStateException("Calling Timings from Async Operation"); +- } +- } +- +- @NotNull +- @Override +- public Timing startTiming() { +- checkThread(); +- return super.startTiming(); +- } +- +- @Override +- public void stopTiming() { +- checkThread(); +- super.stopTiming(); +- } +-} +diff --git a/src/main/java/org/bukkit/command/Command.java b/src/main/java/org/bukkit/command/Command.java +index 0a26fffe9b1e5080b5639767a03af11006108b4a..3b73c0e59788f5f49ca2423032550f11855d52ae 100644 +--- a/src/main/java/org/bukkit/command/Command.java ++++ b/src/main/java/org/bukkit/command/Command.java +@@ -33,16 +33,6 @@ public abstract class Command { + protected String usageMessage; + private String permission; + private net.kyori.adventure.text.Component permissionMessage; // Paper +- /** +- * @deprecated Timings will be removed in the future +- */ +- @Deprecated(forRemoval = true) +- public co.aikar.timings.Timing timings; // Paper +- /** +- * @deprecated Timings will be removed in the future +- */ +- @Deprecated(forRemoval = true) +- @NotNull public String getTimingName() {return getName();} // Paper + + protected Command(@NotNull String name) { + this(name, "", "/" + name, new ArrayList()); +diff --git a/src/main/java/org/bukkit/command/FormattedCommandAlias.java b/src/main/java/org/bukkit/command/FormattedCommandAlias.java +index abe256e1e45ce28036da4aa1586715bc8a1a3414..395ef82dad3c828d46b69b02c58fdb0ae1886cb9 100644 +--- a/src/main/java/org/bukkit/command/FormattedCommandAlias.java ++++ b/src/main/java/org/bukkit/command/FormattedCommandAlias.java +@@ -12,7 +12,6 @@ public class FormattedCommandAlias extends Command { + + public FormattedCommandAlias(@NotNull String alias, @NotNull String[] formatStrings) { + super(alias); +- timings = co.aikar.timings.TimingsManager.getCommandTiming("minecraft", this); // Spigot + this.formatStrings = formatStrings; + } + +@@ -120,9 +119,6 @@ public class FormattedCommandAlias extends Command { + return formatString.trim(); // Paper - Causes an extra space at the end, breaks with brig commands + } + +- @NotNull +- @Override // Paper +- public String getTimingName() {return "Command Forwarder - " + super.getTimingName();} // Paper + + private static boolean inRange(int i, int j, int k) { + return i >= j && i <= k; +diff --git a/src/main/java/org/bukkit/command/SimpleCommandMap.java b/src/main/java/org/bukkit/command/SimpleCommandMap.java +index 5df19bd701c67506689fc7f49d91f99ebfbc83f0..fda7b851432f5fff472ac80390d41e52ba142b49 100644 +--- a/src/main/java/org/bukkit/command/SimpleCommandMap.java ++++ b/src/main/java/org/bukkit/command/SimpleCommandMap.java +@@ -39,7 +39,6 @@ public class SimpleCommandMap implements CommandMap { + register("bukkit", new VersionCommand("version")); + register("bukkit", new ReloadCommand("reload")); + //register("bukkit", new PluginsCommand("plugins")); // Paper +- register("bukkit", new co.aikar.timings.TimingsCommand("timings")); // Paper + } + + public void setFallbackCommands() { +@@ -71,7 +70,6 @@ public class SimpleCommandMap implements CommandMap { + */ + @Override + public boolean register(@NotNull String label, @NotNull String fallbackPrefix, @NotNull Command command) { +- command.timings = co.aikar.timings.TimingsManager.getCommandTiming(fallbackPrefix, command); // Paper + label = label.toLowerCase(Locale.ROOT).trim(); + fallbackPrefix = fallbackPrefix.toLowerCase(Locale.ROOT).trim(); + boolean registered = register(label, command, false, fallbackPrefix); +@@ -153,17 +151,11 @@ public class SimpleCommandMap implements CommandMap { + return false; + } + +- // Paper start - Plugins do weird things to workaround normal registration +- if (target.timings == null) { +- target.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, target); +- } +- // Paper end +- + try { +- try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources ++ //try (co.aikar.timings.Timing ignored = target.timings.startTiming()) { // Paper - use try with resources // Purpur + // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) + target.execute(sender, sentCommandLabel, Arrays.copyOfRange(args, 1, args.length)); +- } // target.timings.stopTiming(); // Spigot // Paper ++ //} // target.timings.stopTiming(); // Spigot // Paper // Purpur + } catch (CommandException ex) { + server.getPluginManager().callEvent(new com.destroystokyo.paper.event.server.ServerExceptionEvent(new com.destroystokyo.paper.exception.ServerCommandException(ex, target, sender, args))); // Paper + //target.timings.stopTiming(); // Spigot // Paper +diff --git a/src/main/java/org/bukkit/plugin/SimplePluginManager.java b/src/main/java/org/bukkit/plugin/SimplePluginManager.java +index 065352b383d6398382b4f14641859dbfe2e6a355..664e6faeb07bf6c85e27b9845acc5e7cd36c148d 100644 +--- a/src/main/java/org/bukkit/plugin/SimplePluginManager.java ++++ b/src/main/java/org/bukkit/plugin/SimplePluginManager.java +@@ -720,7 +720,6 @@ public final class SimplePluginManager implements PluginManager { + throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled"); + } + +- executor = new co.aikar.timings.TimedEventExecutor(executor, plugin, null, event); // Paper + if (false) { // Spigot - RL handles useTimings check now // Paper + getEventListeners(event).register(new TimedRegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); + } else { +@@ -956,16 +955,7 @@ public final class SimplePluginManager implements PluginManager { + @Override + public boolean useTimings() { + if (true) {return this.paperPluginManager.useTimings();} // Paper +- return co.aikar.timings.Timings.isTimingsEnabled(); // Spigot +- } +- +- /** +- * Sets whether or not per event timing code should be used +- * +- * @param use True if per event timing code should be used +- */ +- public void useTimings(boolean use) { +- co.aikar.timings.Timings.setTimingsEnabled(use); // Paper ++ return false; + } + + // Paper start +diff --git a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +index eaefbb00e9993d54906cc8cf35cf753c0d6c7707..0d2d0c55e966918c8f1821f1ffdab0a9e102d0c6 100644 +--- a/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java ++++ b/src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java +@@ -43,7 +43,6 @@ import org.bukkit.plugin.TimedRegisteredListener; + import org.bukkit.plugin.UnknownDependencyException; + import org.jetbrains.annotations.NotNull; + import org.jetbrains.annotations.Nullable; +-import org.spigotmc.CustomTimingsHandler; // Spigot + import org.yaml.snakeyaml.error.YAMLException; + + /** +@@ -293,9 +292,10 @@ public final class JavaPluginLoader implements PluginLoader { + } + } + +- EventExecutor executor = new co.aikar.timings.TimedEventExecutor(new EventExecutor() { // Paper ++ // Paper ++ EventExecutor executor = new EventExecutor() { + @Override +- public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException { // Paper ++ public void execute(@NotNull Listener listener, @NotNull Event event) throws EventException { + try { + if (!eventClass.isAssignableFrom(event.getClass())) { + return; +@@ -306,8 +306,9 @@ public final class JavaPluginLoader implements PluginLoader { + } catch (Throwable t) { + throw new EventException(t); + } +- } +- }, plugin, method, eventClass); // Paper ++ } // Paper ++ ++ }; // Paper + if (false) { // Spigot - RL handles useTimings check now + eventSet.add(new TimedRegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); + } else { +diff --git a/src/main/java/org/spigotmc/CustomTimingsHandler.java b/src/main/java/org/spigotmc/CustomTimingsHandler.java +deleted file mode 100644 +index 12946bd55fcf7c40d39081779a7fa30049ee6165..0000000000000000000000000000000000000000 +--- a/src/main/java/org/spigotmc/CustomTimingsHandler.java ++++ /dev/null +@@ -1,67 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package org.spigotmc; +- +-import org.bukkit.Bukkit; +-import org.jetbrains.annotations.NotNull; +-import org.bukkit.plugin.AuthorNagException; +-import co.aikar.timings.Timing; +-import co.aikar.timings.Timings; +-import co.aikar.timings.TimingsManager; +- +-import java.lang.reflect.Method; +-import java.util.logging.Level; +- +-/** +- * This is here for legacy purposes incase any plugin used it. +- * +- * If you use this, migrate ASAP as this will be removed in the future! +- * +- * @deprecated +- * @see co.aikar.timings.Timings#of +- */ +-@Deprecated(forRemoval = true) +-public final class CustomTimingsHandler { +- private final Timing handler; +- +- public CustomTimingsHandler(@NotNull String name) { +- Timing timing; +- +- new AuthorNagException("Deprecated use of CustomTimingsHandler. Please Switch to Timings.of ASAP").printStackTrace(); +- try { +- final Method ofSafe = TimingsManager.class.getDeclaredMethod("getHandler", String.class, String.class, Timing.class); +- ofSafe.setAccessible(true); +- timing = (Timing) ofSafe.invoke(null,"Minecraft", "(Deprecated API) " + name, null); +- } catch (Exception e) { +- e.printStackTrace(); +- Bukkit.getLogger().log(Level.SEVERE, "This handler could not be registered"); +- timing = Timings.NULL_HANDLER; +- } +- handler = timing; +- } +- +- public void startTiming() { handler.startTiming(); } +- public void stopTiming() { handler.stopTiming(); } +- +-} diff --git a/patches/server/0002-Feature-secure-seed.patch b/patches/server/0002-Feature-secure-seed.patch index 5e2fc1d..3698c0c 100644 --- a/patches/server/0002-Feature-secure-seed.patch +++ b/patches/server/0002-Feature-secure-seed.patch @@ -427,10 +427,10 @@ index 135fa024d81b962761f0edc6896a2a507b6981f9..8ec743bbb76e402a2a0b92bdafe49d70 } diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java -index 72aa1b66950c1111793dad1583c19ac0d7e5823d..66c94c8337a873fcffdeb223d31e7951cc2eab2e 100644 +index 0166d6bd686d68ffdcc42e908b0d1aa41a3bffdf..e1fefb845cd0835be7f7b67dd4ae94758064b513 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java -@@ -618,6 +618,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. +@@ -614,6 +614,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. chunkgenerator = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, chunkgenerator, gen); } // CraftBukkit end diff --git a/patches/server/0005-Add-config.patch b/patches/server/0005-Add-config.patch index e7eb607..1974637 100644 --- a/patches/server/0005-Add-config.patch +++ b/patches/server/0005-Add-config.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Add-config diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java new file mode 100644 -index 0000000000000000000000000000000000000000..748a78a092067ccb2c9ebb650860765180703b8f +index 0000000000000000000000000000000000000000..344a70e3c1274936d218757059c713bf9e75603b --- /dev/null +++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -0,0 +1,183 @@ +@@ -0,0 +1,162 @@ +package net.edenor.foldenor.config; + +import com.google.common.base.Throwables; @@ -35,14 +35,6 @@ index 0000000000000000000000000000000000000000..748a78a092067ccb2c9ebb6508607651 + public static int version; + static boolean verbose; + -+ public static boolean sendNullEntityPackets = true; -+ -+ public static boolean vanilaEndPortalTeleportation = false; -+ -+ public static int maxProjectileLoadsPerProjectile = 10; -+ -+ public static int maxProjectileLoadsPerTick = 10; -+ + public static void init(File configFile) { + init(configFile, true); + } @@ -83,11 +75,6 @@ index 0000000000000000000000000000000000000000..748a78a092067ccb2c9ebb6508607651 + } + + static void readConfig() { -+ networkSettings(); -+ -+ optimizationSettings(); -+ -+ otherSettings(); + + try { + config.save(CONFIG_FILE); @@ -96,19 +83,6 @@ index 0000000000000000000000000000000000000000..748a78a092067ccb2c9ebb6508607651 + } + } + -+ private static void otherSettings() { -+ vanilaEndPortalTeleportation = getBoolean("other.vanila-end-portal-teleportation", vanilaEndPortalTeleportation); -+ } -+ -+ private static void optimizationSettings() { -+ maxProjectileLoadsPerProjectile = getInt("optimizations.max_projectile_loads_per_projectile",maxProjectileLoadsPerProjectile); -+ maxProjectileLoadsPerTick = getInt("optimizations.max_projectile_loads_per_tick",maxProjectileLoadsPerTick); -+ } -+ -+ private static void networkSettings() { -+ sendNullEntityPackets = getBoolean("network.send-null-entity-packets", sendNullEntityPackets); -+ } -+ + protected static void set(String path, Object val) { + config.addDefault(path, val); + config.set(path, val); @@ -139,6 +113,11 @@ index 0000000000000000000000000000000000000000..748a78a092067ccb2c9ebb6508607651 + return config.getList(path, config.getList(path)); + } + ++ protected static List getStringList(String path, List def, String... comment) { ++ config.addDefault(path, def); ++ return config.getStringList(path); ++ } ++ + static Map getMap(String path, Map def, String... comment) { + if (def != null && config.getConfigurationSection(path) == null) { + config.addDefault(path, def); diff --git a/patches/server/0007-Send-null-entity-packets.patch b/patches/server/0007-Send-null-entity-packets.patch index eb43fcc..febdb9d 100644 --- a/patches/server/0007-Send-null-entity-packets.patch +++ b/patches/server/0007-Send-null-entity-packets.patch @@ -4,6 +4,38 @@ Date: Wed, 31 Jul 2024 14:54:16 +0400 Subject: [PATCH] Send-null-entity-packets +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index 344a70e3c1274936d218757059c713bf9e75603b..c8c6631093ff2ebd23527e80778b35f9c44cc8bf 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -23,6 +23,8 @@ public class FoldenorConfig { + public static int version; + static boolean verbose; + ++ public static boolean sendNullEntityPackets = false; ++ + public static void init(File configFile) { + init(configFile, true); + } +@@ -63,6 +65,7 @@ public class FoldenorConfig { + } + + static void readConfig() { ++ readNetworkSettings(); + + try { + config.save(CONFIG_FILE); +@@ -71,6 +74,10 @@ public class FoldenorConfig { + } + } + ++ protected static void readNetworkSettings(){ ++ sendNullEntityPackets = getBoolean("network.send-null-entity-packets", sendNullEntityPackets); ++ } ++ + protected static void set(String path, Object val) { + config.addDefault(path, val); + config.set(path, val); diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java index 8ea2f24695f5dad55e21f238b69442513e7a90c6..38ca0acf3a1af7dcdd9e87e6d9e2e14c6d3fad41 100644 --- a/src/main/java/net/minecraft/server/level/ServerEntity.java diff --git a/patches/server/0019-Pufferfish-Optimize-suffocation.patch b/patches/server/0019-Pufferfish-Optimize-suffocation.patch new file mode 100644 index 0000000..445457e --- /dev/null +++ b/patches/server/0019-Pufferfish-Optimize-suffocation.patch @@ -0,0 +1,91 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 14:15:52 +0400 +Subject: [PATCH] Pufferfish-Optimize-suffocation + + +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index c8c6631093ff2ebd23527e80778b35f9c44cc8bf..8c8101d47376b9fb13652869401494c8b605620e 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -25,6 +25,8 @@ public class FoldenorConfig { + + public static boolean sendNullEntityPackets = false; + ++ public static boolean enableSuffocationOptimization = false; ++ + public static void init(File configFile) { + init(configFile, true); + } +@@ -67,6 +69,8 @@ public class FoldenorConfig { + static void readConfig() { + readNetworkSettings(); + ++ readOptimizationSettings(); ++ + try { + config.save(CONFIG_FILE); + } catch (IOException ex) { +@@ -78,6 +82,14 @@ public class FoldenorConfig { + sendNullEntityPackets = getBoolean("network.send-null-entity-packets", sendNullEntityPackets); + } + ++ private static void readOptimizationSettings() { ++ enableSuffocationOptimization = getBoolean("optimizations.enable-suffocation-optimization", true, ++ "Optimizes the suffocation check by selectively skipping", ++ "the check in a way that still appears vanilla. This should", ++ "be left enabled on most servers, but is provided as a", ++ "configuration option if the vanilla deviation is undesirable."); ++ } ++ + protected static void set(String path, Object val) { + config.addDefault(path, val); + config.set(path, val); +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index b4fec6374803534aa3b5d5e7aec64f68c7c7c356..873e9b488a14866c99779a5b5e5f3d1c211febd6 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -463,7 +463,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + if (this.isAlive()) { + boolean flag = this instanceof net.minecraft.world.entity.player.Player; + +- if (!this.level().isClientSide) { ++ if (shouldCheckForSuffocation() && this.isInWall()) { // Pufferfish - optimize suffocation + if (this.isInWall()) { + this.hurt(this.damageSources().inWall(), 1.0F); + } else if (flag && !this.level().getWorldBorder().isWithinBounds(this.getBoundingBox())) { +@@ -1428,6 +1428,16 @@ public abstract class LivingEntity extends Entity implements Attackable { + return this.getHealth() <= 0.0F; + } + ++ // Pufferfish start - optimize suffocation ++ public boolean couldPossiblyBeHurt(float amount) { ++ return !((float) this.invulnerableTime > (float) this.invulnerableDuration / 2.0F) || !(amount <= this.lastHurt); ++ } ++ ++ public boolean shouldCheckForSuffocation() { ++ return !net.edenor.foldenor.config.FoldenorConfig.enableSuffocationOptimization || (tickCount % 10 == 0 && couldPossiblyBeHurt(1.0F)); ++ } ++ // Pufferfish end ++ + @Override + public boolean hurt(DamageSource source, float amount) { + if (this.isInvulnerableTo(source)) { +diff --git a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java +index d3b4d492aee380dc17f4232d90eaae4f07bb9f86..82921c56c49edb0ca07425da563aa4876d4e6fb1 100644 +--- a/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java ++++ b/src/main/java/net/minecraft/world/entity/boss/wither/WitherBoss.java +@@ -154,6 +154,13 @@ public class WitherBoss extends Monster implements PowerableMob, RangedAttackMob + this.bossEvent.setName(this.getDisplayName()); + } + ++ // Pufferfish start - optimize suffocation ++ @Override ++ public boolean shouldCheckForSuffocation() { ++ return true; ++ } ++ // Pufferfish end ++ + @Override + protected SoundEvent getAmbientSound() { + return SoundEvents.WITHER_AMBIENT; diff --git a/patches/server/0020-Pufferfish-Optimize-random-calls-in-chunk-ticking.patch b/patches/server/0020-Pufferfish-Optimize-random-calls-in-chunk-ticking.patch new file mode 100644 index 0000000..15ed1a7 --- /dev/null +++ b/patches/server/0020-Pufferfish-Optimize-random-calls-in-chunk-ticking.patch @@ -0,0 +1,51 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 14:17:23 +0400 +Subject: [PATCH] Pufferfish-Optimize-random-calls-in-chunk-ticking + + +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index e1fefb845cd0835be7f7b67dd4ae94758064b513..a3e8f0c12b2b6592ef6c2ace56ca0e053a4bbbff 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -994,7 +994,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + ProfilerFiller gameprofilerfiller = this.getProfiler(); + + gameprofilerfiller.push("thunder"); +- if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && this.random.nextInt(this.spigotConfig.thunderChance) == 0) { // Spigot // Paper - Option to disable thunder ++ if (!this.paperConfig().environment.disableThunder && flag && this.isThundering() && this.spigotConfig.thunderChance > 0 && /*this.random.nextInt(this.spigotConfig.thunderChance) == 0 &&*/ chunk.shouldDoLightning(this.random)) { // Spigot // Paper - Option to disable thunder // Pufferfish - replace random with shouldDoLightning + BlockPos blockposition = this.findLightningTargetAround(this.getBlockRandomPos(j, 0, k, 15)); + + if (this.isRainingAt(blockposition)) { +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 e0359a8857387428d44c19af8dd0e2e743a0b2b5..42ae955ff55e868f9eedde61a21ec26763fd7700 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -93,6 +93,18 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + private final LevelChunkTicks blockTicks; + private final LevelChunkTicks fluidTicks; + ++ // Pufferfish start - instead of using a random every time the chunk is ticked, define when lightning strikes preemptively ++ private int lightningTick; ++ // shouldDoLightning compiles down to 29 bytes, which with the default of 35 byte inlining should guarantee an inline ++ public final boolean shouldDoLightning(net.minecraft.util.RandomSource random) { ++ if (this.lightningTick-- <= 0) { ++ this.lightningTick = random.nextInt(this.level.spigotConfig.thunderChance) << 1; ++ return true; ++ } ++ return false; ++ } ++ // Pufferfish end ++ + public LevelChunk(Level world, ChunkPos pos) { + this(world, pos, UpgradeData.EMPTY, new LevelChunkTicks<>(), new LevelChunkTicks<>(), 0L, (LevelChunkSection[]) null, (LevelChunk.PostLoadProcessor) null, (BlendingData) null); + } +@@ -124,6 +136,8 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + this.debug = !empty && this.level.isDebug(); + this.defaultBlockState = empty ? VOID_AIR_BLOCKSTATE : AIR_BLOCKSTATE; + // Paper end - get block chunk optimisation ++ ++ this.lightningTick = new java.util.Random().nextInt(100000) << 1; // Pufferfish - initialize lightning tick + } + + // CraftBukkit start diff --git a/patches/server/0021-Pufferfish-Dynamic-Activation-of-Brain.patch b/patches/server/0021-Pufferfish-Dynamic-Activation-of-Brain.patch new file mode 100644 index 0000000..54fd303 --- /dev/null +++ b/patches/server/0021-Pufferfish-Dynamic-Activation-of-Brain.patch @@ -0,0 +1,399 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 14:31:43 +0400 +Subject: [PATCH] Pufferfish-Dynamic-Activation-of-Brain + + +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index 8c8101d47376b9fb13652869401494c8b605620e..c36cac84603cd0c66c332eff8d55e16b7a5d3428 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -2,6 +2,9 @@ package net.edenor.foldenor.config; + + import com.google.common.base.Throwables; + 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.InvalidConfigurationException; +@@ -12,6 +15,7 @@ import java.io.IOException; + import java.lang.reflect.InvocationTargetException; + import java.lang.reflect.Method; + import java.lang.reflect.Modifier; ++import java.util.Collections; + import java.util.List; + import java.util.Map; + import java.util.logging.Level; +@@ -27,6 +31,14 @@ public class FoldenorConfig { + + public static boolean enableSuffocationOptimization = false; + ++ // Pufferfish DAB ++ public static boolean dearEnabled; ++ public static int startDistance; ++ public static int startDistanceSquared; ++ public static int maximumActivationPrio; ++ public static int activationDistanceMod; ++ // Pufferfish DAB ++ + public static void init(File configFile) { + init(configFile, true); + } +@@ -71,6 +83,12 @@ public class FoldenorConfig { + + readOptimizationSettings(); + ++ try { ++ readDynamicActivationOfBrains(); ++ } catch (IOException e) { ++ throw new RuntimeException(e); ++ } ++ + try { + config.save(CONFIG_FILE); + } catch (IOException ex) { +@@ -90,6 +108,30 @@ public class FoldenorConfig { + "configuration option if the vanilla deviation is undesirable."); + } + ++ private static void readDynamicActivationOfBrains() throws IOException { ++ dearEnabled = getBoolean("dab.enabled", true); ++ startDistance = getInt("dab.start-distance", 12, ++ "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("dab.max-tick-freq", 20, ++ "This value defines how often in ticks, the furthest entity", ++ "will get their pathfinders and behaviors ticked. 20 = 1s"); ++ activationDistanceMod = getInt("dab.activation-dist-mod", 8, ++ "This value defines how much distance modifies an entity's", ++ "tick frequency. freq = (distanceToPlayer^2) / (2^value)", ++ "If you want further away entities to tick less often, use 7.", ++ "If you want further away entities to tick more often, try 9."); ++ ++ for (EntityType entityType : BuiltInRegistries.ENTITY_TYPE) { ++ entityType.dabEnabled = true; // reset all, before setting the ones to true ++ } ++ getStringList("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 + "\""))); ++ } ++ + protected static void set(String path, Object val) { + config.addDefault(path, val); + config.set(path, val); +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index a3e8f0c12b2b6592ef6c2ace56ca0e053a4bbbff..9a9be6f618c361db39eff0f0397bbc21615da411 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -806,6 +806,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + this.timings.entityTick.startTiming(); // Spigot + profiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.ENTITY_TICK); try { // Folia - profiler + regionizedWorldData.forEachTickingEntity((entity) -> { // Folia - regionised ticking ++ entity.activatedPriorityReset = false; // Pufferfish - DAB + if (!entity.isRemoved()) { + if (false && this.shouldDiscardEntity(entity)) { // CraftBukkit - We prevent spawning in general, so this butchering is not needed + entity.discard(); +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index d1164f15c6b9ae221f6cae2bdb7d040cfa3cbccf..f6ad6cb80aefb6c64a0dbb986f4afcc70aa19c1d 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -431,6 +431,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + private UUID originWorld; + public boolean freezeLocked = false; // Paper - Freeze Tick Lock API + public boolean fixedPose = false; // Paper - Expand Pose API ++ public boolean activatedPriorityReset = false; // Pufferfish - DAB ++ public int activatedPriority = net.edenor.foldenor.config.FoldenorConfig.maximumActivationPrio; // Pufferfish - DAB (golf score) + + public void setOrigin(@javax.annotation.Nonnull Location location) { + this.origin = location.toVector(); +diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java +index c74a01a8551457507441d266b6923b4248560abf..da815f30a44cbfe577b75b79e4527930fc6980d7 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityType.java ++++ b/src/main/java/net/minecraft/world/entity/EntityType.java +@@ -316,6 +316,7 @@ public class EntityType implements FeatureElement, EntityTypeT + private final boolean canSpawnFarFromPlayer; + private final int clientTrackingRange; + private final int updateInterval; ++ public boolean dabEnabled = false; // Pufferfish + @Nullable + private String descriptionId; + @Nullable +diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java +index 741ba5f0834ab5f0b0ffdeb91a9ff8cfaf922792..f74e8826700932d5e145f90e57d562c15863f27a 100644 +--- a/src/main/java/net/minecraft/world/entity/Mob.java ++++ b/src/main/java/net/minecraft/world/entity/Mob.java +@@ -244,10 +244,10 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + @Override + public void inactiveTick() { + super.inactiveTick(); +- if (this.goalSelector.inactiveTick()) { ++ if (this.goalSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priroity + this.goalSelector.tick(); + } +- if (this.targetSelector.inactiveTick()) { ++ if (this.targetSelector.inactiveTick(this.activatedPriority, true)) { // Pufferfish - pass activated priority + this.targetSelector.tick(); + } + } +@@ -945,16 +945,20 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + + if (i % 2 != 0 && this.tickCount > 1) { + gameprofilerfiller.push("targetSelector"); ++ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.targetSelector.tickRunningGoals(false); + gameprofilerfiller.pop(); + gameprofilerfiller.push("goalSelector"); ++ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.goalSelector.tickRunningGoals(false); + gameprofilerfiller.pop(); + } else { + gameprofilerfiller.push("targetSelector"); ++ if (this.targetSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.targetSelector.tick(); + gameprofilerfiller.pop(); + gameprofilerfiller.push("goalSelector"); ++ if (this.goalSelector.inactiveTick(this.activatedPriority, false)) // Pufferfish - use this to alternate ticking + this.goalSelector.tick(); + gameprofilerfiller.pop(); + } +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java +index 758f62416ca9c02351348ac0d41deeb4624abc0e..69130969c9a434ec2361e573c9a1ec9f462dfda2 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/VillagerPanicTrigger.java +@@ -36,7 +36,11 @@ public class VillagerPanicTrigger extends Behavior { + + @Override + protected void tick(ServerLevel world, Villager entity, long time) { +- if (time % 100L == 0L) { ++ // Pufferfish start ++ if (entity.nextGolemPanic < 0) entity.nextGolemPanic = time + 100; ++ if (--entity.nextGolemPanic < time) { ++ entity.nextGolemPanic = -1; ++ // Pufferfish end + entity.spawnGolemIfNeeded(world, time, 3); + } + } +diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +index 9bdbf3e9453bc3ce96d52d04b8cde0d05f7356d8..b581263990fed39efef95a62fd7f10e3b21a3d19 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java ++++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java +@@ -43,9 +43,12 @@ public class GoalSelector { + } + + // Paper start +- public boolean inactiveTick() { ++ public boolean inactiveTick(int tickRate, boolean inactive) { // Pufferfish start ++ if (inactive && !net.edenor.foldenor.config.FoldenorConfig.dearEnabled) tickRate = 4; // reset to Paper's ++ tickRate = Math.min(tickRate, 3); + this.curRate++; +- return this.curRate % 3 == 0; // TODO newGoalRate was already unused in 1.20.4, check if this is correct ++ return this.curRate % tickRate == 0; // TODO newGoalRate was already unused in 1.20.4, check if this is correct ++ // Pufferfish end + } + public boolean hasTasks() { + for (WrappedGoal task : this.availableGoals) { +diff --git a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +index 69986f75d3cf729204cca0c7e5428536af31f695..98a759dbe46e2ead39af0f340c9b73c8f4ddce1e 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java ++++ b/src/main/java/net/minecraft/world/entity/animal/allay/Allay.java +@@ -217,9 +217,11 @@ public class Allay extends PathfinderMob implements InventoryCarrier, VibrationS + return 0.4F; + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { + this.level().getProfiler().push("allayBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel) this.level(), this); + this.level().getProfiler().pop(); + this.level().getProfiler().push("allayActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java +index 01a0731e92d39c8718538244e34a271fb8717fc2..44937570f8e968ba4fe2822f69ca8f09679da89d 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java ++++ b/src/main/java/net/minecraft/world/entity/animal/axolotl/Axolotl.java +@@ -269,9 +269,11 @@ public class Axolotl extends Animal implements LerpingModel, VariantHolder> { + .ifPresent(this::setVariant); + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { + this.level().getProfiler().push("frogBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel)this.level(), this); + this.level().getProfiler().pop(); + this.level().getProfiler().push("frogActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +index 43046f4a0cff620834ac4647efdcde227185b2ff..90393485ebcf8a4c8c74802fff942b1af8cfbf00 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java ++++ b/src/main/java/net/minecraft/world/entity/animal/frog/Tadpole.java +@@ -83,9 +83,11 @@ public class Tadpole extends AbstractFish { + return SoundEvents.TADPOLE_FLOP; + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { + this.level().getProfiler().push("tadpoleBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel) this.level(), this); + this.level().getProfiler().pop(); + this.level().getProfiler().push("tadpoleActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java +index 376bcbc189008464f4d518c1e07643431ba96306..07bdea8a7d6706839a758afe0242202c7e841416 100644 +--- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java ++++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java +@@ -190,9 +190,11 @@ public class Goat extends Animal { + return (Brain) super.getBrain(); // CraftBukkit - decompile error + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { + this.level().getProfiler().push("goatBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel) this.level(), this); + this.level().getProfiler().pop(); + this.level().getProfiler().push("goatActivityUpdate"); +diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java +index d5e0c493f4c348724958193795ceb987765a465f..7de73564bc73d6504e18977e97a2ef5f46189e15 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java +@@ -153,9 +153,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase { + return (Brain)super.getBrain(); + } + ++ private int behaviorTick; // Pufferfish + @Override + protected void customServerAiStep() { + this.level().getProfiler().push("hoglinBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel)this.level(), this); + this.level().getProfiler().pop(); + HoglinAi.updateActivity(this); +diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +index bc58323801ee16fe9b63c21332144ec002a902f2..b2ae7088f90bf3cd04a59c6ddfdba60c58c6e1c8 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java ++++ b/src/main/java/net/minecraft/world/entity/monster/piglin/Piglin.java +@@ -293,9 +293,11 @@ public class Piglin extends AbstractPiglin implements CrossbowAttackMob, Invento + return !this.cannotHunt; + } + ++ private int behaviorTick; // Pufferfish + @Override + protected void customServerAiStep() { + this.level().getProfiler().push("piglinBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick((ServerLevel) this.level(), this); + this.level().getProfiler().pop(); + PiglinAi.updateActivity(this); +diff --git a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +index 38bf417a9ad4647f4af24d969f3bf4fed9c4bad7..40bbd80b1ed4afede6f0769e7f3fcfc61200452f 100644 +--- a/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java ++++ b/src/main/java/net/minecraft/world/entity/monster/warden/Warden.java +@@ -272,11 +272,13 @@ public class Warden extends Monster implements VibrationSystem { + + } + ++ private int behaviorTick = 0; // Pufferfish + @Override + protected void customServerAiStep() { + ServerLevel worldserver = (ServerLevel) this.level(); + + worldserver.getProfiler().push("wardenBrain"); ++ if (this.behaviorTick++ % this.activatedPriority == 0) // Pufferfish + this.getBrain().tick(worldserver, this); + this.level().getProfiler().pop(); + super.customServerAiStep(); +diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java +index 91728a992a29bc22e46a260750d5dd88e629bfd1..3f4e4b612e127e51b240bca8776f6fc3743bd2d7 100644 +--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java ++++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java +@@ -143,6 +143,8 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + return holder.is(PoiTypes.MEETING); + }); + ++ public long nextGolemPanic = -1; // Pufferfish ++ + public Villager(EntityType entityType, Level world) { + this(entityType, world, VillagerType.PLAINS); + } +@@ -246,6 +248,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + // Spigot End + ++ private int behaviorTick = 0; // Pufferfish + @Override + @Deprecated // Paper + protected void customServerAiStep() { +@@ -255,7 +258,11 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + protected void customServerAiStep(final boolean inactive) { + // Paper end + this.level().getProfiler().push("villagerBrain"); +- if (!inactive) this.getBrain().tick((ServerLevel) this.level(), this); // Paper ++ // Pufferfish start ++ if (!inactive && this.behaviorTick++ % this.activatedPriority == 0) { ++ this.getBrain().tick((ServerLevel) this.level(), this); // Paper ++ } ++ // Pufferfish end + this.level().getProfiler().pop(); + if (this.assignProfessionWhenSpawned) { + this.assignProfessionWhenSpawned = false; +diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java +index 28f376f6ab219b3327f074c95b87b923270504f6..24b45224b6a9e3b86ddba550e720b62bd5638c5d 100644 +--- a/src/main/java/org/spigotmc/ActivationRange.java ++++ b/src/main/java/org/spigotmc/ActivationRange.java +@@ -38,6 +38,7 @@ import co.aikar.timings.MinecraftTimings; + import net.minecraft.world.entity.schedule.Activity; + import net.minecraft.world.level.Level; + import net.minecraft.world.phys.AABB; ++import net.minecraft.world.phys.Vec3; + + public class ActivationRange + { +@@ -232,6 +233,24 @@ public class ActivationRange + } + // Paper end - Configurable marker ticking + ActivationRange.activateEntity(entity, bbByType); // Folia - threaded regions ++ ++ // Pufferfish start ++ if (net.edenor.foldenor.config.FoldenorConfig.dearEnabled && entity.getType().dabEnabled) { ++ if (!entity.activatedPriorityReset) { ++ entity.activatedPriorityReset = true; ++ entity.activatedPriority = net.edenor.foldenor.config.FoldenorConfig.maximumActivationPrio; ++ } ++ Vec3 playerVec = player.position(); ++ Vec3 entityVec = entity.position(); ++ double diffX = playerVec.x - entityVec.x, diffY = playerVec.y - entityVec.y, diffZ = playerVec.z - entityVec.z; ++ int squaredDistance = (int) (diffX * diffX + diffY * diffY + diffZ * diffZ); ++ entity.activatedPriority = squaredDistance > net.edenor.foldenor.config.FoldenorConfig.startDistanceSquared ? ++ Math.max(1, Math.min(squaredDistance >> net.edenor.foldenor.config.FoldenorConfig.activationDistanceMod, entity.activatedPriority)) : ++ 1; ++ } else { ++ entity.activatedPriority = 1; ++ } ++ // Pufferfish end + } + // Paper end + } diff --git a/patches/server/0022-Leaves-Protocol-Core.patch b/patches/server/0022-Leaves-Protocol-Core.patch new file mode 100644 index 0000000..c40ae63 --- /dev/null +++ b/patches/server/0022-Leaves-Protocol-Core.patch @@ -0,0 +1,782 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 15:04:25 +0400 +Subject: [PATCH] Leaves-Protocol-Core + + +diff --git a/src/main/java/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java b/src/main/java/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java +index b2a4ae4388758f89fcd0dc3b0c4c03bf46fa5470..3f815ee7c66d27f1dfec01f9fd57847fc9366863 100644 +--- a/src/main/java/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java ++++ b/src/main/java/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java +@@ -34,19 +34,29 @@ public interface CustomPacketPayload { + + private void writeCap(B value, CustomPacketPayload.Type id, CustomPacketPayload payload) { + value.writeResourceLocation(id.id()); +- StreamCodec streamCodec = this.findCodec(id.id); ++ StreamCodec streamCodec = (StreamCodec) this.findCodec(id.id); + streamCodec.encode(value, (T)payload); + } + + @Override + public void encode(B friendlyByteBuf, CustomPacketPayload customPacketPayload) { ++ // Leaves start - protocol core ++ if (customPacketPayload instanceof org.leavesmc.leaves.protocol.core.LeavesCustomPayload leavesCustomPayload) { ++ friendlyByteBuf.writeResourceLocation(leavesCustomPayload.id()); ++ leavesCustomPayload.write(friendlyByteBuf); ++ return; ++ } ++ // Leaves end - protocol core + this.writeCap(friendlyByteBuf, customPacketPayload.type(), customPacketPayload); + } + + @Override + public CustomPacketPayload decode(B friendlyByteBuf) { + ResourceLocation resourceLocation = friendlyByteBuf.readResourceLocation(); +- return (CustomPacketPayload)this.findCodec(resourceLocation).decode(friendlyByteBuf); ++ // Leaves start - protocol core ++ var leavesCustomPayload = org.leavesmc.leaves.protocol.core.LeavesProtocolManager.decode(resourceLocation, friendlyByteBuf); ++ return java.util.Objects.requireNonNullElseGet(leavesCustomPayload, () -> (CustomPacketPayload) this.findCodec(resourceLocation).decode(friendlyByteBuf)); ++ // Leaves end - protocol core + } + }; + } +diff --git a/src/main/java/net/minecraft/resources/ResourceLocation.java b/src/main/java/net/minecraft/resources/ResourceLocation.java +index 1967c43ee3a12e63365cc40ee6565307e2fd73cf..6e376d0db5321d8e9b6e0b54617ffd171bf4ee73 100644 +--- a/src/main/java/net/minecraft/resources/ResourceLocation.java ++++ b/src/main/java/net/minecraft/resources/ResourceLocation.java +@@ -36,7 +36,7 @@ public final class ResourceLocation implements Comparable { + private final String namespace; + private final String path; + +- private ResourceLocation(String namespace, String path) { ++ public ResourceLocation(String namespace, String path) { // Leaves - private -> public + assert isValidNamespace(namespace); + + assert isValidPath(path); +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index ac56b02498eb38883ae462be6ef3d15cb2a855aa..fca787f92827019e776a6ccae8f90fe2b12c5177 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1922,6 +1922,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop leavesPayload) { ++ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePayload(player, leavesPayload); ++ } ++ // Leaves end - protocol + // Paper start - Brand support + if (packet.payload() instanceof net.minecraft.network.protocol.common.custom.BrandPayload brandPayload) { + this.player.clientBrandName = brandPayload.brand(); +@@ -178,6 +183,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + String channels = payload.toString(com.google.common.base.Charsets.UTF_8); + for (String channel : channels.split("\0")) { + this.getCraftPlayer().addChannel(channel); ++ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleMinecraftRegister(channel, player); // Leaves - protocol + } + } catch (Exception ex) { + ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t register custom payload", ex); +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 683b5c164bf40a9d900cb96134350f2fdd06b620..9741cad817e5bb0d8efc134f7fdb9f6142891425 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -443,6 +443,8 @@ public abstract class PlayerList { + //return; // Folia - region threading - must still allow the player to connect, as we must add to chunk map before handling disconnect + } + ++ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player); // Leaves - protocol ++ + final net.kyori.adventure.text.Component jm = playerJoinEvent.joinMessage(); + + if (jm != null && !jm.equals(net.kyori.adventure.text.Component.empty())) { // Paper - Adventure +@@ -688,6 +690,7 @@ public abstract class PlayerList { + return this.remove(entityplayer, net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? entityplayer.getBukkitEntity().displayName() : io.papermc.paper.adventure.PaperAdventure.asAdventure(entityplayer.getDisplayName()))); + } + public net.kyori.adventure.text.Component remove(ServerPlayer entityplayer, net.kyori.adventure.text.Component leaveMessage) { ++ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerLeave(entityplayer); // Leaves - protocol + // Paper end - Fix kick event leave message not being sent + ServerLevel worldserver = entityplayer.serverLevel(); + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index ae22118c14f3e27cd72238ed1d556251e3852b10..ada5c6360d988b51697c225aa314f2e43bb3e04b 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -489,6 +489,7 @@ public final class CraftServer implements Server { + this.potionBrewer = new io.papermc.paper.potion.PaperPotionBrewer(console); // Paper - custom potion mixes + datapackManager = new io.papermc.paper.datapack.PaperDatapackManager(console.getPackRepository()); // Paper + this.spark = new io.papermc.paper.SparksFly(this); // Paper - spark ++ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.init(); // Leaves - protocol + } + + public boolean getCommandBlockOverride(String command) { +@@ -1156,6 +1157,7 @@ public final class CraftServer implements Server { + this.spark.registerCommandBeforePlugins(this); // Paper - spark + this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); + this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); ++ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleServerReload(); // Leaves - protocol + + int pollCount = 0; + +diff --git a/src/main/java/org/leavesmc/leaves/LeavesLogger.java b/src/main/java/org/leavesmc/leaves/LeavesLogger.java +new file mode 100644 +index 0000000000000000000000000000000000000000..47347a3bdab2ff9818bf8198291d2dabec7da8c6 +--- /dev/null ++++ b/src/main/java/org/leavesmc/leaves/LeavesLogger.java +@@ -0,0 +1,24 @@ ++package org.leavesmc.leaves; ++ ++import org.bukkit.Bukkit; ++ ++import java.util.logging.Level; ++import java.util.logging.Logger; ++ ++public class LeavesLogger extends Logger { ++ public static final LeavesLogger LOGGER = new LeavesLogger(); ++ ++ private LeavesLogger() { ++ super("Leaves", null); ++ setParent(Bukkit.getLogger()); ++ setLevel(Level.ALL); ++ } ++ ++ public void severe(String msg, Exception exception) { ++ this.severe(msg + ", " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ ++ public void warning(String msg, Exception exception) { ++ this.warning(msg + ", " + exception.getCause() + ": " + exception.getMessage()); ++ } ++} +diff --git a/src/main/java/org/leavesmc/leaves/protocol/core/LeavesCustomPayload.java b/src/main/java/org/leavesmc/leaves/protocol/core/LeavesCustomPayload.java +new file mode 100644 +index 0000000000000000000000000000000000000000..b09a7bfe4cbff89acfaf4aec498a5b4e5f911cf6 +--- /dev/null ++++ b/src/main/java/org/leavesmc/leaves/protocol/core/LeavesCustomPayload.java +@@ -0,0 +1,29 @@ ++package org.leavesmc.leaves.protocol.core; ++ ++import net.minecraft.network.FriendlyByteBuf; ++import net.minecraft.network.protocol.common.custom.CustomPacketPayload; ++import net.minecraft.resources.ResourceLocation; ++import org.jetbrains.annotations.NotNull; ++ ++import java.lang.annotation.ElementType; ++import java.lang.annotation.Retention; ++import java.lang.annotation.RetentionPolicy; ++import java.lang.annotation.Target; ++ ++public interface LeavesCustomPayload> extends CustomPacketPayload { ++ ++ @Target({ElementType.METHOD, ElementType.CONSTRUCTOR}) ++ @Retention(RetentionPolicy.RUNTIME) ++ @interface New { ++ } ++ ++ void write(FriendlyByteBuf buf); ++ ++ ResourceLocation id(); ++ ++ @Override ++ @NotNull ++ default Type type() { ++ return new CustomPacketPayload.Type<>(id()); ++ } ++} +diff --git a/src/main/java/org/leavesmc/leaves/protocol/core/LeavesProtocol.java b/src/main/java/org/leavesmc/leaves/protocol/core/LeavesProtocol.java +new file mode 100644 +index 0000000000000000000000000000000000000000..986d2a6641ff8017dddf3e5f2655adfc2866c119 +--- /dev/null ++++ b/src/main/java/org/leavesmc/leaves/protocol/core/LeavesProtocol.java +@@ -0,0 +1,12 @@ ++package org.leavesmc.leaves.protocol.core; ++ ++import java.lang.annotation.ElementType; ++import java.lang.annotation.Retention; ++import java.lang.annotation.RetentionPolicy; ++import java.lang.annotation.Target; ++ ++@Target(ElementType.TYPE) ++@Retention(RetentionPolicy.RUNTIME) ++public @interface LeavesProtocol { ++ String[] namespace(); ++} +diff --git a/src/main/java/org/leavesmc/leaves/protocol/core/LeavesProtocolManager.java b/src/main/java/org/leavesmc/leaves/protocol/core/LeavesProtocolManager.java +new file mode 100644 +index 0000000000000000000000000000000000000000..a705e1f4a818db4bc2c2b0de0760e16aaf28c526 +--- /dev/null ++++ b/src/main/java/org/leavesmc/leaves/protocol/core/LeavesProtocolManager.java +@@ -0,0 +1,438 @@ ++package org.leavesmc.leaves.protocol.core; ++ ++import com.google.common.collect.ImmutableSet; ++import net.minecraft.network.FriendlyByteBuf; ++import net.minecraft.network.chat.Component; ++import net.minecraft.resources.ResourceLocation; ++import net.minecraft.server.level.ServerPlayer; ++import org.apache.commons.lang.ArrayUtils; ++import org.bukkit.event.player.PlayerKickEvent; ++import org.jetbrains.annotations.NotNull; ++import org.leavesmc.leaves.LeavesLogger; ++ ++import java.io.File; ++import java.io.IOException; ++import java.lang.reflect.Constructor; ++import java.lang.reflect.Executable; ++import java.lang.reflect.InvocationTargetException; ++import java.lang.reflect.Method; ++import java.lang.reflect.Modifier; ++import java.net.JarURLConnection; ++import java.net.URL; ++import java.net.URLDecoder; ++import java.nio.charset.StandardCharsets; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.Collections; ++import java.util.Enumeration; ++import java.util.HashMap; ++import java.util.HashSet; ++import java.util.Iterator; ++import java.util.LinkedHashSet; ++import java.util.List; ++import java.util.Map; ++import java.util.Set; ++import java.util.jar.JarEntry; ++import java.util.jar.JarFile; ++ ++public class LeavesProtocolManager { ++ ++ private static final Class[] PAYLOAD_PARAMETER_TYPES = {ResourceLocation.class, FriendlyByteBuf.class}; ++ ++ private static final LeavesLogger LOGGER = LeavesLogger.LOGGER; ++ ++ private static final Map> KNOWN_TYPES = new HashMap<>(); ++ private static final Map> KNOW_RECEIVERS = new HashMap<>(); ++ private static Set ALL_KNOWN_ID = new HashSet<>(); ++ ++ private static final List TICKERS = new ArrayList<>(); ++ private static final List PLAYER_JOIN = new ArrayList<>(); ++ private static final List PLAYER_LEAVE = new ArrayList<>(); ++ private static final List RELOAD_SERVER = new ArrayList<>(); ++ private static final Map> MINECRAFT_REGISTER = new HashMap<>(); ++ ++ public static void init() { ++ for (Class clazz : getClasses("org.leavesmc.leaves.protocol")) { ++ final LeavesProtocol protocol = clazz.getAnnotation(LeavesProtocol.class); ++ if (protocol != null) { ++ Set methods; ++ try { ++ Method[] publicMethods = clazz.getMethods(); ++ Method[] privateMethods = clazz.getDeclaredMethods(); ++ methods = new HashSet<>(publicMethods.length + privateMethods.length, 1.0f); ++ Collections.addAll(methods, publicMethods); ++ Collections.addAll(methods, privateMethods); ++ } catch (NoClassDefFoundError error) { ++ LOGGER.severe("Failed to load class " + clazz.getName() + " due to missing dependencies, " + error.getCause() + ": " + error.getMessage()); ++ return; ++ } ++ ++ Map map = KNOWN_TYPES.getOrDefault(protocol, new HashMap<>()); ++ for (final Method method : methods) { ++ if (method.isBridge() || method.isSynthetic() || !Modifier.isStatic(method.getModifiers())) { ++ continue; ++ } ++ ++ method.setAccessible(true); ++ ++ final ProtocolHandler.Init init = method.getAnnotation(ProtocolHandler.Init.class); ++ if (init != null) { ++ try { ++ method.invoke(null); ++ } catch (InvocationTargetException | IllegalAccessException exception) { ++ LOGGER.severe("Failed to invoke init method " + method.getName() + " in " + clazz.getName() + ", " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ continue; ++ } ++ ++ final ProtocolHandler.PayloadReceiver receiver = method.getAnnotation(ProtocolHandler.PayloadReceiver.class); ++ if (receiver != null) { ++ try { ++ boolean found = false; ++ for (Method payloadMethod : receiver.payload().getDeclaredMethods()) { ++ if (payloadMethod.isAnnotationPresent(LeavesCustomPayload.New.class)) { ++ if (Arrays.equals(payloadMethod.getParameterTypes(), PAYLOAD_PARAMETER_TYPES) && payloadMethod.getReturnType() == receiver.payload() && Modifier.isStatic(payloadMethod.getModifiers())) { ++ payloadMethod.setAccessible(true); ++ map.put(receiver, payloadMethod); ++ found = true; ++ break; ++ } ++ } ++ } ++ ++ if (!found) { ++ Constructor> constructor = receiver.payload().getConstructor(PAYLOAD_PARAMETER_TYPES); ++ if (constructor.isAnnotationPresent(LeavesCustomPayload.New.class)) { ++ constructor.setAccessible(true); ++ map.put(receiver, constructor); ++ } else { ++ throw new NoSuchMethodException(); ++ } ++ } ++ } catch (NoSuchMethodException exception) { ++ LOGGER.severe("Failed to find constructor for " + receiver.payload().getName() + ", " + exception.getCause() + ": " + exception.getMessage()); ++ continue; ++ } ++ ++ if (!KNOW_RECEIVERS.containsKey(protocol)) { ++ KNOW_RECEIVERS.put(protocol, new HashMap<>()); ++ } ++ ++ KNOW_RECEIVERS.get(protocol).put(receiver, method); ++ continue; ++ } ++ ++ final ProtocolHandler.Ticker ticker = method.getAnnotation(ProtocolHandler.Ticker.class); ++ if (ticker != null) { ++ TICKERS.add(method); ++ continue; ++ } ++ ++ final ProtocolHandler.PlayerJoin playerJoin = method.getAnnotation(ProtocolHandler.PlayerJoin.class); ++ if (playerJoin != null) { ++ PLAYER_JOIN.add(method); ++ continue; ++ } ++ ++ final ProtocolHandler.PlayerLeave playerLeave = method.getAnnotation(ProtocolHandler.PlayerLeave.class); ++ if (playerLeave != null) { ++ PLAYER_LEAVE.add(method); ++ continue; ++ } ++ ++ final ProtocolHandler.ReloadServer reloadServer = method.getAnnotation(ProtocolHandler.ReloadServer.class); ++ if (reloadServer != null) { ++ RELOAD_SERVER.add(method); ++ continue; ++ } ++ ++ final ProtocolHandler.MinecraftRegister minecraftRegister = method.getAnnotation(ProtocolHandler.MinecraftRegister.class); ++ if (minecraftRegister != null) { ++ if (!MINECRAFT_REGISTER.containsKey(protocol)) { ++ MINECRAFT_REGISTER.put(protocol, new HashMap<>()); ++ } ++ ++ MINECRAFT_REGISTER.get(protocol).put(minecraftRegister, method); ++ } ++ } ++ KNOWN_TYPES.put(protocol, map); ++ } ++ } ++ ++ for (LeavesProtocol protocol : KNOWN_TYPES.keySet()) { ++ Map map = KNOWN_TYPES.get(protocol); ++ for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) { ++ if (receiver.sendFabricRegister() && !receiver.ignoreId()) { ++ for (String payloadId : receiver.payloadId()) { ++ for (String namespace : protocol.namespace()) { ++ ALL_KNOWN_ID.add(new ResourceLocation(namespace, payloadId)); ++ } ++ } ++ } ++ } ++ } ++ ALL_KNOWN_ID = ImmutableSet.copyOf(ALL_KNOWN_ID); ++ } ++ ++ public static LeavesCustomPayload decode(ResourceLocation id, FriendlyByteBuf buf) { ++ for (LeavesProtocol protocol : KNOWN_TYPES.keySet()) { ++ if (!ArrayUtils.contains(protocol.namespace(), id.getNamespace())) { ++ continue; ++ } ++ ++ Map map = KNOWN_TYPES.get(protocol); ++ for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) { ++ if (receiver.ignoreId() || ArrayUtils.contains(receiver.payloadId(), id.getPath())) { ++ try { ++ if (map.get(receiver) instanceof Constructor constructor) { ++ return (LeavesCustomPayload) constructor.newInstance(id, buf); ++ } else if (map.get(receiver) instanceof Method method) { ++ return (LeavesCustomPayload) method.invoke(null, id, buf); ++ } ++ } catch (InvocationTargetException | InstantiationException | IllegalAccessException exception) { ++ LOGGER.warning("Failed to create payload for " + id + " in " + ArrayUtils.toString(protocol.namespace()) + ", " + exception.getCause() + ": " + exception.getMessage()); ++ buf.readBytes(buf.readableBytes()); ++ return new ErrorPayload(id, protocol.namespace(), receiver.payloadId()); ++ } ++ } ++ } ++ } ++ return null; ++ } ++ ++ public static void handlePayload(ServerPlayer player, LeavesCustomPayload payload) { ++ if (payload instanceof ErrorPayload errorPayload) { ++ player.connection.disconnect(Component.literal("Payload " + Arrays.toString(errorPayload.packetID) + " from " + Arrays.toString(errorPayload.protocolID) + " error"), PlayerKickEvent.Cause.INVALID_PAYLOAD); ++ return; ++ } ++ ++ for (LeavesProtocol protocol : KNOW_RECEIVERS.keySet()) { ++ if (!ArrayUtils.contains(protocol.namespace(), payload.type().id().getNamespace())) { ++ continue; ++ } ++ ++ Map map = KNOW_RECEIVERS.get(protocol); ++ for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) { ++ if (payload.getClass() == receiver.payload()) { ++ if (receiver.ignoreId() || ArrayUtils.contains(receiver.payloadId(), payload.type().id().getPath())) { ++ try { ++ map.get(receiver).invoke(null, player, payload); ++ } catch (InvocationTargetException | IllegalAccessException exception) { ++ LOGGER.warning("Failed to handle payload " + payload.type().id() + " in " + ArrayUtils.toString(protocol.namespace()) + ", " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ } ++ } ++ } ++ } ++ } ++ ++ public static void handleTick() { ++ if (!TICKERS.isEmpty()) { ++ try { ++ for (Method method : TICKERS) { ++ method.invoke(null); ++ } ++ } catch (InvocationTargetException | IllegalAccessException exception) { ++ LOGGER.warning("Failed to tick, " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ } ++ } ++ ++ public static void handlePlayerJoin(ServerPlayer player) { ++ if (!PLAYER_JOIN.isEmpty()) { ++ try { ++ for (Method method : PLAYER_JOIN) { ++ method.invoke(null, player); ++ } ++ } catch (InvocationTargetException | IllegalAccessException exception) { ++ LOGGER.warning("Failed to handle player join, " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ } ++ ++ ProtocolUtils.sendPayloadPacket(player, new FabricRegisterPayload(ALL_KNOWN_ID)); ++ } ++ ++ public static void handlePlayerLeave(ServerPlayer player) { ++ if (!PLAYER_LEAVE.isEmpty()) { ++ try { ++ for (Method method : PLAYER_LEAVE) { ++ method.invoke(null, player); ++ } ++ } catch (InvocationTargetException | IllegalAccessException exception) { ++ LOGGER.warning("Failed to handle player leave, " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ } ++ } ++ ++ public static void handleServerReload() { ++ if (!RELOAD_SERVER.isEmpty()) { ++ try { ++ for (Method method : RELOAD_SERVER) { ++ method.invoke(null); ++ } ++ } catch (InvocationTargetException | IllegalAccessException exception) { ++ LOGGER.warning("Failed to handle server reload, " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ } ++ } ++ ++ public static void handleMinecraftRegister(String channelId, ServerPlayer player) { ++ for (LeavesProtocol protocol : MINECRAFT_REGISTER.keySet()) { ++ String[] channel = channelId.split(":"); ++ if (!ArrayUtils.contains(protocol.namespace(), channel[0])) { ++ continue; ++ } ++ ++ Map map = MINECRAFT_REGISTER.get(protocol); ++ for (ProtocolHandler.MinecraftRegister register : map.keySet()) { ++ if (register.ignoreId() || register.channelId().equals(channel[1]) || ++ ArrayUtils.contains(register.channelIds(), channel[1])) { ++ try { ++ map.get(register).invoke(null, player); ++ } catch (InvocationTargetException | IllegalAccessException exception) { ++ LOGGER.warning("Failed to handle minecraft register, " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ } ++ } ++ } ++ } ++ ++ public static 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 exception) { ++ LOGGER.warning("Failed to load jar file, " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ } ++ } ++ } catch (IOException exception) { ++ LOGGER.warning("Failed to load classes, " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ return classes; ++ } ++ ++ 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 exception) { ++ LOGGER.warning("Failed to load class " + className + ", " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ } ++ } ++ } ++ } ++ ++ private static void findClassesInPackageByJar(String packageName, Enumeration entries, String packageDirName, Set> classes) { ++ while (entries.hasMoreElements()) { ++ JarEntry entry = entries.nextElement(); ++ String name = entry.getName(); ++ if (name.charAt(0) == '/') { ++ name = name.substring(1); ++ } ++ if (name.startsWith(packageDirName)) { ++ int idx = name.lastIndexOf('/'); ++ 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 exception) { ++ LOGGER.warning("Failed to load class " + className + ", " + exception.getCause() + ": " + exception.getMessage()); ++ } ++ } ++ } ++ } ++ } ++ ++ public record ErrorPayload(ResourceLocation id, String[] protocolID, ++ String[] packetID) implements LeavesCustomPayload { ++ @Override ++ public void write(@NotNull FriendlyByteBuf buf) { ++ } ++ } ++ ++ public record EmptyPayload(ResourceLocation id) implements LeavesCustomPayload { ++ ++ @New ++ public EmptyPayload(ResourceLocation location, FriendlyByteBuf buf) { ++ this(location); ++ } ++ ++ @Override ++ public void write(@NotNull FriendlyByteBuf buf) { ++ } ++ } ++ ++ public record LeavesPayload(FriendlyByteBuf data, ++ ResourceLocation id) implements LeavesCustomPayload { ++ ++ @New ++ public LeavesPayload(ResourceLocation location, FriendlyByteBuf buf) { ++ this(new FriendlyByteBuf(buf.readBytes(buf.readableBytes())), location); ++ } ++ ++ @Override ++ public void write(FriendlyByteBuf buf) { ++ buf.writeBytes(data); ++ } ++ } ++ ++ public record FabricRegisterPayload( ++ Set channels) implements LeavesCustomPayload { ++ ++ public static final ResourceLocation CHANNEL = ResourceLocation.withDefaultNamespace("register"); ++ ++ @New ++ public FabricRegisterPayload(ResourceLocation location, FriendlyByteBuf buf) { ++ this(buf.readCollection(HashSet::new, FriendlyByteBuf::readResourceLocation)); ++ } ++ ++ @Override ++ public void write(FriendlyByteBuf buf) { ++ boolean first = true; ++ ++ ResourceLocation channel; ++ for (Iterator var3 = this.channels.iterator(); var3.hasNext(); buf.writeBytes(channel.toString().getBytes(StandardCharsets.US_ASCII))) { ++ channel = var3.next(); ++ if (first) { ++ first = false; ++ } else { ++ buf.writeByte(0); ++ } ++ } ++ } ++ ++ @Override ++ public ResourceLocation id() { ++ return CHANNEL; ++ } ++ } ++} +diff --git a/src/main/java/org/leavesmc/leaves/protocol/core/ProtocolHandler.java b/src/main/java/org/leavesmc/leaves/protocol/core/ProtocolHandler.java +new file mode 100644 +index 0000000000000000000000000000000000000000..9d71f8e6af24301bedf60f5c87e0bb3c1697d5e3 +--- /dev/null ++++ b/src/main/java/org/leavesmc/leaves/protocol/core/ProtocolHandler.java +@@ -0,0 +1,63 @@ ++package org.leavesmc.leaves.protocol.core; ++ ++import java.lang.annotation.ElementType; ++import java.lang.annotation.Retention; ++import java.lang.annotation.RetentionPolicy; ++import java.lang.annotation.Target; ++ ++public class ProtocolHandler { ++ ++ @Target(ElementType.METHOD) ++ @Retention(RetentionPolicy.RUNTIME) ++ public @interface Init { ++ ++ } ++ ++ @Target(ElementType.METHOD) ++ @Retention(RetentionPolicy.RUNTIME) ++ public @interface PayloadReceiver { ++ ++ Class> payload(); ++ ++ String[] payloadId() default ""; ++ ++ boolean ignoreId() default false; ++ ++ boolean sendFabricRegister() default true; ++ } ++ ++ @Target(ElementType.METHOD) ++ @Retention(RetentionPolicy.RUNTIME) ++ public @interface Ticker { ++ int delay() default 0; ++ } ++ ++ @Target(ElementType.METHOD) ++ @Retention(RetentionPolicy.RUNTIME) ++ public @interface PlayerJoin { ++ ++ } ++ ++ @Target(ElementType.METHOD) ++ @Retention(RetentionPolicy.RUNTIME) ++ public @interface PlayerLeave { ++ ++ } ++ ++ @Target(ElementType.METHOD) ++ @Retention(RetentionPolicy.RUNTIME) ++ public @interface ReloadServer { ++ ++ } ++ ++ @Target(ElementType.METHOD) ++ @Retention(RetentionPolicy.RUNTIME) ++ public @interface MinecraftRegister { ++ ++ String channelId() default ""; ++ ++ String[] channelIds() default {}; ++ ++ boolean ignoreId() default false; ++ } ++} +diff --git a/src/main/java/org/leavesmc/leaves/protocol/core/ProtocolUtils.java b/src/main/java/org/leavesmc/leaves/protocol/core/ProtocolUtils.java +new file mode 100644 +index 0000000000000000000000000000000000000000..f54381eec7ec0ffde39e87b39b16e02d3ed1b419 +--- /dev/null ++++ b/src/main/java/org/leavesmc/leaves/protocol/core/ProtocolUtils.java +@@ -0,0 +1,47 @@ ++package org.leavesmc.leaves.protocol.core; ++ ++import io.netty.buffer.ByteBuf; ++import net.minecraft.network.FriendlyByteBuf; ++import net.minecraft.network.RegistryFriendlyByteBuf; ++import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; ++import net.minecraft.network.protocol.common.custom.CustomPacketPayload; ++import net.minecraft.resources.ResourceLocation; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.level.ServerPlayer; ++import org.jetbrains.annotations.NotNull; ++ ++import java.util.function.Consumer; ++import java.util.function.Function; ++ ++public class ProtocolUtils { ++ ++ private static final Function bufDecorator = RegistryFriendlyByteBuf.decorator(MinecraftServer.getServer().registryAccess()); ++ ++ public static void sendEmptyPayloadPacket(ServerPlayer player, ResourceLocation id) { ++ player.connection.send(new ClientboundCustomPayloadPacket(new LeavesProtocolManager.EmptyPayload(id))); ++ } ++ ++ @SuppressWarnings("all") ++ public static void sendPayloadPacket(@NotNull ServerPlayer player, ResourceLocation id, Consumer consumer) { ++ player.connection.send(new ClientboundCustomPayloadPacket(new LeavesCustomPayload() { ++ @Override ++ public void write(@NotNull FriendlyByteBuf buf) { ++ consumer.accept(buf); ++ } ++ ++ @Override ++ @NotNull ++ public ResourceLocation id() { ++ return id; ++ } ++ })); ++ } ++ ++ public static void sendPayloadPacket(ServerPlayer player, CustomPacketPayload payload) { ++ player.connection.send(new ClientboundCustomPayloadPacket(payload)); ++ } ++ ++ public static RegistryFriendlyByteBuf decorate(ByteBuf buf) { ++ return bufDecorator.apply(buf); ++ } ++} diff --git a/patches/wip/0017-Leaves-Appleskin-Protocol.patch b/patches/server/0023-Leaves-Appleskin-Protocol.patch similarity index 75% rename from patches/wip/0017-Leaves-Appleskin-Protocol.patch rename to patches/server/0023-Leaves-Appleskin-Protocol.patch index 5ec6ab3..9637f57 100644 --- a/patches/wip/0017-Leaves-Appleskin-Protocol.patch +++ b/patches/server/0023-Leaves-Appleskin-Protocol.patch @@ -1,36 +1,37 @@ From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: AltronMaxX -Date: Fri, 22 Dec 2023 20:00:45 +0400 +Date: Tue, 6 Aug 2024 15:08:16 +0400 Subject: [PATCH] Leaves-Appleskin-Protocol diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -index 9a67b0107f4b79911b4d1e676e4d97c7ff8466a9..3e4af65b58540520a7cde6da06aedadff6e2369c 100644 +index c36cac84603cd0c66c332eff8d55e16b7a5d3428..8e901c59e1310d70552b3853d3f5e721aad71a79 100644 --- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java -@@ -25,6 +25,7 @@ public class FoldenorConfig { - static boolean verbose; +@@ -39,6 +39,8 @@ public class FoldenorConfig { + public static int activationDistanceMod; + // Pufferfish DAB - public static boolean sendNullEntityPackets = true; + public static boolean appleskinProtocol = false; ++ + public static void init(File configFile) { + init(configFile, true); + } +@@ -98,6 +100,7 @@ public class FoldenorConfig { - public static boolean vanilaEndPortalTeleportation = false; - -@@ -125,6 +126,7 @@ public class FoldenorConfig { - - private static void networkSettings() { + protected static void readNetworkSettings(){ sendNullEntityPackets = getBoolean("network.send-null-entity-packets", sendNullEntityPackets); + appleskinProtocol = getBoolean("network.appleskin-protocol", appleskinProtocol); } - protected static void set(String path, Object val) { -diff --git a/src/main/java/top/leavesmc/leaves/protocol/AppleSkinProtocol.java b/src/main/java/top/leavesmc/leaves/protocol/AppleSkinProtocol.java + private static void readOptimizationSettings() { +diff --git a/src/main/java/org/leavesmc/leaves/protocol/AppleSkinProtocol.java b/src/main/java/org/leavesmc/leaves/protocol/AppleSkinProtocol.java new file mode 100644 -index 0000000000000000000000000000000000000000..462677d45c0530689a152f59dcea6eabcdfb101d +index 0000000000000000000000000000000000000000..a8694777f0619584c76c9cba47f7926435b768a9 --- /dev/null -+++ b/src/main/java/top/leavesmc/leaves/protocol/AppleSkinProtocol.java -@@ -0,0 +1,106 @@ -+package top.leavesmc.leaves.protocol; ++++ b/src/main/java/org/leavesmc/leaves/protocol/AppleSkinProtocol.java +@@ -0,0 +1,105 @@ ++package org.leavesmc.leaves.protocol; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; @@ -38,10 +39,9 @@ index 0000000000000000000000000000000000000000..462677d45c0530689a152f59dcea6eab +import net.minecraft.world.food.FoodData; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; -+import top.leavesmc.leaves.protocol.core.LeavesProtocol; -+import top.leavesmc.leaves.protocol.core.ProtocolHandler; -+import top.leavesmc.leaves.protocol.core.ProtocolUtils; -+import net.edenor.foldenor.config.FoldenorConfig; ++import org.leavesmc.leaves.protocol.core.LeavesProtocol; ++import org.leavesmc.leaves.protocol.core.ProtocolHandler; ++import org.leavesmc.leaves.protocol.core.ProtocolUtils; + +import java.util.HashMap; +import java.util.HashSet; @@ -71,14 +71,14 @@ index 0000000000000000000000000000000000000000..462677d45c0530689a152f59dcea6eab + + @ProtocolHandler.PlayerJoin + public static void onPlayerLoggedIn(@NotNull ServerPlayer player) { -+ if (FoldenorConfig.appleskinProtocol) { ++ if (net.edenor.foldenor.config.FoldenorConfig.appleskinProtocol) { + resetPlayerData(player); + } + } + + @ProtocolHandler.PlayerLeave + public static void onPlayerLoggedOut(@NotNull ServerPlayer player) { -+ if (FoldenorConfig.appleskinProtocol) { ++ if (net.edenor.foldenor.config.FoldenorConfig.appleskinProtocol) { + players.remove(player); + resetPlayerData(player); + } @@ -86,14 +86,14 @@ index 0000000000000000000000000000000000000000..462677d45c0530689a152f59dcea6eab + + @ProtocolHandler.MinecraftRegister(ignoreId = true) + public static void onPlayerSubscribed(@NotNull ServerPlayer player) { -+ if (FoldenorConfig.appleskinProtocol) { ++ if (net.edenor.foldenor.config.FoldenorConfig.appleskinProtocol) { + players.add(player); + } + } + + @ProtocolHandler.Ticker + public static void tick() { -+ if (FoldenorConfig.appleskinProtocol) { ++ if (net.edenor.foldenor.config.FoldenorConfig.appleskinProtocol) { + for (ServerPlayer player : players) { + FoodData data = player.getFoodData(); + @@ -120,7 +120,7 @@ index 0000000000000000000000000000000000000000..462677d45c0530689a152f59dcea6eab + + @ProtocolHandler.ReloadServer + public static void onServerReload() { -+ if (!FoldenorConfig.appleskinProtocol) { ++ if (!net.edenor.foldenor.config.FoldenorConfig.appleskinProtocol) { + disableAllPlayer(); + } + } diff --git a/patches/server/0024-Gale-Faster-chunk-serialization.patch b/patches/server/0024-Gale-Faster-chunk-serialization.patch new file mode 100644 index 0000000..4c77a92 --- /dev/null +++ b/patches/server/0024-Gale-Faster-chunk-serialization.patch @@ -0,0 +1,719 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 15:19:02 +0400 +Subject: [PATCH] Gale-Faster-chunk-serialization + + +diff --git a/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/LithiumHashPalette.java b/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/LithiumHashPalette.java +new file mode 100644 +index 0000000000000000000000000000000000000000..491bb572ef804c4337e624b8ca83bf7aac755a75 +--- /dev/null ++++ b/src/main/java/me/jellysquid/mods/lithium/common/world/chunk/LithiumHashPalette.java +@@ -0,0 +1,194 @@ ++// Gale - Lithium - faster chunk serialization ++ ++package me.jellysquid.mods.lithium.common.world.chunk; ++ ++import com.google.common.collect.ImmutableList; ++import it.unimi.dsi.fastutil.HashCommon; ++import it.unimi.dsi.fastutil.objects.Reference2IntMap; ++import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; ++ ++import java.util.Arrays; ++import java.util.List; ++import java.util.function.Predicate; ++ ++import net.minecraft.core.IdMap; ++import net.minecraft.network.FriendlyByteBuf; ++import net.minecraft.network.VarInt; ++import net.minecraft.world.level.chunk.Palette; ++import net.minecraft.world.level.chunk.PaletteResize; ++ ++import static it.unimi.dsi.fastutil.Hash.FAST_LOAD_FACTOR; ++ ++/** ++ * Generally provides better performance over the vanilla {@link net.minecraft.world.level.chunk.HashMapPalette} when calling ++ * {@link LithiumHashPalette#idFor(Object)} through using a faster backing map and reducing pointer chasing. ++ */ ++public class LithiumHashPalette implements Palette { ++ private static final int ABSENT_VALUE = -1; ++ ++ private final IdMap idList; ++ private final PaletteResize resizeHandler; ++ private final int indexBits; ++ ++ private final Reference2IntMap table; ++ private T[] entries; ++ private int size = 0; ++ ++ public LithiumHashPalette(IdMap idList, PaletteResize resizeHandler, int indexBits, T[] entries, Reference2IntMap table, int size) { ++ this.idList = idList; ++ this.resizeHandler = resizeHandler; ++ this.indexBits = indexBits; ++ this.entries = entries; ++ this.table = table; ++ this.size = size; ++ } ++ ++ public LithiumHashPalette(IdMap idList, int bits, PaletteResize resizeHandler, List list) { ++ this(idList, bits, resizeHandler); ++ ++ for (T t : list) { ++ this.addEntry(t); ++ } ++ } ++ ++ @SuppressWarnings("unchecked") ++ public LithiumHashPalette(IdMap idList, int bits, PaletteResize resizeHandler) { ++ this.idList = idList; ++ this.indexBits = bits; ++ this.resizeHandler = resizeHandler; ++ ++ int capacity = 1 << bits; ++ ++ this.entries = (T[]) new Object[capacity]; ++ this.table = new Reference2IntOpenHashMap<>(capacity, FAST_LOAD_FACTOR); ++ this.table.defaultReturnValue(ABSENT_VALUE); ++ } ++ ++ @Override ++ public int idFor(T obj) { ++ int id = this.table.getInt(obj); ++ ++ if (id == ABSENT_VALUE) { ++ id = this.computeEntry(obj); ++ } ++ ++ return id; ++ } ++ ++ @Override ++ public boolean maybeHas(Predicate predicate) { ++ for (int i = 0; i < this.size; ++i) { ++ if (predicate.test(this.entries[i])) { ++ return true; ++ } ++ } ++ ++ return false; ++ } ++ ++ private int computeEntry(T obj) { ++ int id = this.addEntry(obj); ++ ++ if (id >= 1 << this.indexBits) { ++ if (this.resizeHandler == null) { ++ throw new IllegalStateException("Cannot grow"); ++ } else { ++ id = this.resizeHandler.onResize(this.indexBits + 1, obj); ++ } ++ } ++ ++ return id; ++ } ++ ++ private int addEntry(T obj) { ++ int nextId = this.size; ++ ++ if (nextId >= this.entries.length) { ++ this.resize(this.size); ++ } ++ ++ this.table.put(obj, nextId); ++ this.entries[nextId] = obj; ++ ++ this.size++; ++ ++ return nextId; ++ } ++ ++ private void resize(int neededCapacity) { ++ this.entries = Arrays.copyOf(this.entries, HashCommon.nextPowerOfTwo(neededCapacity + 1)); ++ } ++ ++ @Override ++ public T valueFor(int id) { ++ T[] entries = this.entries; ++ ++ if (id >= 0 && id < entries.length) { ++ return entries[id]; ++ } ++ ++ return null; ++ } ++ ++ @Override ++ public void read(FriendlyByteBuf buf) { ++ this.clear(); ++ ++ int entryCount = buf.readVarInt(); ++ ++ for (int i = 0; i < entryCount; ++i) { ++ this.addEntry(this.idList.byId(buf.readVarInt())); ++ } ++ } ++ ++ @Override ++ public void write(FriendlyByteBuf buf) { ++ int size = this.size; ++ buf.writeVarInt(size); ++ ++ for (int i = 0; i < size; ++i) { ++ buf.writeVarInt(this.idList.getId(this.valueFor(i))); ++ } ++ } ++ ++ @Override ++ public int getSerializedSize() { ++ int size = VarInt.getByteSize(this.size); ++ ++ for (int i = 0; i < this.size; ++i) { ++ size += VarInt.getByteSize(this.idList.getId(this.valueFor(i))); ++ } ++ ++ return size; ++ } ++ ++ @Override ++ public int getSize() { ++ return this.size; ++ } ++ ++ @Override ++ public Palette copy() { ++ return new LithiumHashPalette<>(this.idList, this.resizeHandler, this.indexBits, this.entries.clone(), new Reference2IntOpenHashMap<>(this.table), this.size); ++ } ++ ++ private void clear() { ++ Arrays.fill(this.entries, null); ++ this.table.clear(); ++ this.size = 0; ++ } ++ ++ public List getElements() { ++ ImmutableList.Builder builder = new ImmutableList.Builder<>(); ++ for (T entry : this.entries) { ++ if (entry != null) { ++ builder.add(entry); ++ } ++ } ++ return builder.build(); ++ } ++ ++ public static Palette create(int bits, IdMap idList, PaletteResize listener, List list) { ++ return new LithiumHashPalette<>(idList, bits, listener, list); ++ } ++} +diff --git a/src/main/java/net/minecraft/util/BitStorage.java b/src/main/java/net/minecraft/util/BitStorage.java +index 19661e106612b8e4e152085fb398db7bd06acc23..f852d2c0c0e37df78dbea28e183aee3572978dee 100644 +--- a/src/main/java/net/minecraft/util/BitStorage.java ++++ b/src/main/java/net/minecraft/util/BitStorage.java +@@ -1,6 +1,7 @@ + package net.minecraft.util; + + import java.util.function.IntConsumer; ++import net.minecraft.world.level.chunk.Palette; + + public interface BitStorage extends ca.spottedleaf.moonrise.patches.block_counting.BlockCountingBitStorage { // Paper - block counting + int getAndSet(int index, int value); +@@ -38,4 +39,6 @@ public interface BitStorage extends ca.spottedleaf.moonrise.patches.block_counti + return ret; + } + // Paper end - block counting ++ ++ void compact(Palette srcPalette, Palette dstPalette, short[] out); // Gale - Lithium - faster chunk serialization + } +diff --git a/src/main/java/net/minecraft/util/SimpleBitStorage.java b/src/main/java/net/minecraft/util/SimpleBitStorage.java +index 8acf2f2491a8d9d13392c5e89b2bd5c9918285e1..23a323651eed1be8b84fe75ed19926c26fb55097 100644 +--- a/src/main/java/net/minecraft/util/SimpleBitStorage.java ++++ b/src/main/java/net/minecraft/util/SimpleBitStorage.java +@@ -2,6 +2,8 @@ package net.minecraft.util; + + import java.util.function.IntConsumer; + import javax.annotation.Nullable; ++ ++import net.minecraft.world.level.chunk.Palette; + import org.apache.commons.lang3.Validate; + + public class SimpleBitStorage implements BitStorage { +@@ -204,8 +206,10 @@ public class SimpleBitStorage implements BitStorage { + private final long mask; + private final int size; + private final int valuesPerLong; +- private final int divideMul; private final long divideMulUnsigned; // Paper - Perf: Optimize SimpleBitStorage; referenced in b(int) with 2 Integer.toUnsignedLong calls +- private final int divideAdd; private final long divideAddUnsigned; // Paper - Perf: Optimize SimpleBitStorage ++ private final int divideMul; ++ private final long divideMulUnsigned; // Paper - Perf: Optimize SimpleBitStorage; referenced in b(int) with 2 Integer.toUnsignedLong calls ++ private final int divideAdd; ++ private final long divideAddUnsigned; // Paper - Perf: Optimize SimpleBitStorage + private final int divideShift; + + public SimpleBitStorage(int elementBits, int size, int[] data) { +@@ -218,7 +222,7 @@ public class SimpleBitStorage implements BitStorage { + + for (int k = this.valuesPerLong - 1; k >= 0; k--) { + l <<= elementBits; +- l |= (long)data[j + k] & this.mask; ++ l |= (long) data[j + k] & this.mask; + } + + this.data[i++] = l; +@@ -230,7 +234,7 @@ public class SimpleBitStorage implements BitStorage { + + for (int o = m - 1; o >= 0; o--) { + n <<= elementBits; +- n |= (long)data[j + o] & this.mask; ++ n |= (long) data[j + o] & this.mask; + } + + this.data[i] = n; +@@ -238,18 +242,20 @@ public class SimpleBitStorage implements BitStorage { + } + + public SimpleBitStorage(int elementBits, int size) { +- this(elementBits, size, (long[])null); ++ this(elementBits, size, (long[]) null); + } + + public SimpleBitStorage(int elementBits, int size, @Nullable long[] data) { +- Validate.inclusiveBetween(1L, 32L, (long)elementBits); ++ Validate.inclusiveBetween(1L, 32L, (long) elementBits); + this.size = size; + this.bits = elementBits; + this.mask = (1L << elementBits) - 1L; +- this.valuesPerLong = (char)(64 / elementBits); ++ this.valuesPerLong = (char) (64 / elementBits); + int i = 3 * (this.valuesPerLong - 1); +- this.divideMul = MAGIC[i + 0]; this.divideMulUnsigned = Integer.toUnsignedLong(this.divideMul); // Paper - Perf: Optimize SimpleBitStorage +- this.divideAdd = MAGIC[i + 1]; this.divideAddUnsigned = Integer.toUnsignedLong(this.divideAdd); // Paper - Perf: Optimize SimpleBitStorage ++ this.divideMul = MAGIC[i + 0]; ++ this.divideMulUnsigned = Integer.toUnsignedLong(this.divideMul); // Paper - Perf: Optimize SimpleBitStorage ++ this.divideAdd = MAGIC[i + 1]; ++ this.divideAddUnsigned = Integer.toUnsignedLong(this.divideAdd); // Paper - Perf: Optimize SimpleBitStorage + this.divideShift = MAGIC[i + 2]; + int j = (size + this.valuesPerLong - 1) / this.valuesPerLong; + if (data != null) { +@@ -276,8 +282,8 @@ public class SimpleBitStorage implements BitStorage { + int i = this.cellIndex(index); + long l = this.data[i]; + int j = (index - i * this.valuesPerLong) * this.bits; +- int k = (int)(l >> j & this.mask); +- this.data[i] = l & ~(this.mask << j) | ((long)value & this.mask) << j; ++ int k = (int) (l >> j & this.mask); ++ this.data[i] = l & ~(this.mask << j) | ((long) value & this.mask) << j; + return k; + } + +@@ -288,7 +294,7 @@ public class SimpleBitStorage implements BitStorage { + int i = this.cellIndex(index); + long l = this.data[i]; + int j = (index - i * this.valuesPerLong) * this.bits; +- this.data[i] = l & ~(this.mask << j) | ((long)value & this.mask) << j; ++ this.data[i] = l & ~(this.mask << j) | ((long) value & this.mask) << j; + } + + @Override +@@ -297,7 +303,7 @@ public class SimpleBitStorage implements BitStorage { + int i = this.cellIndex(index); + long l = this.data[i]; + int j = (index - i * this.valuesPerLong) * this.bits; +- return (int)(l >> j & this.mask); ++ return (int) (l >> j & this.mask); + } + + @Override +@@ -321,7 +327,7 @@ public class SimpleBitStorage implements BitStorage { + + for (long l : this.data) { + for (int j = 0; j < this.valuesPerLong; j++) { +- action.accept((int)(l & this.mask)); ++ action.accept((int) (l & this.mask)); + l >>= this.bits; + if (++i >= this.size) { + return; +@@ -339,7 +345,7 @@ public class SimpleBitStorage implements BitStorage { + long l = this.data[k]; + + for (int m = 0; m < this.valuesPerLong; m++) { +- out[j + m] = (int)(l & this.mask); ++ out[j + m] = (int) (l & this.mask); + l >>= this.bits; + } + +@@ -351,7 +357,7 @@ public class SimpleBitStorage implements BitStorage { + long o = this.data[i - 1]; + + for (int p = 0; p < n; p++) { +- out[j + p] = (int)(o & this.mask); ++ out[j + p] = (int) (o & this.mask); + o >>= this.bits; + } + } +@@ -359,7 +365,7 @@ public class SimpleBitStorage implements BitStorage { + + @Override + public BitStorage copy() { +- return new SimpleBitStorage(this.bits, this.size, (long[])this.data.clone()); ++ return new SimpleBitStorage(this.bits, this.size, (long[]) this.data.clone()); + } + + // Paper start - block counting +@@ -380,7 +386,7 @@ public class SimpleBitStorage implements BitStorage { + for (long value : this.data) { + int li = 0; + do { +- final int paletteIdx = (int)(value & mask); ++ final int paletteIdx = (int) (value & mask); + value >>= bits; + + ret.computeIfAbsent(paletteIdx, (final int key) -> { +@@ -401,4 +407,44 @@ public class SimpleBitStorage implements BitStorage { + super(message); + } + } ++ ++ // Gale start - Lithium - faster chunk serialization ++ @Override ++ public void compact(Palette srcPalette, Palette dstPalette, short[] out) { ++ if (this.size >= Short.MAX_VALUE) { ++ throw new IllegalStateException("Array too large"); ++ } ++ ++ if (this.size != out.length) { ++ throw new IllegalStateException("Array size mismatch"); ++ } ++ ++ short[] mappings = new short[(int) (this.mask + 1)]; ++ ++ int idx = 0; ++ ++ for (long word : this.data) { ++ long bits = word; ++ ++ for (int elementIdx = 0; elementIdx < this.valuesPerLong; ++elementIdx) { ++ int value = (int) (bits & this.mask); ++ int remappedId = mappings[value]; ++ ++ if (remappedId == 0) { ++ remappedId = dstPalette.idFor(srcPalette.valueFor(value)) + 1; ++ mappings[value] = (short) remappedId; ++ } ++ ++ out[idx] = (short) (remappedId - 1); ++ bits >>= this.bits; ++ ++ ++idx; ++ ++ if (idx >= this.size) { ++ return; ++ } ++ } ++ } ++ } ++ // Gale end - Lithium - faster chunk serialization + } +diff --git a/src/main/java/net/minecraft/util/ZeroBitStorage.java b/src/main/java/net/minecraft/util/ZeroBitStorage.java +index 15c5164d0ef41a978c16ee317fa73e97f2480207..99d82bfe7a60ee3561686c4d2f34d0185155ae66 100644 +--- a/src/main/java/net/minecraft/util/ZeroBitStorage.java ++++ b/src/main/java/net/minecraft/util/ZeroBitStorage.java +@@ -80,4 +80,6 @@ public class ZeroBitStorage implements BitStorage { + return ret; + } + // Paper end - block counting ++ ++ @Override public void compact(net.minecraft.world.level.chunk.Palette srcPalette, net.minecraft.world.level.chunk.Palette dstPalette, short[] out) {} // Gale - Lithium - faster chunk serialization + } +diff --git a/src/main/java/net/minecraft/world/level/chunk/PaletteResize.java b/src/main/java/net/minecraft/world/level/chunk/PaletteResize.java +index acae3eb30e0689048937f479dc3070f0688abdad..4b79f0474a9013dd4fdb68c6363ca1942ba8b007 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/PaletteResize.java ++++ b/src/main/java/net/minecraft/world/level/chunk/PaletteResize.java +@@ -1,5 +1,5 @@ + package net.minecraft.world.level.chunk; + +-interface PaletteResize { ++public interface PaletteResize { // Gale - Lithium - faster chunk serialization - package -> public + int onResize(int newBits, T object); + } +diff --git a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +index 13d3c877b006a4975e7370713e3919c661e7890f..c02d7156ec4e3017fde7510fb3e6b3205734018e 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java ++++ b/src/main/java/net/minecraft/world/level/chunk/PalettedContainer.java +@@ -6,6 +6,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder; + import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; + import it.unimi.dsi.fastutil.ints.IntArraySet; + import it.unimi.dsi.fastutil.ints.IntSet; ++ + import java.util.Arrays; + import java.util.List; + import java.util.Optional; +@@ -14,6 +15,8 @@ import java.util.function.IntUnaryOperator; + import java.util.function.Predicate; + import java.util.stream.LongStream; + import javax.annotation.Nullable; ++ ++import me.jellysquid.mods.lithium.common.world.chunk.LithiumHashPalette; + import net.minecraft.core.IdMap; + import net.minecraft.network.FriendlyByteBuf; + import net.minecraft.network.VarInt; +@@ -25,6 +28,23 @@ import net.minecraft.util.ThreadingDetector; + import net.minecraft.util.ZeroBitStorage; + + public class PalettedContainer implements PaletteResize, PalettedContainerRO { ++ // Gale start - Lithium - faster chunk serialization ++ private static final ThreadLocal CACHED_ARRAY_4096 = ThreadLocal.withInitial(() -> new short[4096]); ++ private static final ThreadLocal CACHED_ARRAY_64 = ThreadLocal.withInitial(() -> new short[64]); ++ ++ private Optional asOptional(long[] data) { ++ return Optional.of(Arrays.stream(data)); ++ } ++ ++ private short[] getOrCreate(int size) { ++ return switch (size) { ++ case 64 -> CACHED_ARRAY_64.get(); ++ case 4096 -> CACHED_ARRAY_4096.get(); ++ default -> new short[size]; ++ }; ++ } ++ ++ // Gale end - Lithium - faster chunk serialization + private static final int MIN_PALETTE_BITS = 0; + private final PaletteResize dummyPaletteResize = (newSize, added) -> 0; + public final IdMap registry; +@@ -42,7 +62,12 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + } + + // Paper start - Anti-Xray - Add preset values +- @Deprecated @io.papermc.paper.annotation.DoNotUse public static Codec> codecRW(IdMap idList, Codec entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) { return PalettedContainer.codecRW(idList, entryCodec, paletteProvider, defaultValue, null); } ++ @Deprecated ++ @io.papermc.paper.annotation.DoNotUse ++ public static Codec> codecRW(IdMap idList, Codec entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) { ++ return PalettedContainer.codecRW(idList, entryCodec, paletteProvider, defaultValue, null); ++ } ++ + public static Codec> codecRW(IdMap idList, Codec entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue, T @org.jetbrains.annotations.Nullable [] presetValues) { + PalettedContainerRO.Unpacker> unpacker = (idListx, paletteProviderx, serialized) -> { + return unpack(idListx, paletteProviderx, serialized, defaultValue, presetValues); +@@ -53,9 +78,9 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + + public static Codec> codecRO(IdMap idList, Codec entryCodec, PalettedContainer.Strategy paletteProvider, T defaultValue) { + PalettedContainerRO.Unpacker> unpacker = (idListx, paletteProviderx, serialized) -> unpack( +- idListx, paletteProviderx, serialized, defaultValue, null // Paper - Anti-Xray - Add preset values +- ) +- .map(result -> (PalettedContainerRO)result); ++ idListx, paletteProviderx, serialized, defaultValue, null // Paper - Anti-Xray - Add preset values ++ ) ++ .map(result -> (PalettedContainerRO) result); + return codec(idList, entryCodec, paletteProvider, defaultValue, unpacker); + } + +@@ -64,21 +89,26 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + ) { + return RecordCodecBuilder.create( + instance -> instance.group( +- entryCodec.mapResult(ExtraCodecs.orElsePartial(defaultValue)) +- .listOf() +- .fieldOf("palette") +- .forGetter(PalettedContainerRO.PackedData::paletteEntries), +- Codec.LONG_STREAM.lenientOptionalFieldOf("data").forGetter(PalettedContainerRO.PackedData::storage) +- ) +- .apply(instance, PalettedContainerRO.PackedData::new) ++ entryCodec.mapResult(ExtraCodecs.orElsePartial(defaultValue)) ++ .listOf() ++ .fieldOf("palette") ++ .forGetter(PalettedContainerRO.PackedData::paletteEntries), ++ Codec.LONG_STREAM.lenientOptionalFieldOf("data").forGetter(PalettedContainerRO.PackedData::storage) ++ ) ++ .apply(instance, PalettedContainerRO.PackedData::new) + ) + .comapFlatMap( +- serialized -> reader.read(idList, provider, (PalettedContainerRO.PackedData)serialized), container -> container.pack(idList, provider) ++ serialized -> reader.read(idList, provider, (PalettedContainerRO.PackedData) serialized), container -> container.pack(idList, provider) + ); + } + + // Paper start - Anti-Xray - Add preset values +- @Deprecated @io.papermc.paper.annotation.DoNotUse public PalettedContainer(IdMap idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Configuration dataProvider, BitStorage storage, List paletteEntries) { this(idList, paletteProvider, dataProvider, storage, paletteEntries, null, null); } ++ @Deprecated ++ @io.papermc.paper.annotation.DoNotUse ++ public PalettedContainer(IdMap idList, PalettedContainer.Strategy paletteProvider, PalettedContainer.Configuration dataProvider, BitStorage storage, List paletteEntries) { ++ this(idList, paletteProvider, dataProvider, storage, paletteEntries, null, null); ++ } ++ + public PalettedContainer( + IdMap idList, + PalettedContainer.Strategy paletteProvider, +@@ -125,7 +155,12 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + } + + // Paper start - Anti-Xray - Add preset values +- @Deprecated @io.papermc.paper.annotation.DoNotUse public PalettedContainer(IdMap idList, T object, PalettedContainer.Strategy paletteProvider) { this(idList, object, paletteProvider, null); } ++ @Deprecated ++ @io.papermc.paper.annotation.DoNotUse ++ public PalettedContainer(IdMap idList, T object, PalettedContainer.Strategy paletteProvider) { ++ this(idList, object, paletteProvider, null); ++ } ++ + public PalettedContainer(IdMap idList, T object, PalettedContainer.Strategy paletteProvider, T @org.jetbrains.annotations.Nullable [] presetValues) { + this.presetValues = presetValues; + // Paper end +@@ -183,7 +218,7 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + this.release(); + } + +- return (T)var5; ++ return (T) var5; + } + + public T getAndSetUnchecked(int x, int y, int z, T value) { +@@ -246,7 +281,12 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + + // Paper start - Anti-Xray; Add chunk packet info + @Override +- @Deprecated @io.papermc.paper.annotation.DoNotUse public void write(FriendlyByteBuf buf) { this.write(buf, null, 0); } ++ @Deprecated ++ @io.papermc.paper.annotation.DoNotUse ++ public void write(FriendlyByteBuf buf) { ++ this.write(buf, null, 0); ++ } ++ + @Override + public synchronized void write(FriendlyByteBuf buf, @Nullable com.destroystokyo.paper.antixray.ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { // Paper - Synchronize + this.acquire(); +@@ -304,28 +344,53 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + public synchronized PalettedContainerRO.PackedData pack(IdMap idList, PalettedContainer.Strategy paletteProvider) { // Paper - synchronize + this.acquire(); + +- PalettedContainerRO.PackedData var12; ++ // Gale start - Lithium - faster chunk serialization ++ Optional data = Optional.empty(); ++ List elements = null; + try { +- HashMapPalette hashMapPalette = new HashMapPalette<>(idList, this.data.storage.getBits(), this.dummyPaletteResize); +- int i = paletteProvider.size(); +- int[] is = new int[i]; +- this.data.storage.unpack(is); +- swapPalette(is, id -> hashMapPalette.idFor(this.data.palette.valueFor(id))); +- int j = paletteProvider.calculateBitsForSerialization(idList, hashMapPalette.getSize()); +- Optional optional; +- if (j != 0) { +- SimpleBitStorage simpleBitStorage = new SimpleBitStorage(j, i, is); +- optional = Optional.of(Arrays.stream(simpleBitStorage.getRaw())); +- } else { +- optional = Optional.empty(); ++ // The palette that will be serialized ++ LithiumHashPalette hashPalette = null; ++ ++ final Palette palette = this.data.palette(); ++ final BitStorage storage = this.data.storage(); ++ if (storage instanceof ZeroBitStorage || palette.getSize() == 1) { ++ // If the palette only contains one entry, don't attempt to repack it. ++ elements = List.of(palette.valueFor(0)); ++ } else if (palette instanceof LithiumHashPalette lithiumHashPalette) { ++ hashPalette = lithiumHashPalette; + } + +- var12 = new PalettedContainerRO.PackedData<>(hashMapPalette.getEntries(), optional); ++ if (elements == null) { ++ LithiumHashPalette compactedPalette = new LithiumHashPalette<>(idList, storage.getBits(), this.dummyPaletteResize); ++ short[] array = this.getOrCreate(paletteProvider.size()); ++ ++ storage.compact(this.data.palette(), compactedPalette, array); ++ ++ // If the palette didn't change during compaction, do a simple copy of the data array ++ if (hashPalette != null && hashPalette.getSize() == compactedPalette.getSize() && storage.getBits() == paletteProvider.calculateBitsForSerialization(idList, hashPalette.getSize())) { // paletteSize can de-sync from palette - see https://github.com/CaffeineMC/lithium-fabric/issues/279 ++ data = this.asOptional(storage.getRaw().clone()); ++ elements = hashPalette.getElements(); ++ } else { ++ int bits = paletteProvider.calculateBitsForSerialization(idList, compactedPalette.getSize()); ++ if (bits != 0) { ++ // Re-pack the integer array as the palette has changed size ++ SimpleBitStorage copy = new SimpleBitStorage(bits, array.length); ++ for (int i = 0; i < array.length; ++i) { ++ copy.set(i, array[i]); ++ } ++ ++ // We don't need to clone the data array as we are the sole owner of it ++ data = this.asOptional(copy.getRaw()); ++ } ++ ++ elements = compactedPalette.getElements(); ++ } ++ } + } finally { + this.release(); + } +- +- return var12; ++ return new PalettedContainerRO.PackedData<>(elements, data); ++ // Gale end - Lithium - faster chunk serialization + } + + private static void swapPalette(int[] is, IntUnaryOperator applier) { +@@ -364,18 +429,40 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + + @Override + public void count(PalettedContainer.CountConsumer counter) { +- if (this.data.palette.getSize() == 1) { +- counter.accept(this.data.palette.valueFor(0), this.data.storage.getSize()); +- } else { +- Int2IntOpenHashMap int2IntOpenHashMap = new Int2IntOpenHashMap(); +- this.data.storage.getAll(key -> int2IntOpenHashMap.addTo(key, 1)); +- int2IntOpenHashMap.int2IntEntrySet().forEach(entry -> counter.accept(this.data.palette.valueFor(entry.getIntKey()), entry.getIntValue())); ++ // Gale start - Lithium - faster chunk serialization ++ int len = this.data.palette().getSize(); ++ ++ // Do not allocate huge arrays if we're using a large palette ++ if (len > 4096) { ++ // VanillaCopy ++ if (this.data.palette.getSize() == 1) { ++ counter.accept(this.data.palette.valueFor(0), this.data.storage.getSize()); ++ } else { ++ Int2IntOpenHashMap int2IntOpenHashMap = new Int2IntOpenHashMap(); ++ this.data.storage.getAll((key) -> { ++ int2IntOpenHashMap.addTo(key, 1); ++ }); ++ int2IntOpenHashMap.int2IntEntrySet().forEach((entry) -> counter.accept(this.data.palette.valueFor(entry.getIntKey()), entry.getIntValue())); ++ } ++ } ++ ++ short[] counts = new short[len]; ++ ++ this.data.storage().getAll(i -> counts[i]++); ++ ++ for (int i = 0; i < counts.length; i++) { ++ T obj = this.data.palette().valueFor(i); ++ ++ if (obj != null) { ++ counter.accept(obj, counts[i]); ++ } + } ++ // Gale end - Lithium - faster chunk serialization + } + + static record Configuration(Palette.Factory factory, int bits) { + public PalettedContainer.Data createData(IdMap idList, PaletteResize listener, int size) { +- BitStorage bitStorage = (BitStorage)(this.bits == 0 ? new ZeroBitStorage(size) : new SimpleBitStorage(this.bits, size)); ++ BitStorage bitStorage = (BitStorage) (this.bits == 0 ? new ZeroBitStorage(size) : new SimpleBitStorage(this.bits, size)); + Palette palette = this.factory.create(this.bits, idList, listener, List.of()); + return new PalettedContainer.Data<>(this, bitStorage, palette); + } +@@ -430,7 +517,8 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + case 0 -> new PalettedContainer.Configuration(SINGLE_VALUE_PALETTE_FACTORY, bits); + case 1, 2, 3, 4 -> new PalettedContainer.Configuration(LINEAR_PALETTE_FACTORY, 4); + case 5, 6, 7, 8 -> new PalettedContainer.Configuration(HASHMAP_PALETTE_FACTORY, bits); +- default -> new PalettedContainer.Configuration(PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY, Mth.ceillog2(idList.size())); ++ default -> ++ new PalettedContainer.Configuration(PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY, Mth.ceillog2(idList.size())); + }; + } + }; +@@ -440,7 +528,8 @@ public class PalettedContainer implements PaletteResize, PalettedContainer + return switch (bits) { + case 0 -> new PalettedContainer.Configuration(SINGLE_VALUE_PALETTE_FACTORY, bits); + case 1, 2, 3 -> new PalettedContainer.Configuration(LINEAR_PALETTE_FACTORY, bits); +- default -> new PalettedContainer.Configuration(PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY, Mth.ceillog2(idList.size())); ++ default -> ++ new PalettedContainer.Configuration(PalettedContainer.Strategy.GLOBAL_PALETTE_FACTORY, Mth.ceillog2(idList.size())); + }; + } + }; diff --git a/patches/server/0025-Gale-Skip-entity-move-if-movement-is-zero.patch b/patches/server/0025-Gale-Skip-entity-move-if-movement-is-zero.patch new file mode 100644 index 0000000..6777fdc --- /dev/null +++ b/patches/server/0025-Gale-Skip-entity-move-if-movement-is-zero.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 15:22:29 +0400 +Subject: [PATCH] Gale-Skip-entity-move-if-movement-is-zero + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index f6ad6cb80aefb6c64a0dbb986f4afcc70aa19c1d..79ce634f6ef7cf7446f58162c22781479e20af08 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -325,6 +325,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + public float yRotO; + public float xRotO; + private AABB bb; ++ private boolean boundingBoxChanged = false; // Gale - VMP - skip entity move if movement is zero + public boolean onGround; + public boolean horizontalCollision; + public boolean verticalCollision; +@@ -1128,6 +1129,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + // Paper end - detailed watchdog information + + public void move(MoverType movementType, Vec3 movement) { ++ // Gale start - VMP - skip entity move if movement is zero ++ if (!this.boundingBoxChanged && movement.equals(Vec3.ZERO)) { ++ return; ++ } ++ // Gale end - VMP - skip entity move if movement is zero + final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity + // Paper start - detailed watchdog information + ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main"); +@@ -4915,6 +4921,11 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + } + + public final void setBoundingBox(AABB boundingBox) { ++ // Gale start - VMP - skip entity move if movement is zero ++ if (!this.bb.equals(boundingBox)) { ++ this.boundingBoxChanged = true; ++ } ++ // Gale end - VMP - skip entity move if movement is zero + // CraftBukkit start - block invalid bounding boxes + double minX = boundingBox.minX, + minY = boundingBox.minY, diff --git a/patches/server/0026-Gale-Reduce-acquire-POI-for-stuck-entities.patch b/patches/server/0026-Gale-Reduce-acquire-POI-for-stuck-entities.patch new file mode 100644 index 0000000..c262fe7 --- /dev/null +++ b/patches/server/0026-Gale-Reduce-acquire-POI-for-stuck-entities.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 15:36:28 +0400 +Subject: [PATCH] Gale-Reduce-acquire-POI-for-stuck-entities + + +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index 8e901c59e1310d70552b3853d3f5e721aad71a79..f8618b1b7d3ecddd5ac9e1057167050a610f8e54 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -41,6 +41,8 @@ public class FoldenorConfig { + + public static boolean appleskinProtocol = false; + ++ public static int acquirePoiForStuckEntity = 60; ++ + public static void init(File configFile) { + init(configFile, true); + } +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +index e1b6fe9ecda25f86431baf414f1bfd3a26a8b2bd..fe26be2f2a6ec2791fb9b86cb0c829a9b687f8a5 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/AcquirePoi.java +@@ -13,6 +13,7 @@ import net.minecraft.core.BlockPos; + import net.minecraft.core.GlobalPos; + import net.minecraft.core.Holder; + import net.minecraft.network.protocol.game.DebugPackets; ++import net.minecraft.server.level.ServerLevel; + import net.minecraft.util.RandomSource; + import net.minecraft.world.entity.Mob; + import net.minecraft.world.entity.PathfinderMob; +@@ -26,6 +27,13 @@ import org.apache.commons.lang3.mutable.MutableLong; + public class AcquirePoi { + public static final int SCAN_RANGE = 48; + ++ // Gale start - Airplane - reduce acquire POI for stuck entities ++ public static void addAdditionalTimeToMutableLongIfEntityIsStuck(MutableLong mutableLong, ServerLevel world, PathfinderMob entity) { ++ long stuckEntityAdditionalWaitTime = net.edenor.foldenor.config.FoldenorConfig.acquirePoiForStuckEntity; ++ mutableLong.add(stuckEntityAdditionalWaitTime <= 0L ? 0L : entity.getNavigation().isStuck() ? stuckEntityAdditionalWaitTime : 0L); ++ } ++ // Gale end - Airplane - reduce acquire POI for stuck entities ++ + public static BehaviorControl create( + Predicate> poiPredicate, MemoryModuleType poiPosModule, boolean onlyRunIfChild, Optional entityStatus + ) { +@@ -51,13 +59,13 @@ public class AcquirePoi { + if (onlyRunIfChild && entity.isBaby()) { + return false; + } else if (mutableLong.getValue() == 0L) { +- mutableLong.setValue(world.getGameTime() + (long)world.random.nextInt(20)); ++ addAdditionalTimeToMutableLongIfEntityIsStuck(mutableLong, world, entity); // Gale - Airplane - reduce acquire POI for stuck entities + return false; + } else if (world.getGameTime() < mutableLong.getValue()) { + return false; + } else { + mutableLong.setValue(time + 20L + (long)world.getRandom().nextInt(20)); +- if (entity.getNavigation().isStuck()) mutableLong.add(200); // Paper - Perf: Wait an additional 10s to check again if they're stuck ++ addAdditionalTimeToMutableLongIfEntityIsStuck(mutableLong, world, entity); // Paper - Perf: Wait an additional 10s to check again if they're stuck // Gale - Airplane - reduce acquire POI for stuck entities + PoiManager poiManager = world.getPoiManager(); + long2ObjectMap.long2ObjectEntrySet().removeIf(entry -> !entry.getValue().isStillValid(time)); + Predicate predicate2 = pos -> { diff --git a/patches/server/0027-Gale-Optimize-sun-burn-tick.patch b/patches/server/0027-Gale-Optimize-sun-burn-tick.patch new file mode 100644 index 0000000..e0cbdaf --- /dev/null +++ b/patches/server/0027-Gale-Optimize-sun-burn-tick.patch @@ -0,0 +1,79 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 15:40:32 +0400 +Subject: [PATCH] Gale-Optimize-sun-burn-tick + + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 79ce634f6ef7cf7446f58162c22781479e20af08..93457c36f3b8dabb860472c8336bca57668a408b 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -316,7 +316,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + public double xo; + public double yo; + public double zo; +- private Vec3 position; ++ public Vec3 position; // Gale - JettPack - optimize sun burn tick - private -> public + private BlockPos blockPosition; + private ChunkPos chunkPosition; + private Vec3 deltaMovement; +@@ -2102,9 +2102,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + /** @deprecated */ + @Deprecated + public float getLightLevelDependentMagicValue() { +- return this.level().hasChunkAt(this.getBlockX(), this.getBlockZ()) ? this.level().getLightLevelDependentMagicValue(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())) : 0.0F; ++ return this.getLightLevelDependentMagicValue(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())); // Gale - JettPack - optimize sun burn tick - allow passing BlockPos to getLightLevelDependentMagicValue + } + ++ // Gale start - JettPack - optimize sun burn tick - allow passing BlockPos to getLightLevelDependentMagicValue ++ /** ++ * @deprecated ++ */ ++ @Deprecated ++ public float getLightLevelDependentMagicValue(BlockPos pos) { ++ return this.level().hasChunkAt(this.getBlockX(), this.getBlockZ()) ? this.level.getLightLevelDependentMagicValue(pos) : 0.0F; ++ } ++ // Gale end - JettPack - optimize sun burn tick - allow passing BlockPos to getLightLevelDependentMagicValue ++ + public void absMoveTo(double x, double y, double z, float yaw, float pitch) { + this.absMoveTo(x, y, z); + this.absRotateTo(yaw, pitch); +diff --git a/src/main/java/net/minecraft/world/entity/Mob.java b/src/main/java/net/minecraft/world/entity/Mob.java +index f74e8826700932d5e145f90e57d562c15863f27a..3fad8a4aaa398f0d3a3b5c1977f264eac08676fc 100644 +--- a/src/main/java/net/minecraft/world/entity/Mob.java ++++ b/src/main/java/net/minecraft/world/entity/Mob.java +@@ -1772,15 +1772,29 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab + + protected void playAttackSound() {} + ++ // Gale start - JettPack - optimize sun burn tick - cache eye blockpos ++ private BlockPos cached_eye_blockpos; ++ private int cached_position_hashcode; ++ // Gale end - JettPack - optimize sun burn tick - cache eye blockpos ++ + public boolean isSunBurnTick() { + if (this.level().isDay() && !this.level().isClientSide) { +- float f = this.getLightLevelDependentMagicValue(); +- BlockPos blockposition = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ()); ++ // Gale start - JettPack - optimize sun burn tick - optimizations and cache eye blockpos ++ int positionHashCode = this.position.hashCode(); ++ if (this.cached_position_hashcode != positionHashCode) { ++ this.cached_eye_blockpos = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ()); ++ this.cached_position_hashcode = positionHashCode; ++ } ++ ++ float f = this.getLightLevelDependentMagicValue(cached_eye_blockpos); // Pass BlockPos to getBrightness ++ ++ // Check brightness first ++ if (f <= 0.5F) return false; ++ if (this.random.nextFloat() * 30.0F >= (f - 0.4F) * 2.0F) return false; ++ // Gale end - JettPack - optimize sun burn tick - optimizations and cache eye blockpos + boolean flag = this.isInWaterRainOrBubble() || this.isInPowderSnow || this.wasInPowderSnow; + +- if (f > 0.5F && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && !flag && this.level().canSeeSky(blockposition)) { +- return true; +- } ++ return !flag && this.level().canSeeSky(this.cached_eye_blockpos); // Gale - JettPack - optimize sun burn tick - optimizations and cache eye blockpos + } + + return false; diff --git a/patches/server/0028-Purpur-Alternative-Keepalive-Handling.patch b/patches/server/0028-Purpur-Alternative-Keepalive-Handling.patch new file mode 100644 index 0000000..9a3d153 --- /dev/null +++ b/patches/server/0028-Purpur-Alternative-Keepalive-Handling.patch @@ -0,0 +1,78 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 15:43:15 +0400 +Subject: [PATCH] Purpur-Alternative-Keepalive-Handling + + +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index f8618b1b7d3ecddd5ac9e1057167050a610f8e54..b52f7bcc17fba6a3a4376a4bfea17430be9c4a8e 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -43,6 +43,8 @@ public class FoldenorConfig { + + public static int acquirePoiForStuckEntity = 60; + ++ public static boolean useAlternateKeepAlive = false; ++ + public static void init(File configFile) { + init(configFile, true); + } +@@ -103,6 +105,7 @@ public class FoldenorConfig { + protected static void readNetworkSettings(){ + sendNullEntityPackets = getBoolean("network.send-null-entity-packets", sendNullEntityPackets); + appleskinProtocol = getBoolean("network.appleskin-protocol", appleskinProtocol); ++ useAlternateKeepAlive = getBoolean("network.use-alternate-keepalive", useAlternateKeepAlive); + } + + private static void readOptimizationSettings() { +diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +index 6531c3173f0fd54196c11db3e6348e0afe617376..d5bb4d2a23687fe0dfc6c6920fc90580a366019f 100644 +--- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +@@ -74,6 +74,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + private long keepAliveChallenge; + private long closedListenerTime; + private boolean closed = false; ++ private it.unimi.dsi.fastutil.longs.LongList keepAlives = new it.unimi.dsi.fastutil.longs.LongArrayList(); // Purpur + private int latency; + private volatile boolean suspendFlushingOnServerThread = false; + public final java.util.Map packCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - adventure resource pack callbacks +@@ -136,6 +137,16 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + + @Override + public void handleKeepAlive(ServerboundKeepAlivePacket packet) { ++ // Purpur start ++ if (net.edenor.foldenor.config.FoldenorConfig.useAlternateKeepAlive) { ++ if (this.keepAlivePending && !keepAlives.isEmpty() && keepAlives.contains(packet.getId())) { ++ int ping = (int) (Util.getMillis() - packet.getId()); ++ this.latency = (this.latency * 3 + ping) / 4; ++ this.keepAlivePending = false; ++ keepAlives.clear(); // we got a valid response, lets roll with it and forget the rest ++ } ++ } else ++ // Purpur end + //PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // CraftBukkit // Paper - handle ServerboundKeepAlivePacket async + if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) { + int i = (int) (Util.getMillis() - this.keepAliveTime); +@@ -271,6 +282,21 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack + long currentTime = Util.getMillis(); + long elapsedTime = currentTime - this.keepAliveTime; + ++ // Purpur start ++ if (net.edenor.foldenor.config.FoldenorConfig.useAlternateKeepAlive) { ++ if (elapsedTime >= 1000L) { // 1 second ++ if (this.keepAlivePending && !this.processedDisconnect && keepAlives.size() * 1000L >= KEEPALIVE_LIMIT) { ++ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, org.bukkit.event.player.PlayerKickEvent.Cause.TIMEOUT); ++ } else if (this.checkIfClosed(currentTime)) { ++ this.keepAlivePending = true; ++ this.keepAliveTime = currentTime; // hijack this field for 1 second intervals ++ this.keepAlives.add(currentTime); // currentTime is ID ++ this.send(new ClientboundKeepAlivePacket(currentTime)); ++ } ++ } ++ } else ++ // Purpur end ++ + if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // Paper - use vanilla's 15000L between keep alive packets + if (this.keepAlivePending && !this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // Paper - check keepalive limit, don't fire if already disconnected + this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause diff --git a/patches/server/0029-Purpur-Remove-timings.patch b/patches/server/0029-Purpur-Remove-timings.patch new file mode 100644 index 0000000..4bef344 --- /dev/null +++ b/patches/server/0029-Purpur-Remove-timings.patch @@ -0,0 +1,1875 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 16:13:01 +0400 +Subject: [PATCH] Purpur-Remove-timings + + +diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java +deleted file mode 100644 +index 6b3cde6d4d1e63bec01f502f2027ee9fddac08aa..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/MinecraftTimings.java ++++ /dev/null +@@ -1,178 +0,0 @@ +-package co.aikar.timings; +- +-import com.google.common.collect.MapMaker; +-import io.papermc.paper.configuration.GlobalConfiguration; +-import net.minecraft.commands.functions.CommandFunction; +-import net.minecraft.network.protocol.Packet; +-import net.minecraft.world.level.block.Block; +-import net.minecraft.world.level.block.entity.BlockEntity; +-import org.bukkit.plugin.Plugin; +-import org.bukkit.scheduler.BukkitTask; +- +-import org.bukkit.craftbukkit.scheduler.CraftTask; +- +-import java.util.Map; +- +-// TODO: Re-implement missing timers +-@Deprecated(forRemoval = true) +-public final class MinecraftTimings { +- +- public static final Timing serverOversleep = Timings.ofSafe("Server Oversleep"); +- public static final Timing playerListTimer = Timings.ofSafe("Player List"); +- public static final Timing commandFunctionsTimer = Timings.ofSafe("Command Functions"); +- public static final Timing connectionTimer = Timings.ofSafe("Connection Handler"); +- public static final Timing tickablesTimer = Timings.ofSafe("Tickables"); +- public static final Timing minecraftSchedulerTimer = Timings.ofSafe("Minecraft Scheduler"); +- public static final Timing bukkitSchedulerTimer = Timings.ofSafe("Bukkit Scheduler"); +- public static final Timing bukkitSchedulerPendingTimer = Timings.ofSafe("Bukkit Scheduler - Pending"); +- public static final Timing bukkitSchedulerFinishTimer = Timings.ofSafe("Bukkit Scheduler - Finishing"); +- public static final Timing chunkIOTickTimer = Timings.ofSafe("ChunkIOTick"); +- public static final Timing timeUpdateTimer = Timings.ofSafe("Time Update"); +- public static final Timing serverCommandTimer = Timings.ofSafe("Server Command"); +- public static final Timing savePlayers = Timings.ofSafe("Save Players"); +- +- public static final Timing tickEntityTimer = Timings.ofSafe("## tickEntity"); +- public static final Timing tickTileEntityTimer = Timings.ofSafe("## tickTileEntity"); +- public static final Timing packetProcessTimer = Timings.ofSafe("## Packet Processing"); +- public static final Timing scheduledBlocksTimer = Timings.ofSafe("## Scheduled Blocks"); +- public static final Timing structureGenerationTimer = Timings.ofSafe("Structure Generation"); +- +- public static final Timing processQueueTimer = Timings.ofSafe("processQueue"); +- public static final Timing processTasksTimer = Timings.ofSafe("processTasks"); +- +- public static final Timing playerCommandTimer = Timings.ofSafe("playerCommand"); +- +- public static final Timing entityActivationCheckTimer = Timings.ofSafe("entityActivationCheck"); +- +- public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update"); +- public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate"); +- public static final Timing scoreboardScoreSearch = Timings.ofSafe("Scoreboard score search"); // Paper - add timings for scoreboard search +- +- private static final Map, String> taskNameCache = new MapMaker().weakKeys().makeMap(); +- +- private MinecraftTimings() {} +- +- public static Timing getInternalTaskName(String taskName) { +- return Timings.ofSafe(taskName); +- } +- +- /** +- * Gets a timer associated with a plugins tasks. +- * @param bukkitTask +- * @param period +- * @return +- */ +- public static Timing getPluginTaskTimings(BukkitTask bukkitTask, long period) { +- if (!bukkitTask.isSync()) { +- return NullTimingHandler.NULL; +- } +- Plugin plugin; +- +- CraftTask craftTask = (CraftTask) bukkitTask; +- +- final Class taskClass = craftTask.getTaskClass(); +- if (bukkitTask.getOwner() != null) { +- plugin = bukkitTask.getOwner(); +- } else { +- plugin = TimingsManager.getPluginByClassloader(taskClass); +- } +- +- final String taskname = taskNameCache.computeIfAbsent(taskClass, clazz -> { +- try { +- String clsName = !clazz.isMemberClass() +- ? clazz.getName() +- : clazz.getCanonicalName(); +- if (clsName != null && clsName.contains("$Lambda$")) { +- clsName = clsName.replaceAll("(Lambda\\$.*?)/.*", "$1"); +- } +- return clsName != null ? clsName : "UnknownTask"; +- } catch (Throwable ex) { +- new Exception("Error occurred detecting class name", ex).printStackTrace(); +- return "MangledClassFile"; +- } +- }); +- +- StringBuilder name = new StringBuilder(64); +- name.append("Task: ").append(taskname); +- if (period > 0) { +- name.append(" (interval:").append(period).append(")"); +- } else { +- name.append(" (Single)"); +- } +- +- if (plugin == null) { +- return Timings.ofSafe(null, name.toString()); +- } +- +- return Timings.ofSafe(plugin, name.toString()); +- } +- +- /** +- * Get a named timer for the specified entity type to track type specific timings. +- * @param entityType +- * @return +- */ +- public static Timing getEntityTimings(String entityType, String type) { +- return Timings.ofSafe("Minecraft", "## tickEntity - " + entityType + " - " + type, tickEntityTimer); +- } +- +- public static Timing getBehaviorTimings(String type) { +- return Timings.ofSafe("## Behavior - " + type); +- } +- +- public static Timing getSensorTimings(String type, int rate) { +- return Timings.ofSafe("## Sensor - " + type + " (Default rate: " + rate + ")"); +- } +- +- /** +- * Get a named timer for the specified tile entity type to track type specific timings. +- * @param entity +- * @return +- */ +- public static Timing getTileEntityTimings(BlockEntity entity) { +- String entityType = entity.getClass().getName(); +- return Timings.ofSafe("Minecraft", "## tickTileEntity - " + entityType, tickTileEntityTimer); +- } +- public static Timing getCancelTasksTimer() { +- return Timings.ofSafe("Cancel Tasks"); +- } +- public static Timing getCancelTasksTimer(Plugin plugin) { +- return Timings.ofSafe(plugin, "Cancel Tasks"); +- } +- +- public static void stopServer() { +- TimingsManager.stopServer(); +- } +- +- public static Timing getBlockTiming(Block block) { +- return Timings.ofSafe("## Scheduled Block: " + block.toString(), scheduledBlocksTimer); +- } +-/* +- public static Timing getStructureTiming(StructureGenerator structureGenerator) { +- return Timings.ofSafe("Structure Generator - " + structureGenerator.getName(), structureGenerationTimer); +- }*/ +- +- public static Timing getPacketTiming(Packet packet) { +- return Timings.ofSafe("## Packet - " + packet.getClass().getName(), packetProcessTimer); +- } +- +- public static Timing getCommandFunctionTiming(CommandFunction function) { +- return Timings.ofSafe("Command Function - " + function.id()); +- } +- +- public static void processConfig(GlobalConfiguration.Timings config) { +- TimingsManager.url = config.url; +- if (!TimingsManager.url.endsWith("/")) { +- TimingsManager.url += "/"; +- } +- TimingsManager.privacy = config.serverNamePrivacy; +- if (!config.hiddenConfigEntries.contains("proxies.velocity.secret")) { +- config.hiddenConfigEntries.add("proxies.velocity.secret"); +- } +- TimingsManager.hiddenConfigs.addAll(config.hiddenConfigEntries); +- co.aikar.timings.Timings.setVerboseTimingsEnabled(config.verbose); +- co.aikar.timings.Timings.setTimingsEnabled(config.enabled); +- co.aikar.timings.Timings.setHistoryInterval(config.historyInterval * 20); +- co.aikar.timings.Timings.setHistoryLength(config.historyLength * 20); +- } +-} +diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java +deleted file mode 100644 +index 7620c72a4c243cbeea245203ce03a97cbfa7d922..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/TimingsExport.java ++++ /dev/null +@@ -1,388 +0,0 @@ +-/* +- * This file is licensed under the MIT License (MIT). +- * +- * Copyright (c) 2014 Daniel Ennis +- * +- * Permission is hereby granted, free of charge, to any person obtaining a copy +- * of this software and associated documentation files (the "Software"), to deal +- * in the Software without restriction, including without limitation the rights +- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +- * copies of the Software, and to permit persons to whom the Software is +- * furnished to do so, subject to the following conditions: +- * +- * The above copyright notice and this permission notice shall be included in +- * all copies or substantial portions of the Software. +- * +- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +- * THE SOFTWARE. +- */ +-package co.aikar.timings; +- +-import com.google.common.collect.Sets; +-import io.papermc.paper.adventure.PaperAdventure; +-import net.kyori.adventure.text.event.ClickEvent; +-import net.kyori.adventure.text.format.NamedTextColor; +-import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +-import net.minecraft.server.MinecraftServer; +-import org.apache.commons.lang.StringUtils; +-import org.bukkit.Bukkit; +-import org.bukkit.Material; +-import org.bukkit.configuration.ConfigurationSection; +-import org.bukkit.configuration.MemorySection; +-import org.bukkit.entity.EntityType; +-import org.json.simple.JSONObject; +-import org.json.simple.JSONValue; +-import oshi.SystemInfo; +-import oshi.hardware.HardwareAbstractionLayer; +- +-import java.io.ByteArrayOutputStream; +-import java.io.IOException; +-import java.io.InputStream; +-import java.io.OutputStream; +-import java.lang.management.ManagementFactory; +-import java.lang.management.OperatingSystemMXBean; +-import java.lang.management.RuntimeMXBean; +-import java.net.HttpURLConnection; +-import java.net.InetAddress; +-import java.net.URL; +-import java.util.List; +-import java.util.Map; +-import java.util.Set; +-import java.util.logging.Level; +-import java.util.zip.GZIPOutputStream; +- +-import static co.aikar.timings.TimingsManager.HISTORY; +-import static co.aikar.util.JSONUtil.appendObjectData; +-import static co.aikar.util.JSONUtil.createObject; +-import static co.aikar.util.JSONUtil.pair; +-import static co.aikar.util.JSONUtil.toArray; +-import static co.aikar.util.JSONUtil.toArrayMapper; +-import static co.aikar.util.JSONUtil.toObjectMapper; +-import static net.kyori.adventure.text.Component.text; +- +-@SuppressWarnings({"rawtypes", "SuppressionAnnotation"}) +-@Deprecated(forRemoval = true) +-public class TimingsExport extends Thread { +- +- private final TimingsReportListener listeners; +- private final Map out; +- private final TimingHistory[] history; +- private static long lastReport = 0; +- +- private TimingsExport(TimingsReportListener listeners, Map out, TimingHistory[] history) { +- super("Timings paste thread"); +- this.listeners = listeners; +- this.out = out; +- this.history = history; +- } +- +- /** +- * Checks if any pending reports are being requested, and builds one if needed. +- */ +- public static void reportTimings() { +- if (Timings.requestingReport.isEmpty()) { +- return; +- } +- TimingsReportListener listeners = new TimingsReportListener(Timings.requestingReport); +- listeners.addConsoleIfNeeded(); +- +- Timings.requestingReport.clear(); +- long now = System.currentTimeMillis(); +- final long lastReportDiff = now - lastReport; +- if (lastReportDiff < 60000) { +- listeners.sendMessage(text("Please wait at least 1 minute in between Timings reports. (" + (int)((60000 - lastReportDiff) / 1000) + " seconds)", NamedTextColor.RED)); +- listeners.done(); +- return; +- } +- final long lastStartDiff = now - TimingsManager.timingStart; +- if (lastStartDiff < 180000) { +- listeners.sendMessage(text("Please wait at least 3 minutes before generating a Timings report. Unlike Timings v1, v2 benefits from longer timings and is not as useful with short timings. (" + (int)((180000 - lastStartDiff) / 1000) + " seconds)", NamedTextColor.RED)); +- listeners.done(); +- return; +- } +- listeners.sendMessage(text("Preparing Timings Report...", NamedTextColor.GREEN)); +- lastReport = now; +- Map parent = createObject( +- // Get some basic system details about the server +- pair("version", Bukkit.getVersion()), +- pair("maxplayers", Bukkit.getMaxPlayers()), +- pair("start", TimingsManager.timingStart / 1000), +- pair("end", System.currentTimeMillis() / 1000), +- pair("online-mode", Bukkit.getServer().getOnlineMode()), +- pair("sampletime", (System.currentTimeMillis() - TimingsManager.timingStart) / 1000), +- pair("datapacks", toArrayMapper(MinecraftServer.getServer().getPackRepository().getSelectedPacks(), pack -> { +- return PlainTextComponentSerializer.plainText().serialize(PaperAdventure.asAdventure(pack.getChatLink(true))); +- })) +- ); +- if (!TimingsManager.privacy) { +- appendObjectData(parent, +- pair("server", Bukkit.getUnsafe().getTimingsServerName()), +- pair("motd", Bukkit.getServer().getMotd()), +- pair("icon", Bukkit.getServer().getServerIcon().getData()) +- ); +- } +- +- final Runtime runtime = Runtime.getRuntime(); +- RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); +- +- OperatingSystemMXBean osInfo = ManagementFactory.getOperatingSystemMXBean(); +- +- HardwareAbstractionLayer hardwareInfo = new SystemInfo().getHardware(); +- +- parent.put("system", createObject( +- pair("timingcost", getCost()), +- pair("loadavg", osInfo.getSystemLoadAverage()), +- pair("name", System.getProperty("os.name")), +- pair("version", System.getProperty("os.version")), +- pair("jvmversion", System.getProperty("java.version")), +- pair("jvmvendor", System.getProperty("java.vendor")), +- pair("jvmvendorversion", System.getProperty("java.vendor.version")), +- pair("arch", System.getProperty("os.arch")), +- pair("maxmem", runtime.maxMemory()), +- pair("memory", createObject( +- pair("heap", ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().toString()), +- pair("nonheap", ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().toString()), +- pair("finalizing", ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount()) +- )), +- pair("cpu", runtime.availableProcessors()), +- pair("cpuname", hardwareInfo.getProcessor().getProcessorIdentifier().getName().trim()), +- pair("runtime", runtimeBean.getUptime()), +- pair("flags", StringUtils.join(runtimeBean.getInputArguments(), " ")), +- pair("gc", toObjectMapper(ManagementFactory.getGarbageCollectorMXBeans(), input -> pair(input.getName(), toArray(input.getCollectionCount(), input.getCollectionTime())))) +- ) +- ); +- +- parent.put("worlds", toObjectMapper(MinecraftServer.getServer().getAllLevels(), world -> { +- if (world.getWorld().getName().equals("worldeditregentempworld")) return null; +- return pair(world.getWorld().getName(), createObject( +- pair("gamerules", toObjectMapper(world.getWorld().getGameRules(), rule -> { +- return pair(rule, world.getWorld().getGameRuleValue(rule)); +- })), +- pair("ticking-distance", world.getWorld().getSimulationDistance()), +- pair("no-ticking-distance", world.getWorld().getViewDistance()), +- pair("sending-distance", world.getWorld().getSendViewDistance()) +- )); +- })); +- +- Set tileEntityTypeSet = Sets.newHashSet(); +- Set entityTypeSet = Sets.newHashSet(); +- +- int size = HISTORY.size(); +- TimingHistory[] history = new TimingHistory[size + 1]; +- int i = 0; +- for (TimingHistory timingHistory : HISTORY) { +- tileEntityTypeSet.addAll(timingHistory.tileEntityTypeSet); +- entityTypeSet.addAll(timingHistory.entityTypeSet); +- history[i++] = timingHistory; +- } +- +- history[i] = new TimingHistory(); // Current snapshot +- tileEntityTypeSet.addAll(history[i].tileEntityTypeSet); +- entityTypeSet.addAll(history[i].entityTypeSet); +- +- +- Map handlers = createObject(); +- Map groupData; +- synchronized (TimingIdentifier.GROUP_MAP) { +- for (TimingIdentifier.TimingGroup group : TimingIdentifier.GROUP_MAP.values()) { +- synchronized (group.handlers) { +- for (TimingHandler id : group.handlers) { +- +- if (!id.isTimed() && !id.isSpecial()) { +- continue; +- } +- +- String name = id.identifier.name; +- if (name.startsWith("##")) { +- name = name.substring(3); +- } +- handlers.put(id.id, toArray( +- group.id, +- name +- )); +- } +- } +- } +- +- groupData = toObjectMapper( +- TimingIdentifier.GROUP_MAP.values(), group -> pair(group.id, group.name)); +- } +- +- parent.put("idmap", createObject( +- pair("groups", groupData), +- pair("handlers", handlers), +- pair("worlds", toObjectMapper(TimingHistory.worldMap.entrySet(), input -> pair(input.getValue(), input.getKey()))), +- pair("tileentity", +- toObjectMapper(tileEntityTypeSet, input -> pair(input.ordinal(), input.name()))), +- pair("entity", +- toObjectMapper(entityTypeSet, input -> pair(input.ordinal(), input.name()))) +- )); +- +- // Information about loaded plugins +- +- parent.put("plugins", toObjectMapper(Bukkit.getPluginManager().getPlugins(), +- plugin -> pair(plugin.getName(), createObject( +- pair("version", plugin.getDescription().getVersion()), +- pair("description", String.valueOf(plugin.getDescription().getDescription()).trim()), +- pair("website", plugin.getDescription().getWebsite()), +- pair("authors", StringUtils.join(plugin.getDescription().getAuthors(), ", ")) +- )))); +- +- +- +- // Information on the users Config +- +- parent.put("config", createObject( +- pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)), +- pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), +- pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)) +- )); +- +- new TimingsExport(listeners, parent, history).start(); +- } +- +- static long getCost() { +- // Benchmark the users System.nanotime() for cost basis +- int passes = 100; +- TimingHandler SAMPLER1 = Timings.ofSafe("Timings Sampler 1"); +- TimingHandler SAMPLER2 = Timings.ofSafe("Timings Sampler 2"); +- TimingHandler SAMPLER3 = Timings.ofSafe("Timings Sampler 3"); +- TimingHandler SAMPLER4 = Timings.ofSafe("Timings Sampler 4"); +- TimingHandler SAMPLER5 = Timings.ofSafe("Timings Sampler 5"); +- TimingHandler SAMPLER6 = Timings.ofSafe("Timings Sampler 6"); +- +- long start = System.nanoTime(); +- for (int i = 0; i < passes; i++) { +- SAMPLER1.startTiming(); +- SAMPLER2.startTiming(); +- SAMPLER3.startTiming(); +- SAMPLER3.stopTiming(); +- SAMPLER4.startTiming(); +- SAMPLER5.startTiming(); +- SAMPLER6.startTiming(); +- SAMPLER6.stopTiming(); +- SAMPLER5.stopTiming(); +- SAMPLER4.stopTiming(); +- SAMPLER2.stopTiming(); +- SAMPLER1.stopTiming(); +- } +- long timingsCost = (System.nanoTime() - start) / passes / 6; +- SAMPLER1.reset(true); +- SAMPLER2.reset(true); +- SAMPLER3.reset(true); +- SAMPLER4.reset(true); +- SAMPLER5.reset(true); +- SAMPLER6.reset(true); +- return timingsCost; +- } +- +- private static JSONObject mapAsJSON(ConfigurationSection config, String parentKey) { +- +- JSONObject object = new JSONObject(); +- for (String key : config.getKeys(false)) { +- String fullKey = (parentKey != null ? parentKey + "." + key : key); +- if (fullKey.equals("database") || fullKey.equals("settings.bungeecord-addresses") || TimingsManager.hiddenConfigs.contains(fullKey) || key.startsWith("seed-") || key.equals("worldeditregentempworld") || key.equals("feature-seeds")) { +- continue; +- } +- final Object val = config.get(key); +- +- object.put(key, valAsJSON(val, fullKey)); +- } +- return object; +- } +- +- private static Object valAsJSON(Object val, final String parentKey) { +- if (!(val instanceof MemorySection)) { +- if (val instanceof List) { +- Iterable v = (Iterable) val; +- return toArrayMapper(v, input -> valAsJSON(input, parentKey)); +- } else { +- return String.valueOf(val); +- } +- } else { +- return mapAsJSON((ConfigurationSection) val, parentKey); +- } +- } +- +- @Override +- public void run() { +- out.put("data", toArrayMapper(history, TimingHistory::export)); +- +- +- String response = null; +- String timingsURL = null; +- try { +- HttpURLConnection con = (HttpURLConnection) new URL(TimingsManager.url + "post").openConnection(); +- con.setDoOutput(true); +- String hostName = "BrokenHost"; +- try { +- hostName = InetAddress.getLocalHost().getHostName(); +- } catch (Exception ignored) {} +- con.setRequestProperty("User-Agent", "Paper/" + Bukkit.getUnsafe().getTimingsServerName() + "/" + hostName); +- con.setRequestMethod("POST"); +- con.setInstanceFollowRedirects(false); +- +- OutputStream request = new GZIPOutputStream(con.getOutputStream()) {{ +- this.def.setLevel(7); +- }}; +- +- request.write(JSONValue.toJSONString(out).getBytes("UTF-8")); +- request.close(); +- +- response = getResponse(con); +- +- if (con.getResponseCode() != 302) { +- listeners.sendMessage(text( "Upload Error: " + con.getResponseCode() + ": " + con.getResponseMessage(), NamedTextColor.RED)); +- listeners.sendMessage(text("Check your logs for more information", NamedTextColor.RED)); +- if (response != null) { +- Bukkit.getLogger().log(Level.SEVERE, response); +- } +- return; +- } +- +- timingsURL = con.getHeaderField("Location"); +- listeners.sendMessage(text("View Timings Report: ", NamedTextColor.GREEN).append(text(timingsURL).clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, timingsURL)))); +- +- if (response != null && !response.isEmpty()) { +- Bukkit.getLogger().log(Level.INFO, "Timing Response: " + response); +- } +- } catch (IOException ex) { +- listeners.sendMessage(text("Error uploading timings, check your logs for more information", NamedTextColor.RED)); +- if (response != null) { +- Bukkit.getLogger().log(Level.SEVERE, response); +- } +- Bukkit.getLogger().log(Level.SEVERE, "Could not paste timings", ex); +- } finally { +- this.listeners.done(timingsURL); +- } +- } +- +- private String getResponse(HttpURLConnection con) throws IOException { +- InputStream is = null; +- try { +- is = con.getInputStream(); +- ByteArrayOutputStream bos = new ByteArrayOutputStream(); +- +- byte[] b = new byte[1024]; +- int bytesRead; +- while ((bytesRead = is.read(b)) != -1) { +- bos.write(b, 0, bytesRead); +- } +- return bos.toString(); +- +- } catch (IOException ex) { +- listeners.sendMessage(text("Error uploading timings, check your logs for more information", NamedTextColor.RED)); +- Bukkit.getLogger().log(Level.WARNING, con.getResponseMessage(), ex); +- return null; +- } finally { +- if (is != null) { +- is.close(); +- } +- } +- } +-} +diff --git a/src/main/java/co/aikar/timings/WorldTimingsHandler.java b/src/main/java/co/aikar/timings/WorldTimingsHandler.java +deleted file mode 100644 +index 2f0d9b953802dee821cfde82d22b0567cce8ee91..0000000000000000000000000000000000000000 +--- a/src/main/java/co/aikar/timings/WorldTimingsHandler.java ++++ /dev/null +@@ -1,120 +0,0 @@ +-package co.aikar.timings; +- +-import net.minecraft.server.level.ServerLevel; +-import net.minecraft.world.level.Level; +-import net.minecraft.world.level.storage.PrimaryLevelData; +- +-/** +- * Set of timers per world, to track world specific timings. +- */ +-// TODO: Re-implement missing timers +-@Deprecated(forRemoval = true) +-public class WorldTimingsHandler { +- public final Timing mobSpawn; +- public final Timing doChunkUnload; +- public final Timing doPortalForcer; +- public final Timing scheduledBlocks; +- public final Timing scheduledBlocksCleanup; +- public final Timing scheduledBlocksTicking; +- public final Timing chunkTicks; +- public final Timing lightChunk; +- public final Timing chunkTicksBlocks; +- public final Timing doVillages; +- public final Timing doChunkMap; +- public final Timing doChunkMapUpdate; +- public final Timing doChunkMapToUpdate; +- public final Timing doChunkMapSortMissing; +- public final Timing doChunkMapSortSendToPlayers; +- public final Timing doChunkMapPlayersNeedingChunks; +- public final Timing doChunkMapPendingSendToPlayers; +- public final Timing doChunkMapUnloadChunks; +- public final Timing doChunkGC; +- public final Timing doSounds; +- public final Timing entityRemoval; +- public final Timing entityTick; +- public final Timing tileEntityTick; +- public final Timing tileEntityPending; +- public final Timing tracker1; +- public final Timing tracker2; +- public final Timing doTick; +- public final Timing tickEntities; +- public final Timing chunks; +- public final Timing newEntities; +- public final Timing raids; +- public final Timing chunkProviderTick; +- public final Timing broadcastChunkUpdates; +- public final Timing countNaturalMobs; +- +- public final Timing chunkLoad; +- public final Timing chunkLoadPopulate; +- public final Timing syncChunkLoad; +- public final Timing chunkLoadLevelTimer; +- public final Timing chunkIO; +- public final Timing chunkPostLoad; +- public final Timing worldSave; +- public final Timing worldSaveChunks; +- public final Timing worldSaveLevel; +- public final Timing chunkSaveData; +- +- +- public final Timing miscMobSpawning; +- +- public WorldTimingsHandler(Level server) { +- String name = ((PrimaryLevelData) server.getLevelData()).getLevelName() + " - "; +- +- mobSpawn = Timings.ofSafe(name + "mobSpawn"); +- doChunkUnload = Timings.ofSafe(name + "doChunkUnload"); +- scheduledBlocks = Timings.ofSafe(name + "Scheduled Blocks"); +- scheduledBlocksCleanup = Timings.ofSafe(name + "Scheduled Blocks - Cleanup"); +- scheduledBlocksTicking = Timings.ofSafe(name + "Scheduled Blocks - Ticking"); +- chunkTicks = Timings.ofSafe(name + "Chunk Ticks"); +- lightChunk = Timings.ofSafe(name + "Light Chunk"); +- chunkTicksBlocks = Timings.ofSafe(name + "Chunk Ticks - Blocks"); +- doVillages = Timings.ofSafe(name + "doVillages"); +- doChunkMap = Timings.ofSafe(name + "doChunkMap"); +- doChunkMapUpdate = Timings.ofSafe(name + "doChunkMap - Update"); +- doChunkMapToUpdate = Timings.ofSafe(name + "doChunkMap - To Update"); +- doChunkMapSortMissing = Timings.ofSafe(name + "doChunkMap - Sort Missing"); +- doChunkMapSortSendToPlayers = Timings.ofSafe(name + "doChunkMap - Sort Send To Players"); +- doChunkMapPlayersNeedingChunks = Timings.ofSafe(name + "doChunkMap - Players Needing Chunks"); +- doChunkMapPendingSendToPlayers = Timings.ofSafe(name + "doChunkMap - Pending Send To Players"); +- doChunkMapUnloadChunks = Timings.ofSafe(name + "doChunkMap - Unload Chunks"); +- doSounds = Timings.ofSafe(name + "doSounds"); +- doChunkGC = Timings.ofSafe(name + "doChunkGC"); +- doPortalForcer = Timings.ofSafe(name + "doPortalForcer"); +- entityTick = Timings.ofSafe(name + "entityTick"); +- entityRemoval = Timings.ofSafe(name + "entityRemoval"); +- tileEntityTick = Timings.ofSafe(name + "tileEntityTick"); +- tileEntityPending = Timings.ofSafe(name + "tileEntityPending"); +- +- chunkLoad = Timings.ofSafe(name + "Chunk Load"); +- chunkLoadPopulate = Timings.ofSafe(name + "Chunk Load - Populate"); +- syncChunkLoad = Timings.ofSafe(name + "Sync Chunk Load"); +- chunkLoadLevelTimer = Timings.ofSafe(name + "Chunk Load - Load Level"); +- chunkIO = Timings.ofSafe(name + "Chunk Load - DiskIO"); +- chunkPostLoad = Timings.ofSafe(name + "Chunk Load - Post Load"); +- worldSave = Timings.ofSafe(name + "World Save"); +- worldSaveLevel = Timings.ofSafe(name + "World Save - Level"); +- worldSaveChunks = Timings.ofSafe(name + "World Save - Chunks"); +- chunkSaveData = Timings.ofSafe(name + "Chunk Save - Data"); +- +- tracker1 = Timings.ofSafe(name + "tracker stage 1"); +- tracker2 = Timings.ofSafe(name + "tracker stage 2"); +- doTick = Timings.ofSafe(name + "doTick"); +- tickEntities = Timings.ofSafe(name + "tickEntities"); +- +- chunks = Timings.ofSafe(name + "Chunks"); +- newEntities = Timings.ofSafe(name + "New entity registration"); +- raids = Timings.ofSafe(name + "Raids"); +- chunkProviderTick = Timings.ofSafe(name + "Chunk provider tick"); +- broadcastChunkUpdates = Timings.ofSafe(name + "Broadcast chunk updates"); +- countNaturalMobs = Timings.ofSafe(name + "Count natural mobs"); +- +- +- miscMobSpawning = Timings.ofSafe(name + "Mob spawning - Misc"); +- } +- +- public static Timing getTickList(ServerLevel worldserver, String timingsType) { +- return Timings.ofSafe(((PrimaryLevelData) worldserver.getLevelData()).getLevelName() + " - Scheduled " + timingsType); +- } +-} +diff --git a/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java +index 0c3c82b28e581286b798ee58ca4193efc2faff4a..f10e48f653a3250056ddfa6b6d9313446af0cafe 100644 +--- a/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java ++++ b/src/main/java/io/papermc/paper/command/brigadier/bukkit/BukkitCommandNode.java +@@ -1,6 +1,6 @@ + package io.papermc.paper.command.brigadier.bukkit; + +-import co.aikar.timings.Timing; ++ + import com.mojang.brigadier.arguments.StringArgumentType; + import com.mojang.brigadier.builder.RequiredArgumentBuilder; + import com.mojang.brigadier.context.CommandContext; +@@ -78,18 +78,13 @@ public class BukkitCommandNode extends LiteralCommandNode { + public int run(CommandContext context) throws CommandSyntaxException { + CommandSender sender = context.getSource().getSender(); + +- // Plugins do weird things to workaround normal registration +- if (this.command.timings == null) { +- this.command.timings = co.aikar.timings.TimingsManager.getCommandTiming(null, this.command); +- } +- + String content = context.getRange().get(context.getInput()); + String[] args = org.apache.commons.lang3.StringUtils.split(content, ' '); // fix adjacent spaces (from console/plugins) causing empty array elements + +- try (Timing ignored = this.command.timings.startTiming()) { ++ //try (Timing ignored = this.command.timings.startTiming()) { // Purpur + // Note: we don't return the result of target.execute as thats success / failure, we return handled (true) or not handled (false) + this.command.execute(sender, this.literal, Arrays.copyOfRange(args, 1, args.length)); +- } ++ //} // Purpur + + // return true as command was handled + return 1; +diff --git a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +index eae1bb367483eb6343a8644f6bac56215298bb43..923055037a3c5ff6a8c3a654f77c624dd3a51f96 100644 +--- a/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java ++++ b/src/main/java/io/papermc/paper/configuration/GlobalConfiguration.java +@@ -1,6 +1,5 @@ + package io.papermc.paper.configuration; + +-import co.aikar.timings.MinecraftTimings; + import com.mojang.logging.LogUtils; + import io.papermc.paper.configuration.constraint.Constraints; + import io.papermc.paper.configuration.type.number.DoubleOr; +@@ -99,29 +98,6 @@ public class GlobalConfiguration extends ConfigurationPart { + public boolean enableImmediately = false; + } + +- @Deprecated(forRemoval = true) +- public Timings timings; +- +- @Deprecated(forRemoval = true) +- public class Timings extends ConfigurationPart { +- public boolean enabled = false; +- public boolean verbose = true; +- public String url = "https://timings.aikar.co/"; +- public boolean serverNamePrivacy = false; +- public List hiddenConfigEntries = List.of( +- "database", +- "proxies.velocity.secret" +- ); +- public int historyInterval = 300; +- public int historyLength = 3600; +- public String serverName = "Unknown Server"; +- +- @PostProcess +- private void postProcess() { +- MinecraftTimings.processConfig(this); +- } +- } +- + public Proxies proxies; + + public class Proxies extends ConfigurationPart { +diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java +index 7ce9ebba8ce304d1f3f21d4f15ee5f3560d7700b..10895545ea073d8c3a0c09166265958c39bcf3e6 100644 +--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java ++++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java +@@ -1,6 +1,5 @@ + package io.papermc.paper.plugin.manager; + +-import co.aikar.timings.TimedEventExecutor; + import com.destroystokyo.paper.event.server.ServerExceptionEvent; + import com.destroystokyo.paper.exception.ServerEventException; + import com.google.common.collect.Sets; +@@ -95,7 +94,6 @@ class PaperEventManager { + throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled"); + } + +- executor = new TimedEventExecutor(executor, plugin, null, event); + this.getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled)); + } + +@@ -182,8 +180,7 @@ class PaperEventManager { + } + } + +- EventExecutor executor = new TimedEventExecutor(EventExecutor.create(method, eventClass), plugin, method, eventClass); +- eventSet.add(new RegisteredListener(listener, executor, eh.priority(), plugin, eh.ignoreCancelled())); ++ eventSet.add(new RegisteredListener(listener, EventExecutor.create(method, eventClass), eh.priority(), plugin, eh.ignoreCancelled())); + } + return ret; + } +diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java +index 097500a59336db1bbfffcd1aa4cff7a8586e46ec..35b00c139864dd7925d46a2d6a317d7e3aae9638 100644 +--- a/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java ++++ b/src/main/java/io/papermc/paper/plugin/manager/PaperPluginManagerImpl.java +@@ -232,7 +232,7 @@ public class PaperPluginManagerImpl implements PluginManager, DependencyContext + + @Override + public boolean useTimings() { +- return co.aikar.timings.Timings.isTimingsEnabled(); ++ return false; + } + + @Override +diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java +index 97817400b70b2579f3a8750f7f33197a5db7ba94..a63b738f567099347cefc7c79911894fbd1fc1bd 100644 +--- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java ++++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java +@@ -51,8 +51,7 @@ public class PacketUtils { + try { // Paper - detailed watchdog information + if (listener instanceof ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // CraftBukkit - Don't handle sync packets for kicked players + if (listener.shouldHandleMessage(packet)) { +- co.aikar.timings.Timing timing = co.aikar.timings.MinecraftTimings.getPacketTiming(packet); // Paper - timings +- try (co.aikar.timings.Timing ignored = timing.startTiming()) { // Paper - timings ++ try { // Paper - timings // Purpur + final ca.spottedleaf.leafprofiler.RegionizedProfiler.Handle profiler = io.papermc.paper.threadedregions.TickRegionScheduler.getProfiler(); // Folia - profiler + final int packetTimerId = profiler.getOrCreateTimerAndStart(() -> "Packet Handler: ".concat(io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(packet.getClass().getName()))); try { // Folia - profiler + packet.handle(listener); +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index fca787f92827019e776a6ccae8f90fe2b12c5177..87a722ca353c5ec20320e71c745fad7b3f33f08f 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -3,7 +3,6 @@ package net.minecraft.server; + import com.google.common.base.Preconditions; + import com.google.common.base.Splitter; + import com.google.common.collect.ImmutableList; +-import co.aikar.timings.Timings; + import com.destroystokyo.paper.event.server.PaperServerListPingEvent; + import com.google.common.base.Stopwatch; + import com.google.common.collect.Lists; +@@ -196,8 +195,6 @@ import org.bukkit.craftbukkit.CraftRegistry; + import org.bukkit.event.server.ServerLoadEvent; + // CraftBukkit end + +-import co.aikar.timings.MinecraftTimings; // Paper +- + public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements ServerInfo, ChunkIOErrorReporter, CommandSource, AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.server.ChunkSystemMinecraftServer { // Paper - rewrite chunk system + + private static MinecraftServer SERVER; // Paper +@@ -1092,7 +1089,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { + return !this.canOversleep(); + }); +- isOversleep = false;MinecraftTimings.serverOversleep.stopTiming(); ++ //isOversleep = false;MinecraftTimings.serverOversleep.stopTiming(); // Purpur + } // Folia - region threading + // Paper end + // Folia - region threading - move up +@@ -1751,9 +1747,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop { // Folia - region threading + entityplayer.connection.suspendFlushing(); + }); +- MinecraftTimings.bukkitSchedulerTimer.startTiming(); // Spigot // Paper ++ //MinecraftTimings.bukkitSchedulerTimer.startTiming(); // Spigot // Paper // Purpur + // Folia - region threading +- MinecraftTimings.bukkitSchedulerTimer.stopTiming(); // Spigot // Paper ++ //MinecraftTimings.bukkitSchedulerTimer.stopTiming(); // Spigot // Paper // Purpur + // Folia - region threading - moved to global tick - and moved entity scheduler to tickRegion + this.profiler.push("commandFunctions"); +- MinecraftTimings.commandFunctionsTimer.startTiming(); // Spigot // Paper ++ //MinecraftTimings.commandFunctionsTimer.startTiming(); // Spigot // Paper // Purpur + if (region == null) this.getFunctions().tick(); // Folia - region threading - TODO Purge functions +- MinecraftTimings.commandFunctionsTimer.stopTiming(); // Spigot // Paper ++ //MinecraftTimings.commandFunctionsTimer.stopTiming(); // Spigot // Paper // Purpur + this.profiler.popPush("levels"); + //Iterator iterator = this.getAllLevels().iterator(); // Paper - Throw exception on world create while being ticked; moved down + + // CraftBukkit start + // Run tasks that are waiting on processing +- MinecraftTimings.processQueueTimer.startTiming(); // Spigot ++ //MinecraftTimings.processQueueTimer.startTiming(); // Spigot // Purpur + if (region == null) while (!this.processQueue.isEmpty()) { // Folia - region threading + this.processQueue.remove().run(); + } +- MinecraftTimings.processQueueTimer.stopTiming(); // Spigot ++ //MinecraftTimings.processQueueTimer.stopTiming(); // Spigot // Purpur + +- MinecraftTimings.timeUpdateTimer.startTiming(); // Spigot // Paper ++ //MinecraftTimings.timeUpdateTimer.startTiming(); // Spigot // Paper // Purpur + // Send time updates to everyone, it will get the right time from the world the player is in. + // Paper start - Perf: Optimize time updates + for (final ServerLevel level : (region == null ? this.getAllLevels() : Arrays.asList(region.world))) { // Folia - region threading +@@ -1865,7 +1861,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop chunks = (it.unimi.dsi.fastutil.objects.ObjectArrayList)list; +@@ -571,7 +571,7 @@ public class ServerChunkCache extends ChunkSource implements ca.spottedleaf.moon + } + } + } finally { profiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.BROADCAST_BLOCK_CHANGES); } // Folia - profiler +- this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing ++ //this.level.timings.broadcastChunkUpdates.stopTiming(); // Paper - timing // Purpur + // Paper end - chunk tick iteration optimisations + gameprofilerfiller.pop(); + gameprofilerfiller.pop(); +diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java +index 9a9be6f618c361db39eff0f0397bbc21615da411..cdc46735be4fcb4d992dfb2d7e662a458fc29878 100644 +--- a/src/main/java/net/minecraft/server/level/ServerLevel.java ++++ b/src/main/java/net/minecraft/server/level/ServerLevel.java +@@ -1,7 +1,6 @@ + package net.minecraft.server.level; + + import com.google.common.annotations.VisibleForTesting; +-import co.aikar.timings.TimingHistory; // Paper + import com.google.common.collect.Lists; + import com.mojang.datafixers.DataFixer; + import com.mojang.datafixers.util.Pair; +@@ -735,7 +734,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + } + + gameprofilerfiller.popPush("tickPending"); +- this.timings.scheduledBlocks.startTiming(); // Paper ++ //this.timings.scheduledBlocks.startTiming(); // Paper // Purpur + if (!this.isDebug() && flag) { + j = regionizedWorldData.getRedstoneGameTime(); // Folia - region threading + gameprofilerfiller.push("blockTicks"); +@@ -748,30 +747,30 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + } finally { profiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.FLUID_TICK); } // Folia - profiler + gameprofilerfiller.pop(); + } +- this.timings.scheduledBlocks.stopTiming(); // Paper ++ //this.timings.scheduledBlocks.stopTiming(); // Paper // Purpur + + gameprofilerfiller.popPush("raid"); + if (flag) { +- this.timings.raids.startTiming(); // Paper - timings ++ // this.timings.raids.startTiming(); // Paper - timings // Purpur + profiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.RAIDS_TICK); try { // Folia - profiler + this.raids.tick(); + } finally { profiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.RAIDS_TICK); } // Folia - profiler +- this.timings.raids.stopTiming(); // Paper - timings ++ // this.timings.raids.stopTiming(); // Paper - timings // Purpur + } + + gameprofilerfiller.popPush("chunkSource"); +- this.timings.chunkProviderTick.startTiming(); // Paper - timings ++ //this.timings.chunkProviderTick.startTiming(); // Paper - timings // Purpur + profiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.CHUNK_PROVIDER_TICK); try { // Folia - profiler + this.getChunkSource().tick(shouldKeepTicking, true); + } finally { profiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.CHUNK_PROVIDER_TICK); } // Folia - profiler +- this.timings.chunkProviderTick.stopTiming(); // Paper - timings ++ //this.timings.chunkProviderTick.stopTiming(); // Paper - timings // Purpur + gameprofilerfiller.popPush("blockEvents"); + if (flag) { +- this.timings.doSounds.startTiming(); // Spigot ++ // this.timings.doSounds.startTiming(); // Spigot // Purpur + profiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.BLOCK_EVENT_TICK); try { // Folia - profiler + this.runBlockEvents(); + } finally { profiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.BLOCK_EVENT_TICK); } // Folia - profiler +- this.timings.doSounds.stopTiming(); // Spigot ++ // this.timings.doSounds.stopTiming(); // Spigot // Purpur + } + + regionizedWorldData.setHandlingTick(false); // Folia - regionised ticking +@@ -784,7 +783,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + + if (flag1 || this.emptyTime++ < 300) { + gameprofilerfiller.push("entities"); +- this.timings.tickEntities.startTiming(); // Spigot ++ //this.timings.tickEntities.startTiming(); // Spigot // Purpur + if (this.dragonFight != null && flag) { + profiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.DRAGON_FIGHT_TICK); try { // Folia - profiler + if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this, this.dragonFight.origin)) { // Folia - region threading +@@ -803,7 +802,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + } + + org.spigotmc.ActivationRange.activateEntities(this); // Spigot +- this.timings.entityTick.startTiming(); // Spigot ++ //this.timings.entityTick.startTiming(); // Spigot // Purpur + profiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.ENTITY_TICK); try { // Folia - profiler + regionizedWorldData.forEachTickingEntity((entity) -> { // Folia - regionised ticking + entity.activatedPriorityReset = false; // Pufferfish - DAB +@@ -834,8 +833,8 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + } + }); + } finally { profiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.ENTITY_TICK); } // Folia - profiler +- this.timings.entityTick.stopTiming(); // Spigot +- this.timings.tickEntities.stopTiming(); // Spigot ++ //this.timings.entityTick.stopTiming(); // Spigot // Purpur ++ //this.timings.tickEntities.stopTiming(); // Spigot // Purpur + gameprofilerfiller.pop(); + profiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.TILE_ENTITY); try { // Folia - profiler + this.tickBlockEntities(); +@@ -1034,12 +1033,12 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + } // Paper - Option to disable ice and snow + + gameprofilerfiller.popPush("tickBlocks"); +- timings.chunkTicksBlocks.startTiming(); // Paper ++ //timings.chunkTicksBlocks.startTiming(); // Paper // Purpur + if (randomTickSpeed > 0) { + this.optimiseRandomTick(chunk, randomTickSpeed); // Paper - optimise random ticking + } + +- timings.chunkTicksBlocks.stopTiming(); // Paper ++ //timings.chunkTicksBlocks.stopTiming(); // Paper // Purpur + gameprofilerfiller.pop(); + } + +@@ -1360,9 +1359,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + currentlyTickingEntity.lazySet(entity); + } + // Paper end - log detailed entity tick information +- ++TimingHistory.entityTicks; // Paper - timings + // Spigot start +- co.aikar.timings.Timing timer; // Paper + /*if (!org.spigotmc.ActivationRange.checkIfActive(entity)) { // Paper - comment out - EAR 2, reimplement below + entity.tickCount++; + timer = entity.getType().inactiveTickTimer.startTiming(); try { // Paper - timings +@@ -1373,7 +1370,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + // Spigot end + // Paper start- timings + final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity); +- timer = isActive ? entity.getType().tickTimer.startTiming() : entity.getType().inactiveTickTimer.startTiming(); // Paper ++ //timer = isActive ? entity.getType().tickTimer.startTiming() : entity.getType().inactiveTickTimer.startTiming(); // Paper // Purpur + // Folia start - timer + final int timerId = isActive ? entity.getType().tickTimerId : entity.getType().inactiveTickTimerId; + final ca.spottedleaf.leafprofiler.RegionizedProfiler.Handle profiler = io.papermc.paper.threadedregions.TickRegionScheduler.getProfiler(); +@@ -1390,7 +1387,6 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + }); + gameprofilerfiller.incrementCounter("tickNonPassenger"); + if (isActive) { // Paper - EAR 2 +- TimingHistory.activatedEntityTicks++; + entity.tick(); + // Folia start - region threading + if (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(entity)) { +@@ -1404,7 +1400,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + // Folia end - region threading + } else { entity.inactiveTick(); } // Paper - EAR 2 + this.getProfiler().pop(); +- } finally { timer.stopTiming(); profiler.stopTimer(timerId); } // Paper - timings // Folia - timer ++ } finally { /*timer.stopTiming();*/ profiler.stopTimer(timerId); } // Paper - timings // Folia - timer // Purpur + Iterator iterator = entity.getPassengers().iterator(); + + while (iterator.hasNext()) { +@@ -1427,7 +1423,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + if (passenger instanceof Player || this.getCurrentWorldData().hasEntityTickingEntity(passenger)) { // Folia - region threading + // Paper - EAR 2 + final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(passenger); +- co.aikar.timings.Timing timer = isActive ? passenger.getType().passengerTickTimer.startTiming() : passenger.getType().passengerInactiveTickTimer.startTiming(); // Paper ++ //co.aikar.timings.Timing timer = isActive ? passenger.getType().passengerTickTimer.startTiming() : passenger.getType().passengerInactiveTickTimer.startTiming(); // Paper // Purpur + // Folia start - timer + final int timerId = isActive ? passenger.getType().tickTimerId : passenger.getType().inactiveTickTimerId; + final ca.spottedleaf.leafprofiler.RegionizedProfiler.Handle profiler = io.papermc.paper.threadedregions.TickRegionScheduler.getProfiler(); +@@ -1472,7 +1468,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + this.tickPassenger(passenger, entity2); + } + +- } finally { timer.stopTiming(); profiler.stopTimer(timerId); }// Paper - EAR2 timings // Folia - timer ++ } finally { /*timer.stopTiming();*/ profiler.stopTimer(timerId); }// Paper - EAR2 timings // Folia - timer // Purpur + } + } else { + passenger.stopRiding(); +@@ -1492,7 +1488,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(getWorld())); + } + +- try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) { ++ //try (co.aikar.timings.Timing ignored = this.timings.worldSave.startTiming()) { // Purpur + if (doFull) { + this.saveLevelData(true); + } +@@ -1509,7 +1505,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + this.convertable.saveDataTag(this.server.registryAccess(), this.serverLevelData, this.server.getPlayerList().getSingleplayerData()); + } + // CraftBukkit end +- } ++ //} // Purpur + } + // Paper end - Incremental chunk and player saving + +@@ -1523,7 +1519,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + + if (!savingDisabled) { + org.bukkit.Bukkit.getPluginManager().callEvent(new org.bukkit.event.world.WorldSaveEvent(this.getWorld())); // CraftBukkit +- try (co.aikar.timings.Timing ignored = timings.worldSave.startTiming()) { // Paper ++ //try (co.aikar.timings.Timing ignored = timings.worldSave.startTiming()) { // Paper // Purpur // Purpur + if (progressListener != null) { + progressListener.progressStartNoAbort(Component.translatable("menu.savingLevel")); + } +@@ -1533,10 +1529,10 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. + progressListener.progressStage(Component.translatable("menu.savingChunks")); + } + +- timings.worldSaveChunks.startTiming(); // Paper ++ //timings.worldSaveChunks.startTiming(); // Paper // Purpur + if (!close) { chunkproviderserver.save(flush); } // Paper - add close param +- timings.worldSaveChunks.stopTiming(); // Paper +- }// Paper ++ //timings.worldSaveChunks.stopTiming(); // Paper // Purpur ++ //}// Paper // Purpur + // Paper - rewrite chunk system + + } +diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java +index 9741cad817e5bb0d8efc134f7fdb9f6142891425..c5c821344d1fc97642d4445b7825c67b46d51176 100644 +--- a/src/main/java/net/minecraft/server/players/PlayerList.java ++++ b/src/main/java/net/minecraft/server/players/PlayerList.java +@@ -1,6 +1,5 @@ + package net.minecraft.server.players; + +-import co.aikar.timings.MinecraftTimings; + import com.google.common.collect.Lists; + import com.google.common.collect.Maps; + import com.google.common.collect.Sets; +@@ -1293,7 +1292,7 @@ public abstract class PlayerList { + public void saveAll(int interval) { + io.papermc.paper.util.MCUtil.ensureMain("Save Players" , () -> { // Paper - Ensure main + final ca.spottedleaf.leafprofiler.RegionizedProfiler.Handle profiler = io.papermc.paper.threadedregions.TickRegionScheduler.getProfiler(); // Folia - profiler +- MinecraftTimings.savePlayers.startTiming(); // Paper ++ //MinecraftTimings.savePlayers.startTiming(); // Paper // Purpur + int numSaved = 0; + long now = System.nanoTime(); // Folia - region threading + int max = io.papermc.paper.configuration.GlobalConfiguration.get().playerAutoSave.maxPerTick(); // Folia - region threading +@@ -1315,7 +1314,7 @@ public abstract class PlayerList { + } + // Paper end - Incremental chunk and player saving + } +- MinecraftTimings.savePlayers.stopTiming(); // Paper ++ //MinecraftTimings.savePlayers.stopTiming(); // Paper // Purpur + return null; }); // Paper - ensure main + } + +diff --git a/src/main/java/net/minecraft/world/entity/EntityType.java b/src/main/java/net/minecraft/world/entity/EntityType.java +index da815f30a44cbfe577b75b79e4527930fc6980d7..280b65d9ea6cde9f417057632c305645de10c1ed 100644 +--- a/src/main/java/net/minecraft/world/entity/EntityType.java ++++ b/src/main/java/net/minecraft/world/entity/EntityType.java +@@ -351,10 +351,7 @@ public class EntityType implements FeatureElement, EntityTypeT + this(factory, spawnGroup, saveable, summonable, fireImmune, spawnableFarFromPlayer, canSpawnInside, dimensions, spawnBoxScale, maxTrackDistance, trackTickInterval, requiredFeatures, "custom"); + } + public EntityType(EntityType.EntityFactory factory, MobCategory spawnGroup, boolean saveable, boolean summonable, boolean fireImmune, boolean spawnableFarFromPlayer, ImmutableSet canSpawnInside, EntityDimensions dimensions, float spawnBoxScale, int maxTrackDistance, int trackTickInterval, FeatureFlagSet requiredFeatures, String id) { +- this.tickTimer = co.aikar.timings.MinecraftTimings.getEntityTimings(id, "tick"); +- this.inactiveTickTimer = co.aikar.timings.MinecraftTimings.getEntityTimings(id, "inactiveTick"); +- this.passengerTickTimer = co.aikar.timings.MinecraftTimings.getEntityTimings(id, "passengerTick"); +- this.passengerInactiveTickTimer = co.aikar.timings.MinecraftTimings.getEntityTimings(id, "passengerInactiveTick"); ++ + // Paper end + // Folia start - profiler + this.tickTimerId = ca.spottedleaf.leafprofiler.LProfilerRegistry.GLOBAL_REGISTRY.getOrCreateTimer("Entity Tick: " + id); +@@ -714,12 +711,6 @@ public class EntityType implements FeatureElement, EntityTypeT + return this.updateInterval; + } + +- // Paper start - timings +- public final co.aikar.timings.Timing tickTimer; +- public final co.aikar.timings.Timing inactiveTickTimer; +- public final co.aikar.timings.Timing passengerTickTimer; +- public final co.aikar.timings.Timing passengerInactiveTickTimer; +- // Paper end + public boolean trackDeltas() { + return this != EntityType.PLAYER && this != EntityType.LLAMA_SPIT && this != EntityType.WITHER && this != EntityType.BAT && this != EntityType.ITEM_FRAME && this != EntityType.GLOW_ITEM_FRAME && this != EntityType.LEASH_KNOT && this != EntityType.PAINTING && this != EntityType.END_CRYSTAL && this != EntityType.EVOKER_FANGS; + } +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index 873e9b488a14866c99779a5b5e5f3d1c211febd6..bd2f3c8f28615b60a02ca09092e1429d5d4ae77c 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -157,7 +157,6 @@ import org.bukkit.event.entity.EntityTeleportEvent; + import org.bukkit.event.player.PlayerItemConsumeEvent; + // CraftBukkit end + +-import co.aikar.timings.MinecraftTimings; // Paper + + public abstract class LivingEntity extends Entity implements Attackable { + +diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java +index 9379dd4056018b52c93ed4888dcdc94579bd9691..4ba50f688566f789c7d06cab57fb17cb4fb22760 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java ++++ b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java +@@ -16,7 +16,6 @@ public abstract class Behavior implements BehaviorContro + private final int maxDuration; + // Paper start - configurable behavior tick rate and timings + private final String configKey; +- private final co.aikar.timings.Timing timing; + // Paper end - configurable behavior tick rate and timings + + public Behavior(Map, MemoryStatus> requiredMemoryState) { +@@ -38,7 +37,6 @@ public abstract class Behavior implements BehaviorContro + key = key.substring(lastSeparator + 1); + } + this.configKey = key.toLowerCase(java.util.Locale.ROOT); +- this.timing = co.aikar.timings.MinecraftTimings.getBehaviorTimings(configKey); + // Paper end - configurable behavior tick rate and timings + } + +@@ -59,9 +57,9 @@ public abstract class Behavior implements BehaviorContro + this.status = Behavior.Status.RUNNING; + int i = this.minDuration + world.getRandom().nextInt(this.maxDuration + 1 - this.minDuration); + this.endTimestamp = time + (long)i; +- this.timing.startTiming(); // Paper - behavior timings ++ //this.timing.startTiming(); // Paper - behavior timings // Purpur + this.start(world, entity, time); +- this.timing.stopTiming(); // Paper - behavior timings ++ //this.timing.stopTiming(); // Paper - behavior timings // Purpur + return true; + } else { + return false; +@@ -73,13 +71,13 @@ public abstract class Behavior implements BehaviorContro + + @Override + public final void tickOrStop(ServerLevel world, E entity, long time) { +- this.timing.startTiming(); // Paper - behavior timings ++ //this.timing.startTiming(); // Paper - behavior timings // Purpur + if (!this.timedOut(time) && this.canStillUse(world, entity, time)) { + this.tick(world, entity, time); + } else { + this.doStop(world, entity, time); + } +- this.timing.stopTiming(); // Paper - behavior timings ++ //this.timing.stopTiming(); // Paper - behavior timings // Purpur + } + + protected void tick(ServerLevel world, E entity, long time) { +diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java +index 85b4b24361e785acf75571ff98f924c00ae80748..1e26b1099139d76e49fce14ee72061e10bf4dcb9 100644 +--- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java ++++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java +@@ -28,7 +28,6 @@ public abstract class Sensor { + private long timeToTick; + // Paper start - configurable sensor tick rate and timings + private final String configKey; +- private final co.aikar.timings.Timing timing; + // Paper end + + public Sensor(int senseInterval) { +@@ -39,7 +38,6 @@ public abstract class Sensor { + key = key.substring(lastSeparator + 1); + } + this.configKey = key.toLowerCase(java.util.Locale.ROOT); +- this.timing = co.aikar.timings.MinecraftTimings.getSensorTimings(configKey, senseInterval); + // Paper end + this.scanRate = senseInterval; + this.timeToTick = (long)RANDOM.nextInt(senseInterval); +@@ -53,10 +51,10 @@ public abstract class Sensor { + if (--this.timeToTick <= 0L) { + // Paper start - configurable sensor tick rate and timings + this.timeToTick = java.util.Objects.requireNonNullElse(world.paperConfig().tickRates.sensor.get(entity.getType(), this.configKey), this.scanRate); +- this.timing.startTiming(); ++ //this.timing.startTiming(); // Purpur + // Paper end + this.doTick(world, entity); +- this.timing.stopTiming(); // Paper - sensor timings ++ //this.timing.stopTiming(); // Paper - sensor timings // Purpur + } + } + +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 4bb14874912557008fab7361754f2d2eb98fc5aa..6c34b4add39f227e876616320abd9e3302be8c1f 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -162,7 +162,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + // Paper end - add paper world config + + public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray +- public final co.aikar.timings.WorldTimingsHandler timings; // Paper + public static BlockPos lastPhysicsProblem; // Spigot + private org.spigotmc.TickLimiter entityLimiter; + private org.spigotmc.TickLimiter tileLimiter; +@@ -781,7 +780,6 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + public void onBorderSetDamageSafeZOne(WorldBorder border, double safeZoneRadius) {} + }); + // CraftBukkit end +- this.timings = new co.aikar.timings.WorldTimingsHandler(this); // Paper - code below can generate new world and access timings + this.entityLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.entityMaxTickTime); + this.tileLimiter = new org.spigotmc.TickLimiter(this.spigotConfig.tileMaxTickTime); + this.entityLookup = new ca.spottedleaf.moonrise.patches.chunk_system.level.entity.dfl.DefaultEntityLookup(this); // Paper - rewrite chunk system +@@ -1416,16 +1414,16 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + ProfilerFiller gameprofilerfiller = this.getProfiler(); + + gameprofilerfiller.push("blockEntities"); +- this.timings.tileEntityPending.startTiming(); // Spigot ++ //this.timings.tileEntityPending.startTiming(); // Spigot // Purpur + final io.papermc.paper.threadedregions.RegionizedWorldData regionizedWorldData = this.getCurrentWorldData(); // Folia - regionised ticking + regionizedWorldData.seTtickingBlockEntities(true); // Folia - regionised ticking + profiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.TILE_ENTITY_PENDING); try { // Folia - profiler + regionizedWorldData.pushPendingTickingBlockEntities(); // Folia - regionised ticking + } finally { profiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.TILE_ENTITY_PENDING); } // Folia - profiler + List blockEntityTickers = regionizedWorldData.getBlockEntityTickers(); // Folia - regionised ticking +- this.timings.tileEntityPending.stopTiming(); // Spigot ++ //this.timings.tileEntityPending.stopTiming(); // Spigot // Purpur + +- this.timings.tileEntityTick.startTiming(); // Spigot ++ //this.timings.tileEntityTick.startTiming(); // Spigot // Purpur + profiler.startTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.TILE_ENTITY_TICK); try { // Folia - profiler + // Spigot start + // Iterator iterator = this.blockEntityTickers.iterator(); +@@ -1457,7 +1455,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl + blockEntityTickers.removeAll(toRemove); // Paper - Fix MC-117075 // Folia - regionised ticking + } finally { profiler.stopTimer(ca.spottedleaf.leafprofiler.LProfilerRegistry.TILE_ENTITY_TICK); } // Folia - profiler + +- this.timings.tileEntityTick.stopTiming(); // Spigot ++ //this.timings.tileEntityTick.stopTiming(); // Spigot // Purpur + regionizedWorldData.seTtickingBlockEntities(false); // Folia - regionised ticking + //co.aikar.timings.TimingHistory.tileEntityTicks += this.blockEntityTickers.size(); // Paper // Folia - region threading + gameprofilerfiller.pop(); +diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +index dd02f20e965e6e4aaaf5992ea3c4bb0b7f3cc270..f6d94cde5b20c6a25e73a0f739e564b302936815 100644 +--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java ++++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java +@@ -128,7 +128,7 @@ public final class NaturalSpawner { + + public static void spawnForChunk(ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnState info, boolean spawnAnimals, boolean spawnMonsters, boolean rareSpawn) { + world.getProfiler().push("spawner"); +- world.timings.mobSpawn.startTiming(); // Spigot ++ //world.timings.mobSpawn.startTiming(); // Spigot // Purpur + MobCategory[] aenumcreaturetype = NaturalSpawner.SPAWNING_CATEGORIES; + int i = aenumcreaturetype.length; + +@@ -181,7 +181,7 @@ public final class NaturalSpawner { + } + } + +- world.timings.mobSpawn.stopTiming(); // Spigot ++ //world.timings.mobSpawn.stopTiming(); // Spigot // Purpur + world.getProfiler().pop(); + } + +diff --git a/src/main/java/net/minecraft/world/level/block/Block.java b/src/main/java/net/minecraft/world/level/block/Block.java +index bf9d00463627c702c639c7cb625c3eb35c2e44aa..70157d581f8cbe60f86f944e7c2b0ce9c722959a 100644 +--- a/src/main/java/net/minecraft/world/level/block/Block.java ++++ b/src/main/java/net/minecraft/world/level/block/Block.java +@@ -103,13 +103,6 @@ public class Block extends BlockBehaviour implements ItemLike { + this != Blocks.STRUCTURE_BLOCK && + this != Blocks.JIGSAW; + } +- public co.aikar.timings.Timing timing; +- public co.aikar.timings.Timing getTiming() { +- if (timing == null) { +- timing = co.aikar.timings.MinecraftTimings.getBlockTiming(this); +- } +- return timing; +- } + // Paper end + @Nullable + private String descriptionId; +diff --git a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java +index 4c2e8714c64a6fe49554a11471e37078267f1eea..c8bb3d7f299d43326daaa12aa119b0eb37cb4b38 100644 +--- a/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java ++++ b/src/main/java/net/minecraft/world/level/block/entity/BlockEntity.java +@@ -33,14 +33,10 @@ import org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry; + import org.bukkit.inventory.InventoryHolder; + // CraftBukkit end + +-import org.spigotmc.CustomTimingsHandler; // Spigot +-import co.aikar.timings.MinecraftTimings; // Paper +-import co.aikar.timings.Timing; // Paper + + public abstract class BlockEntity { + static final ThreadLocal IGNORE_TILE_UPDATES = ThreadLocal.withInitial(() -> Boolean.FALSE); // Paper - Perf: Optimize Hoppers // Folia - region threading + +- public Timing tickTimer = MinecraftTimings.getTileEntityTimings(this); // Paper + // CraftBukkit start - data containers + private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry(); + public CraftPersistentDataContainer persistentDataContainer; +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 42ae955ff55e868f9eedde61a21ec26763fd7700..52e4984b8490867772a0ac3410fd6e1cb97847b6 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java ++++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java +@@ -683,7 +683,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + ((ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemServerLevel)this.level).moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(this.locX, this.locZ).getEntityChunk().callEntitiesLoadEvent(); // Paper - rewrite chunk system + + if (this.needsDecoration) { +- try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper ++ //try (co.aikar.timings.Timing ignored = this.level.timings.chunkLoadPopulate.startTiming()) { // Paper // Purpur + this.needsDecoration = false; + java.util.Random random = new java.util.Random(); + random.setSeed(this.level.getSeed()); +@@ -703,7 +703,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + } + } + server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(bukkitChunk)); +- } // Paper ++ //} // Paper // Purpur + } + } + } +@@ -1059,7 +1059,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + ProfilerFiller gameprofilerfiller = LevelChunk.this.level.getProfiler(); + + gameprofilerfiller.push(this::getType); +- this.blockEntity.tickTimer.startTiming(); // Spigot ++ //this.blockEntity.tickTimer.startTiming(); // Spigot // Purpur + profiler.startTimer(timerId); try { // Folia - profiler + BlockState iblockdata = LevelChunk.this.getBlockState(blockposition); + +@@ -1088,7 +1088,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p + // Paper end - Prevent block entity and entity crashes + // Spigot start + } finally { +- this.blockEntity.tickTimer.stopTiming(); ++ //this.blockEntity.tickTimer.stopTiming(); // Purpur + // Spigot end + } + } +diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +index 3d6c4d0cb4a92660c353fb9604e6bbff93dc692f..63fbcd316b974be65db7a1faa4228a54c5fa68f9 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java ++++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java +@@ -1,6 +1,5 @@ + package org.bukkit.craftbukkit.scheduler; + +-import co.aikar.timings.MinecraftTimings; // Paper + import com.google.common.base.Preconditions; + import com.google.common.util.concurrent.ThreadFactoryBuilder; + +@@ -298,7 +297,7 @@ public class CraftScheduler implements BukkitScheduler { + } + return false; + } +- }){{this.timings=co.aikar.timings.MinecraftTimings.getCancelTasksTimer();}}; // Paper ++ }){}; // Paper + this.handle(task, 0L); + for (CraftTask taskPending = this.head.getNext(); taskPending != null; taskPending = taskPending.getNext()) { + if (taskPending == task) { +@@ -340,7 +339,7 @@ public class CraftScheduler implements BukkitScheduler { + } + } + } +- }){{this.timings=co.aikar.timings.MinecraftTimings.getCancelTasksTimer(plugin);}}; // Paper ++ }){}; // Paper + this.handle(task, 0L); + for (CraftTask taskPending = this.head.getNext(); taskPending != null; taskPending = taskPending.getNext()) { + if (taskPending == task) { +@@ -508,10 +507,10 @@ public class CraftScheduler implements BukkitScheduler { + this.runners.remove(task.getTaskId()); + } + } +- MinecraftTimings.bukkitSchedulerFinishTimer.startTiming(); // Paper ++ //MinecraftTimings.bukkitSchedulerFinishTimer.startTiming(); // Paper // Purpur + this.pending.addAll(temp); + temp.clear(); +- MinecraftTimings.bukkitSchedulerFinishTimer.stopTiming(); // Paper ++ //MinecraftTimings.bukkitSchedulerFinishTimer.stopTiming(); // Paper // Purpur + //this.debugHead = this.debugHead.getNextHead(currentTick); // Paper + } + +@@ -609,7 +608,7 @@ public class CraftScheduler implements BukkitScheduler { + } + + void parsePending() { // Paper +- if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.startTiming(); // Paper ++ //if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.startTiming(); // Paper // Purpur + CraftTask head = this.head; + CraftTask task = head.getNext(); + CraftTask lastTask = head; +@@ -628,7 +627,7 @@ public class CraftScheduler implements BukkitScheduler { + task.setNext(null); + } + this.head = lastTask; +- if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.stopTiming(); // Paper ++ //if (!this.isAsyncScheduler) MinecraftTimings.bukkitSchedulerPendingTimer.stopTiming(); // Paper // Purpur + } + + private boolean isReady(final int currentTick) { +diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java +index 4a79c28fe61bec567db40936ad65ae8a60874ff7..551ac25383730ff553f6be8286cc8a5dbd816887 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java ++++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftTask.java +@@ -2,16 +2,10 @@ package org.bukkit.craftbukkit.scheduler; + + import java.util.function.Consumer; + +-import co.aikar.timings.NullTimingHandler; + import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +-import org.bukkit.Bukkit; + import org.bukkit.plugin.Plugin; + import org.bukkit.scheduler.BukkitTask; + +-import org.spigotmc.CustomTimingsHandler; // Spigot +-import co.aikar.timings.MinecraftTimings; // Paper +-import co.aikar.timings.Timing; // Paper +- + public class CraftTask implements BukkitTask, Runnable { // Spigot + + private volatile CraftTask next = null; +@@ -32,7 +26,6 @@ public class CraftTask implements BukkitTask, Runnable { // Spigot + private long nextRun; + public final Runnable rTask; // Paper + public final Consumer cTask; // Paper +- public Timing timings; // Paper + private final Plugin plugin; + private final int id; + private final long createdAt = System.nanoTime(); +@@ -63,7 +56,6 @@ public class CraftTask implements BukkitTask, Runnable { // Spigot + } + this.id = id; + this.period = period; +- timings = task != null ? MinecraftTimings.getPluginTaskTimings(this, period) : NullTimingHandler.NULL; // Paper + } + + @Override +@@ -86,13 +78,13 @@ public class CraftTask implements BukkitTask, Runnable { // Spigot + + @Override + public void run() { +- try (Timing ignored = timings.startTiming()) { // Paper ++ //try (Timing ignored = timings.startTiming()) { // Paper // Purpur + if (this.rTask != null) { + this.rTask.run(); + } else { + this.cTask.accept(this); + } +- } // Paper ++ //} // Paper // Purpur + } + + long getCreatedAt() { +diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java +index 802688c2091c3f1a31f02599fb73432811d588ee..2bc35cd9830c562153012a132ab6a67b1fbc12d1 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java ++++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java +@@ -117,7 +117,7 @@ public final class CraftScoreboardManager implements ScoreboardManager { + public void forAllObjectives(ObjectiveCriteria criteria, ScoreHolder holder, Consumer consumer) { + // Paper start - add timings for scoreboard search + // plugins leaking scoreboards will make this very expensive, let server owners debug it easily +- co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.startTimingIfSync(); ++ //co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.startTimingIfSync(); // Purpur + try { + // Paper end - add timings for scoreboard search + for (CraftScoreboard scoreboard : this.scoreboards) { +@@ -125,7 +125,7 @@ public final class CraftScoreboardManager implements ScoreboardManager { + board.forAllObjectives(criteria, holder, (score) -> consumer.accept(score)); + } + } finally { // Paper start - add timings for scoreboard search +- co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.stopTimingIfSync(); ++ //co.aikar.timings.MinecraftTimings.scoreboardScoreSearch.stopTimingIfSync(); // Purpur + } + // Paper end - add timings for scoreboard search + } +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index f89e08c22bc76dba5df730a83b37324e38d2956d..e94f5248d622a59db4feb05d955885f5b4a23fe2 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -29,6 +29,7 @@ import net.minecraft.nbt.Tag; + import net.minecraft.resources.ResourceLocation; + import net.minecraft.server.MinecraftServer; + import net.minecraft.server.ServerAdvancementManager; ++import net.minecraft.server.dedicated.DedicatedServer; + import net.minecraft.util.datafix.DataFixers; + import net.minecraft.util.datafix.fixes.References; + import net.minecraft.world.item.Item; +@@ -113,6 +114,11 @@ public final class CraftMagicNumbers implements UnsafeValues { + public net.kyori.adventure.text.Component resolveWithContext(final net.kyori.adventure.text.Component component, final org.bukkit.command.CommandSender context, final org.bukkit.entity.Entity scoreboardSubject, final boolean bypassPermissions) throws IOException { + return io.papermc.paper.adventure.PaperAdventure.resolveWithContext(component, context, scoreboardSubject, bypassPermissions); + } ++ ++ @Override ++ public void reportTimings() { ++ ++ } + // Paper end + + public static BlockState getBlock(MaterialData material) { +@@ -196,13 +202,6 @@ public final class CraftMagicNumbers implements UnsafeValues { + public static ResourceLocation key(Material mat) { + return CraftNamespacedKey.toMinecraft(mat.getKey()); + } +- // ======================================================================== +- // Paper start +- @Override +- public void reportTimings() { +- co.aikar.timings.TimingsExport.reportTimings(); +- } +- // Paper end + + public static byte toLegacyData(BlockState data) { + return CraftLegacy.toLegacyData(data); +@@ -488,7 +487,7 @@ public final class CraftMagicNumbers implements UnsafeValues { + // Paper start + @Override + public String getTimingsServerName() { +- return io.papermc.paper.configuration.GlobalConfiguration.get().timings.serverName; ++ return DedicatedServer.getServer().getServerModName(); + } + + @Override +diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java +index 24b45224b6a9e3b86ddba550e720b62bd5638c5d..69784dbf1fc50b27d15582ca058b7b0027fa49ae 100644 +--- a/src/main/java/org/spigotmc/ActivationRange.java ++++ b/src/main/java/org/spigotmc/ActivationRange.java +@@ -34,7 +34,6 @@ import net.minecraft.world.entity.projectile.FireworkRocketEntity; + import net.minecraft.world.entity.projectile.ThrowableProjectile; + import net.minecraft.world.entity.projectile.ThrownTrident; + import net.minecraft.world.entity.raid.Raider; +-import co.aikar.timings.MinecraftTimings; + import net.minecraft.world.entity.schedule.Activity; + import net.minecraft.world.level.Level; + import net.minecraft.world.phys.AABB; +@@ -169,7 +168,7 @@ public class ActivationRange + */ + public static void activateEntities(Level world) + { +- MinecraftTimings.entityActivationCheckTimer.startTiming(); ++ //MinecraftTimings.entityActivationCheckTimer.startTiming(); // Purpur + final int miscActivationRange = world.spigotConfig.miscActivationRange; + final int raiderActivationRange = world.spigotConfig.raiderActivationRange; + final int animalActivationRange = world.spigotConfig.animalActivationRange; +@@ -254,7 +253,7 @@ public class ActivationRange + } + // Paper end + } +- MinecraftTimings.entityActivationCheckTimer.stopTiming(); ++ //MinecraftTimings.entityActivationCheckTimer.stopTiming(); // Purpur + } + + /** diff --git a/patches/server/0030-Add-chance-config-for-piglin-spawn-in-nether-portal.patch b/patches/server/0030-Add-chance-config-for-piglin-spawn-in-nether-portal.patch new file mode 100644 index 0000000..373729d --- /dev/null +++ b/patches/server/0030-Add-chance-config-for-piglin-spawn-in-nether-portal.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 16:19:01 +0400 +Subject: [PATCH] Add-chance-config-for-piglin-spawn-in-nether-portal + + +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index b52f7bcc17fba6a3a4376a4bfea17430be9c4a8e..553c02a53b9aed6c851305f1e885934677fa5daa 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -45,6 +45,8 @@ public class FoldenorConfig { + + public static boolean useAlternateKeepAlive = false; + ++ public static int piglinSpawnChancePersentInPortal = 100; ++ + public static void init(File configFile) { + init(configFile, true); + } +@@ -114,6 +116,8 @@ public class FoldenorConfig { + "the check in a way that still appears vanilla. This should", + "be left enabled on most servers, but is provided as a", + "configuration option if the vanilla deviation is undesirable."); ++ piglinSpawnChancePersentInPortal = getInt("optimizations.piglin-spawn-chance-persent-in-portal", 100, ++ "Reduces piglin spawn in portal, by reducing change to spawn"); + } + + private static void readDynamicActivationOfBrains() throws IOException { +diff --git a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java +index eba62f1336697157da94c7dcde389cc2e929bb0b..ca35a2a8a54459c7bfd373eaab6c4ebf6a24ef7b 100644 +--- a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java ++++ b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java +@@ -4,6 +4,8 @@ import com.mojang.logging.LogUtils; + import com.mojang.serialization.MapCodec; + import java.util.Optional; + import javax.annotation.Nullable; ++ ++import net.edenor.foldenor.config.FoldenorConfig; + import net.minecraft.BlockUtil; + import net.minecraft.core.BlockPos; + import net.minecraft.core.Direction; +@@ -77,7 +79,9 @@ public class NetherPortalBlock extends Block implements Portal { + + @Override + protected void randomTick(BlockState state, ServerLevel world, BlockPos pos, RandomSource random) { +- if (world.spigotConfig.enableZombiePigmenPortalSpawns && world.dimensionType().natural() && world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && random.nextInt(2000) < world.getDifficulty().getId()) { // Spigot ++ if (world.spigotConfig.enableZombiePigmenPortalSpawns && world.dimensionType().natural() ++ && world.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && random.nextInt(2000) < world.getDifficulty().getId() ++ && random.nextInt(100) >= FoldenorConfig.piglinSpawnChancePersentInPortal) { // Spigot // Foldenor + while (world.getBlockState(pos).is((Block) this)) { + pos = pos.below(); + } diff --git a/patches/server/0031-Leaf-Virtual-Thread-for-async-scheduler.patch b/patches/server/0031-Leaf-Virtual-Thread-for-async-scheduler.patch new file mode 100644 index 0000000..5618945 --- /dev/null +++ b/patches/server/0031-Leaf-Virtual-Thread-for-async-scheduler.patch @@ -0,0 +1,87 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 19:06:17 +0400 +Subject: [PATCH] Leaf-Virtual-Thread-for-async-scheduler + + +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index 553c02a53b9aed6c851305f1e885934677fa5daa..d2badf33e46350aec586342e4e97b65a748c353e 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -47,6 +47,8 @@ public class FoldenorConfig { + + public static int piglinSpawnChancePersentInPortal = 100; + ++ public static boolean useVirtualThreadForAsyncScheduler = false; ++ + public static void init(File configFile) { + init(configFile, true); + } +@@ -91,6 +93,8 @@ public class FoldenorConfig { + + readOptimizationSettings(); + ++ readMiscSettings(); ++ + try { + readDynamicActivationOfBrains(); + } catch (IOException e) { +@@ -120,6 +124,11 @@ public class FoldenorConfig { + "Reduces piglin spawn in portal, by reducing change to spawn"); + } + ++ private static void readMiscSettings() { ++ useVirtualThreadForAsyncScheduler = getBoolean("optimizations.use-virtual-thread-for-async-scheduler", useVirtualThreadForAsyncScheduler, ++ "Use the new Virtual Thread introduced in JDK 21 for CraftAsyncScheduler."); ++ } ++ + private static void readDynamicActivationOfBrains() throws IOException { + dearEnabled = getBoolean("dab.enabled", true); + startDistance = getInt("dab.start-distance", 12, +diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java +index 3c1992e212a6d6f1db4d5b807b38d71913619fc0..3a9d20bda722447247475a8d6aa8b64252f24ca8 100644 +--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java ++++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java +@@ -25,6 +25,7 @@ package org.bukkit.craftbukkit.scheduler; + + import com.destroystokyo.paper.ServerSchedulerReportingWrapper; + import com.google.common.util.concurrent.ThreadFactoryBuilder; ++import net.edenor.foldenor.config.FoldenorConfig; + import org.bukkit.plugin.Plugin; + + import java.util.ArrayList; +@@ -38,17 +39,29 @@ import java.util.concurrent.TimeUnit; + + public class CraftAsyncScheduler extends CraftScheduler { + +- private final ThreadPoolExecutor executor = new ThreadPoolExecutor( +- 4, Integer.MAX_VALUE,30L, TimeUnit.SECONDS, new SynchronousQueue<>(), +- new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").build()); ++ private final org.apache.logging.log4j.Logger LOGGER = org.apache.logging.log4j.LogManager.getLogger(getClass().getName()); // Leaf - Class logger ++ private final Executor executor; // Leaf - use super class + private final Executor management = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder() + .setNameFormat("Craft Async Scheduler Management Thread").build()); + private final List temp = new ArrayList<>(); + + CraftAsyncScheduler() { + super(true); +- executor.allowCoreThreadTimeOut(true); +- executor.prestartAllCoreThreads(); ++ // Leaf start - Ability to use Virtual Thread for async scheduler ++ if (FoldenorConfig.useVirtualThreadForAsyncScheduler) { ++ executor = Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name("Craft Scheduler Thread - ", 0).factory()); ++ return; ++ } ++ ++ executor = new ThreadPoolExecutor( ++ 4, Integer.MAX_VALUE, 30L, TimeUnit.SECONDS, new SynchronousQueue<>(), ++ new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); ++ ++ var threadPoolExecutor = (ThreadPoolExecutor) executor; ++ ++ threadPoolExecutor.allowCoreThreadTimeOut(true); ++ threadPoolExecutor.prestartAllCoreThreads(); ++ // Leaf end - Ability to use Virtual Thread for async scheduler + } + + @Override diff --git a/patches/server/0032-Carpet-Fixes-getBiome-Optimize.patch b/patches/server/0032-Carpet-Fixes-getBiome-Optimize.patch new file mode 100644 index 0000000..b6c007f --- /dev/null +++ b/patches/server/0032-Carpet-Fixes-getBiome-Optimize.patch @@ -0,0 +1,117 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 19:17:31 +0400 +Subject: [PATCH] Carpet-Fixes-getBiome-Optimize + + +diff --git a/src/main/java/net/minecraft/world/level/biome/BiomeManager.java b/src/main/java/net/minecraft/world/level/biome/BiomeManager.java +index 01352cc83b25eb0e30b7e0ff521fc7c1b3d5155b..4da3c8b4fadf120b3ce20a129ffa640736100aa3 100644 +--- a/src/main/java/net/minecraft/world/level/biome/BiomeManager.java ++++ b/src/main/java/net/minecraft/world/level/biome/BiomeManager.java +@@ -14,6 +14,7 @@ public class BiomeManager { + private static final int ZOOM_MASK = 3; + private final BiomeManager.NoiseBiomeSource noiseBiomeSource; + private final long biomeZoomSeed; ++ private static final double maxOffset = 0.4500000001D; // DivineMC - Carpet-Fixes: getBiome Optimize + + public BiomeManager(BiomeManager.NoiseBiomeSource storage, long seed) { + this.noiseBiomeSource = storage; +@@ -29,39 +30,67 @@ public class BiomeManager { + } + + public Holder getBiome(BlockPos pos) { +- int i = pos.getX() - 2; +- int j = pos.getY() - 2; +- int k = pos.getZ() - 2; +- int l = i >> 2; +- int m = j >> 2; +- int n = k >> 2; +- double d = (double)(i & 3) / 4.0; +- double e = (double)(j & 3) / 4.0; +- double f = (double)(k & 3) / 4.0; +- int o = 0; +- double g = Double.POSITIVE_INFINITY; +- +- for (int p = 0; p < 8; p++) { +- boolean bl = (p & 4) == 0; +- boolean bl2 = (p & 2) == 0; +- boolean bl3 = (p & 1) == 0; +- int q = bl ? l : l + 1; +- int r = bl2 ? m : m + 1; +- int s = bl3 ? n : n + 1; +- double h = bl ? d : d - 1.0; +- double t = bl2 ? e : e - 1.0; +- double u = bl3 ? f : f - 1.0; +- double v = getFiddledDistance(this.biomeZoomSeed, q, r, s, h, t, u); +- if (g > v) { +- o = p; +- g = v; ++ // DivineMC start - Carpet-Fixes: getBiome Optimize ++ int xMinus2 = pos.getX() - 2; ++ int yMinus2 = pos.getY() - 2; ++ int zMinus2 = pos.getZ() - 2; ++ int x = xMinus2 >> 2; // BlockPos to BiomePos ++ int y = yMinus2 >> 2; ++ int z = zMinus2 >> 2; ++ double quartX = (double) (xMinus2 & 3) / 4.0D; // quartLocal divided by 4 ++ double quartY = (double) (yMinus2 & 3) / 4.0D; // 0/4, 1/4, 2/4, 3/4 ++ double quartZ = (double) (zMinus2 & 3) / 4.0D; // [0, 0.25, 0.5, 0.75] ++ int smallestX = 0; ++ double smallestDist = Double.POSITIVE_INFINITY; ++ for (int biomeX = 0; biomeX < 8; ++biomeX) { ++ boolean everyOtherQuad = (biomeX & 4) == 0; // 1 1 1 1 0 0 0 0 ++ boolean everyOtherPair = (biomeX & 2) == 0; // 1 1 0 0 1 1 0 0 ++ boolean everyOther = (biomeX & 1) == 0; // 1 0 1 0 1 0 1 0 ++ double quartXX = everyOtherQuad ? quartX : quartX - 1.0D; //[-1.0,-0.75,-0.5,-0.25,0.0,0.25,0.5,0.75] ++ double quartYY = everyOtherPair ? quartY : quartY - 1.0D; ++ double quartZZ = everyOther ? quartZ : quartZ - 1.0D; ++ ++ double maxQuartYY = 0.0D, maxQuartZZ = 0.0D; ++ if (biomeX != 0) { ++ maxQuartYY = Mth.square(Math.max(quartYY + maxOffset, Math.abs(quartYY - maxOffset))); ++ maxQuartZZ = Mth.square(Math.max(quartZZ + maxOffset, Math.abs(quartZZ - maxOffset))); ++ double maxQuartXX = Mth.square(Math.max(quartXX + maxOffset, Math.abs(quartXX - maxOffset))); ++ if (smallestDist < maxQuartXX + maxQuartYY + maxQuartZZ) continue; + } +- } + +- int w = (o & 4) == 0 ? l : l + 1; +- int x = (o & 2) == 0 ? m : m + 1; +- int y = (o & 1) == 0 ? n : n + 1; +- return this.noiseBiomeSource.getNoiseBiome(w, x, y); ++ int xx = everyOtherQuad ? x : x + 1; ++ int yy = everyOtherPair ? y : y + 1; ++ int zz = everyOther ? z : z + 1; ++ ++ //I transferred the code from method_38106 to here, so I could call continue halfway through ++ long seed = LinearCongruentialGenerator.next(this.biomeZoomSeed, xx); ++ seed = LinearCongruentialGenerator.next(seed, yy); ++ seed = LinearCongruentialGenerator.next(seed, zz); ++ seed = LinearCongruentialGenerator.next(seed, xx); ++ seed = LinearCongruentialGenerator.next(seed, yy); ++ seed = LinearCongruentialGenerator.next(seed, zz); ++ double offsetX = getFiddle(seed); ++ double sqrX = Mth.square(quartXX + offsetX); ++ if (biomeX != 0 && smallestDist < sqrX + maxQuartYY + maxQuartZZ) continue; //skip the rest of the loop ++ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); ++ double offsetY = getFiddle(seed); ++ double sqrY = Mth.square(quartYY + offsetY); ++ if (biomeX != 0 && smallestDist < sqrX + sqrY + maxQuartZZ) continue; // skip the rest of the loop ++ seed = LinearCongruentialGenerator.next(seed, this.biomeZoomSeed); ++ double offsetZ = getFiddle(seed); ++ double biomeDist = sqrX + sqrY + Mth.square(quartZZ + offsetZ); ++ ++ if (smallestDist > biomeDist) { ++ smallestX = biomeX; ++ smallestDist = biomeDist; ++ } ++ } ++ return this.noiseBiomeSource.getNoiseBiome( ++ (smallestX & 4) == 0 ? x : x + 1, ++ (smallestX & 2) == 0 ? y : y + 1, ++ (smallestX & 1) == 0 ? z : z + 1 ++ ); ++ // DivineMC end + } + + public Holder getNoiseBiomeAtPosition(double x, double y, double z) { diff --git a/patches/server/0033-Sparkly-Paper-Optimize-canSee-checks.patch b/patches/server/0033-Sparkly-Paper-Optimize-canSee-checks.patch new file mode 100644 index 0000000..0a83775 --- /dev/null +++ b/patches/server/0033-Sparkly-Paper-Optimize-canSee-checks.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 19:22:58 +0400 +Subject: [PATCH] Sparkly-Paper-Optimize-canSee-checks + + +diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java +index f0173385e998e5dd0a6577a7a96804b327b0c1fa..70684caae94f15c6da4c998f164f3a997e7ceff1 100644 +--- a/src/main/java/net/minecraft/server/level/ChunkMap.java ++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java +@@ -1220,7 +1220,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider + // Folia end - region threading + + // CraftBukkit start - respect vanish API +- if (flag && (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(player) || !player.getBukkitEntity().canSee(this.entity.getBukkitEntity()))) { // Paper - only consider hits // Folia - region threading ++ if (flag && (!ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(player) || !player.getBukkitEntity().canSeeChunkMapUpdatePlayer(this.entity.getBukkitEntity()))) { // Paper - only consider hits // Folia - region threading // SparklyPaper - optimize canSee checks + flag = false; + } + // CraftBukkit end +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index eb1e14be98b3731e0557726324593dee2eadd31b..35adad2ef9d7a352bfa35b54be8265babb573bac 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -200,7 +200,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + private boolean hasPlayedBefore = false; + private final ConversationTracker conversationTracker = new ConversationTracker(); + private final Set channels = new HashSet(); +- private final Map>> invertedVisibilityEntities = new HashMap<>(); ++ private final Map>> invertedVisibilityEntities = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(); // SparklyPaper - optimize canSee checks + private final Set unlistedEntities = new HashSet<>(); // Paper - Add Listing API for Player + private static final WeakHashMap> pluginWeakReferences = new WeakHashMap<>(); + private int hash = 0; +@@ -2253,9 +2253,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + + @Override + public boolean canSee(org.bukkit.entity.Entity entity) { +- return this.equals(entity) || entity.isVisibleByDefault() ^ this.invertedVisibilityEntities.containsKey(entity.getUniqueId()); // SPIGOT-7312: Can always see self ++ return this.equals(entity) || entity.isVisibleByDefault() ^ (!invertedVisibilityEntities.isEmpty() && this.invertedVisibilityEntities.containsKey(entity.getUniqueId())); // SPIGOT-7312: Can always see self // SparklyPaper - optimize canSee checks + } + ++ // SparklyPaper - optimize canSee checks ++ // The check in ChunkMap#updatePlayer already rejects if it is the same entity, so we don't need to check it twice, especially because CraftPlayer's equals check is a bit expensive ++ public boolean canSeeChunkMapUpdatePlayer(org.bukkit.entity.Entity entity) { ++ return entity.isVisibleByDefault() ^ (!invertedVisibilityEntities.isEmpty() && this.invertedVisibilityEntities.containsKey(entity.getUniqueId())); // SPIGOT-7312: Can always see self // SparklyPaper - optimize canSee checks ++ } ++ // SparklyPaper end ++ + public boolean canSeePlayer(UUID uuid) { + org.bukkit.entity.Entity entity = this.getServer().getPlayer(uuid); + diff --git a/patches/server/0034-SparklyPaper-Skip-MapItem-update-if-the-map-does-not.patch b/patches/server/0034-SparklyPaper-Skip-MapItem-update-if-the-map-does-not.patch new file mode 100644 index 0000000..19ec6de --- /dev/null +++ b/patches/server/0034-SparklyPaper-Skip-MapItem-update-if-the-map-does-not.patch @@ -0,0 +1,49 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 19:29:22 +0400 +Subject: [PATCH] SparklyPaper Skip 'MapItem#update()' if the map does not have + + +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index d2badf33e46350aec586342e4e97b65a748c353e..fbc0835c8e3cafc678275334a378369207c9a6b1 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -49,6 +49,8 @@ public class FoldenorConfig { + + public static boolean useVirtualThreadForAsyncScheduler = false; + ++ public static boolean skipMapItemUpdatesIfNoBukkitRender = true; ++ + public static void init(File configFile) { + init(configFile, true); + } +@@ -122,6 +124,7 @@ public class FoldenorConfig { + "configuration option if the vanilla deviation is undesirable."); + piglinSpawnChancePersentInPortal = getInt("optimizations.piglin-spawn-chance-persent-in-portal", 100, + "Reduces piglin spawn in portal, by reducing change to spawn"); ++ skipMapItemUpdatesIfNoBukkitRender = getBoolean("optimizations.skip_map_item_updates_if_no_bukkit_render", skipMapItemUpdatesIfNoBukkitRender); + } + + private static void readMiscSettings() { +diff --git a/src/main/java/net/minecraft/world/item/MapItem.java b/src/main/java/net/minecraft/world/item/MapItem.java +index e96f15814986109e5e947c7b3b210be6e56ba0ed..ea5a77d1f54bc6df33a78c3231cf04a5760dac04 100644 +--- a/src/main/java/net/minecraft/world/item/MapItem.java ++++ b/src/main/java/net/minecraft/world/item/MapItem.java +@@ -6,6 +6,8 @@ import com.google.common.collect.Multiset; + import com.google.common.collect.Multisets; + import java.util.List; + import javax.annotation.Nullable; ++ ++import net.edenor.foldenor.config.FoldenorConfig; + import net.minecraft.ChatFormatting; + import net.minecraft.core.BlockPos; + import net.minecraft.core.Direction; +@@ -281,7 +283,7 @@ public class MapItem extends ComplexItem { + mapItemSavedData.tickCarriedBy(player, stack); + } + +- if (!mapItemSavedData.locked && (selected || entity instanceof Player && ((Player)entity).getOffhandItem() == stack)) { ++ if (!mapItemSavedData.locked && (!FoldenorConfig.skipMapItemUpdatesIfNoBukkitRender || mapItemSavedData.mapView.getRenderers().stream().anyMatch(mapRenderer -> mapRenderer.getClass() == org.bukkit.craftbukkit.map.CraftMapRenderer.class)) && (selected || entity instanceof Player && ((Player)entity).getOffhandItem() == stack)) { // SparklyPaper - don't update maps if they don't have the CraftMapRenderer in the render list + this.update(world, entity, mapItemSavedData); + } + } // Folia - region threading diff --git a/patches/server/0035-SparklyPaper-Skip-distanceToSqr-call-in-ServerEntity.patch b/patches/server/0035-SparklyPaper-Skip-distanceToSqr-call-in-ServerEntity.patch new file mode 100644 index 0000000..4dfe20d --- /dev/null +++ b/patches/server/0035-SparklyPaper-Skip-distanceToSqr-call-in-ServerEntity.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 19:30:28 +0400 +Subject: [PATCH] SparklyPaper-Skip-distanceToSqr-call-in-ServerEntity + + +diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java +index 38ca0acf3a1af7dcdd9e87e6d9e2e14c6d3fad41..d099ff1fce6cdd638f6a69b460e23d92daaae69c 100644 +--- a/src/main/java/net/minecraft/server/level/ServerEntity.java ++++ b/src/main/java/net/minecraft/server/level/ServerEntity.java +@@ -220,6 +220,8 @@ public class ServerEntity { + + if ((this.trackDelta || this.entity.hasImpulse || this.entity instanceof LivingEntity && ((LivingEntity) this.entity).isFallFlying()) && this.tickCount > 0) { + Vec3 vec3d1 = this.entity.getDeltaMovement(); ++ ++ if (vec3d1 != this.lastSentMovement) { // SparklyPaper start - skip distanceToSqr call in ServerEntity#sendChanges if the delta movement hasn't changed + double d0 = vec3d1.distanceToSqr(this.lastSentMovement); + + if (d0 > 1.0E-7D || d0 > 0.0D && vec3d1.lengthSqr() == 0.0D) { +@@ -234,6 +236,7 @@ public class ServerEntity { + this.broadcast.accept(new ClientboundSetEntityMotionPacket(this.entity.getId(), this.lastSentMovement)); + } + } ++ } // SparklyPaper end + } + + if (packet1 != null) { diff --git a/patches/server/0036-Leaf-Skip-event-if-no-listeners.patch b/patches/server/0036-Leaf-Skip-event-if-no-listeners.patch new file mode 100644 index 0000000..ef22947 --- /dev/null +++ b/patches/server/0036-Leaf-Skip-event-if-no-listeners.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 19:33:26 +0400 +Subject: [PATCH] Leaf-Skip-event-if-no-listeners + + +diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java +index 10895545ea073d8c3a0c09166265958c39bcf3e6..f66335e2ad467b46e757a5d598be33337d006f70 100644 +--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java ++++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java +@@ -35,15 +35,17 @@ class PaperEventManager { + + // SimplePluginManager + public void callEvent(@NotNull Event event) { ++ // Leaf start - Skip event if no listeners ++ RegisteredListener[] listeners = event.getHandlers().getRegisteredListeners(); ++ if (listeners.length == 0) return; ++ // Leaf end - Skip event if no listeners ++ + if (event.isAsynchronous() && this.server.isPrimaryThread()) { + throw new IllegalStateException(event.getEventName() + " may only be triggered asynchronously."); + } else if (!event.isAsynchronous() && !this.server.isPrimaryThread() && !this.server.isStopping()) { + throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously."); + } + +- HandlerList handlers = event.getHandlers(); +- RegisteredListener[] listeners = handlers.getRegisteredListeners(); +- + for (RegisteredListener registration : listeners) { + if (!registration.getPlugin().isEnabled()) { + continue; diff --git a/patches/server/0037-Divine-lithium-ai.raid.patch b/patches/server/0037-Divine-lithium-ai.raid.patch new file mode 100644 index 0000000..ec46b55 --- /dev/null +++ b/patches/server/0037-Divine-lithium-ai.raid.patch @@ -0,0 +1,71 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 19:37:22 +0400 +Subject: [PATCH] Divine-lithium-ai.raid + + +diff --git a/src/main/java/net/minecraft/world/entity/raid/Raid.java b/src/main/java/net/minecraft/world/entity/raid/Raid.java +index 909d1966b2f22c65ef4b887521b3b0e772f2a7ee..22d62fb2e11ba319fe04834cb2cfb7b7064d72e4 100644 +--- a/src/main/java/net/minecraft/world/entity/raid/Raid.java ++++ b/src/main/java/net/minecraft/world/entity/raid/Raid.java +@@ -107,6 +107,7 @@ public class Raid { + private Raid.RaidStatus status; + private int celebrationTicks; + private Optional waveSpawnPos; ++ private boolean isBarDirty; // DivineMC - lithium: ai.raid + // Paper start + private static final String PDC_NBT_KEY = "BukkitValues"; + private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry PDC_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry(); +@@ -299,6 +300,12 @@ public class Raid { + + public void tick() { + if (!this.isStopped()) { ++ // DivineMC start - lithium: ai.raid ++ if (this.isBarDirty) { ++ this.updateBossbarInternal(); ++ this.isBarDirty = false; ++ } ++ // DivineMC end + if (this.status == Raid.RaidStatus.ONGOING) { + boolean flag = this.active; + +@@ -675,21 +682,24 @@ public class Raid { + + } + ++ // DivineMC start - lithium: ai.raid + public void updateBossbar() { ++ this.isBarDirty = true; ++ } ++ ++ private void updateBossbarInternal() { + this.raidEvent.setProgress(Mth.clamp(this.getHealthOfLivingRaiders() / this.totalHealth, 0.0F, 1.0F)); + } ++ // DivineMC end + + public float getHealthOfLivingRaiders() { + float f = 0.0F; +- Iterator iterator = this.groupRaiderMap.values().iterator(); +- +- while (iterator.hasNext()) { +- Set set = (Set) iterator.next(); ++ for (Set raiders : this.groupRaiderMap.values()) { + + Raider entityraider; + +- for (Iterator iterator1 = set.iterator(); iterator1.hasNext(); f += entityraider.getHealth()) { +- entityraider = (Raider) iterator1.next(); ++ for (Iterator iterator1 = raiders.iterator(); iterator1.hasNext(); f += entityraider.getHealth()) { ++ entityraider = iterator1.next();; + } + } + +@@ -705,7 +715,7 @@ public class Raid { + } + + public void removeFromRaid(Raider entity, boolean countHealth) { +- Set set = (Set) this.groupRaiderMap.get(entity.getWave()); ++ Set set = this.groupRaiderMap.get(entity.getWave()); + + if (set != null) { + boolean flag1 = set.remove(entity); diff --git a/patches/server/0038-Divine-lithium-entity.fast_elytra_check-entity.fast_.patch b/patches/server/0038-Divine-lithium-entity.fast_elytra_check-entity.fast_.patch new file mode 100644 index 0000000..1893b09 --- /dev/null +++ b/patches/server/0038-Divine-lithium-entity.fast_elytra_check-entity.fast_.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 19:38:22 +0400 +Subject: [PATCH] Divine: lithium: entity.fast_elytra_check + + entity.fast_hand_swing + + +diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java +index bd2f3c8f28615b60a02ca09092e1429d5d4ae77c..8dc1b4caac03d7bf09b2bd4cb6d2634699a23c47 100644 +--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java ++++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java +@@ -2668,6 +2668,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + + protected void updateSwingTime() { ++ if (!this.swinging && this.swingTime == 0) return; // DivineMC - lithium: entity.fast_hand_swing + int i = this.getCurrentSwingDuration(); + + if (this.swinging) { +@@ -3616,6 +3617,7 @@ public abstract class LivingEntity extends Entity implements Attackable { + } + + private void updateFallFlying() { ++ if (!this.isFallFlying()) return; // DivineMC - lithium: entity.fast_elytra_check + boolean flag = this.getSharedFlag(7); + + if (flag && !this.onGround() && !this.isPassenger() && !this.hasEffect(MobEffects.LEVITATION)) { diff --git a/patches/server/0039-Purpur-Fix-outdated-server-showing-in-ping-before-se.patch b/patches/server/0039-Purpur-Fix-outdated-server-showing-in-ping-before-se.patch new file mode 100644 index 0000000..0787869 --- /dev/null +++ b/patches/server/0039-Purpur-Fix-outdated-server-showing-in-ping-before-se.patch @@ -0,0 +1,18 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 19:40:36 +0400 +Subject: [PATCH] Purpur: Fix 'outdated server' showing in ping before server + + +diff --git a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java +index 532f09089b8d6798999cf3f83e852df7479e450e..43c63d203859eaa0999937e2f9254c22510139aa 100644 +--- a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java ++++ b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java +@@ -154,6 +154,7 @@ public class ServerStatusPacketListenerImpl implements ServerStatusPacketListene + this.connection.send(new ClientboundStatusResponsePacket(ping)); + // CraftBukkit end + */ ++ if (MinecraftServer.getServer().getStatus().version().isEmpty()) return; // Purpur - do not respond to pings before we know the protocol version + com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.processRequest(MinecraftServer.getServer(), this.connection); + // Paper end + } diff --git a/patches/server/0040-Purpur-Lobotomize-stuck-villagers.patch b/patches/server/0040-Purpur-Lobotomize-stuck-villagers.patch new file mode 100644 index 0000000..71f710b --- /dev/null +++ b/patches/server/0040-Purpur-Lobotomize-stuck-villagers.patch @@ -0,0 +1,223 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 19:52:02 +0400 +Subject: [PATCH] Purpur-Lobotomize-stuck-villagers + + +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index fbc0835c8e3cafc678275334a378369207c9a6b1..4801dc90dbb3863beda1f0b89e6d30b8c0d20b0d 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -51,6 +51,12 @@ public class FoldenorConfig { + + public static boolean skipMapItemUpdatesIfNoBukkitRender = true; + ++ // Purpur Villager lobotomize ++ public static boolean villagerLobotomizeEnabled = false; ++ public static boolean villagerLobotomizeWaitUntilTradeLocked = false; ++ public static int villagerLobotomizeCheckInterval = 100; ++ // Purpur Villager lobotomize ++ + public static void init(File configFile) { + init(configFile, true); + } +@@ -125,6 +131,10 @@ public class FoldenorConfig { + piglinSpawnChancePersentInPortal = getInt("optimizations.piglin-spawn-chance-persent-in-portal", 100, + "Reduces piglin spawn in portal, by reducing change to spawn"); + skipMapItemUpdatesIfNoBukkitRender = getBoolean("optimizations.skip_map_item_updates_if_no_bukkit_render", skipMapItemUpdatesIfNoBukkitRender); ++ ++ villagerLobotomizeEnabled = getBoolean("optimizations.villager-lobotomize.enabled", villagerLobotomizeEnabled); ++ villagerLobotomizeWaitUntilTradeLocked = getBoolean("optimizations.villager-lobotomize.wait-until-trade-locked", villagerLobotomizeWaitUntilTradeLocked); ++ villagerLobotomizeCheckInterval = getInt("optimizations.villager-lobotomize.check-interval", villagerLobotomizeCheckInterval); + } + + private static void readMiscSettings() { +diff --git a/src/main/java/net/minecraft/world/entity/npc/Villager.java b/src/main/java/net/minecraft/world/entity/npc/Villager.java +index 3f4e4b612e127e51b240bca8776f6fc3743bd2d7..c3a470220b4be29896deb56c4eadd33395047507 100644 +--- a/src/main/java/net/minecraft/world/entity/npc/Villager.java ++++ b/src/main/java/net/minecraft/world/entity/npc/Villager.java +@@ -18,6 +18,8 @@ import java.util.Set; + import java.util.function.BiPredicate; + import java.util.stream.Collectors; + import javax.annotation.Nullable; ++ ++import net.edenor.foldenor.config.FoldenorConfig; + import net.minecraft.core.BlockPos; + import net.minecraft.core.GlobalPos; + import net.minecraft.core.Holder; +@@ -143,6 +145,10 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + return holder.is(PoiTypes.MEETING); + }); + ++ private boolean isLobotomized = false; ++ public boolean isLobotomized() { return this.isLobotomized; } // Purpur ++ private int notLobotomizedCount = 0; // Purpur ++ + public long nextGolemPanic = -1; // Pufferfish + + public Villager(EntityType entityType, Level world) { +@@ -158,6 +164,48 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + this.setVillagerData(this.getVillagerData().setType(type).setProfession(VillagerProfession.NONE)); + } + ++ private boolean checkLobotomized() { ++ int interval = FoldenorConfig.villagerLobotomizeCheckInterval; ++ boolean shouldCheckForTradeLocked = FoldenorConfig.villagerLobotomizeWaitUntilTradeLocked; ++ if (this.notLobotomizedCount > 3) { ++ // check half as often if not lobotomized for the last 3+ consecutive checks ++ interval *= 2; ++ } ++ if (this.level().getGameTime() % interval == 0) { ++ // offset Y for short blocks like dirt_path/farmland ++ this.isLobotomized = !(shouldCheckForTradeLocked && this.getVillagerXp() == 0) && !canTravelFrom(BlockPos.containing(this.position().x, this.getBoundingBox().minY + 0.0625D, this.position().z)); ++ ++ if (this.isLobotomized) { ++ this.notLobotomizedCount = 0; ++ } else { ++ this.notLobotomizedCount++; ++ } ++ } ++ return this.isLobotomized; ++ } ++ ++ private boolean canTravelFrom(BlockPos pos) { ++ return canTravelTo(pos.east()) || canTravelTo(pos.west()) || canTravelTo(pos.north()) || canTravelTo(pos.south()); ++ } ++ ++ private boolean canTravelTo(BlockPos pos) { ++ net.minecraft.world.level.block.state.BlockState state = this.level().getBlockStateIfLoaded(pos); ++ if (state == null) { ++ // chunk not loaded ++ return false; ++ } ++ net.minecraft.world.level.block.Block bottom = state.getBlock(); ++ if (bottom instanceof net.minecraft.world.level.block.FenceBlock || ++ bottom instanceof net.minecraft.world.level.block.FenceGateBlock || ++ bottom instanceof net.minecraft.world.level.block.WallBlock) { ++ // bottom block is too tall to get over ++ return false; ++ } ++ net.minecraft.world.level.block.Block top = level().getBlockState(pos.above()).getBlock(); ++ // only if both blocks have no collision ++ return !bottom.hasCollision && !top.hasCollision; ++ } ++ + @Override + public Brain getBrain() { + return (Brain) super.getBrain(); // CraftBukkit - decompile error +@@ -255,13 +303,19 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + // Paper start + this.customServerAiStep(false); + } +- protected void customServerAiStep(final boolean inactive) { ++ protected void customServerAiStep(boolean inactive) { + // Paper end + this.level().getProfiler().push("villagerBrain"); + // Pufferfish start ++ if (FoldenorConfig.villagerLobotomizeEnabled) { ++ // treat as inactive if lobotomized ++ inactive = inactive || checkLobotomized(); ++ } else { ++ this.isLobotomized = false; ++ } + if (!inactive && this.behaviorTick++ % this.activatedPriority == 0) { + this.getBrain().tick((ServerLevel) this.level(), this); // Paper +- } ++ } else if (this.isLobotomized && shouldRestock()) restock(); + // Pufferfish end + this.level().getProfiler().pop(); + if (this.assignProfessionWhenSpawned) { +@@ -379,14 +433,9 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + + private void resetSpecialPrices() { + if (!this.level().isClientSide()) { +- Iterator iterator = this.getOffers().iterator(); +- +- while (iterator.hasNext()) { +- MerchantOffer merchantrecipe = (MerchantOffer) iterator.next(); +- ++ for (MerchantOffer merchantrecipe : this.getOffers()) { + merchantrecipe.resetSpecialPriceDiff(); + } +- + } + } + +@@ -402,11 +451,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + + public void restock() { + this.updateDemand(); +- Iterator iterator = this.getOffers().iterator(); +- +- while (iterator.hasNext()) { +- MerchantOffer merchantrecipe = (MerchantOffer) iterator.next(); +- ++ for (MerchantOffer merchantrecipe : this.getOffers()) { + // CraftBukkit start + VillagerReplenishTradeEvent event = new VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), merchantrecipe.asBukkit()); + Bukkit.getPluginManager().callEvent(event); +@@ -477,11 +522,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + int i = 2 - this.numberOfRestocksToday; + + if (i > 0) { +- Iterator iterator = this.getOffers().iterator(); +- +- while (iterator.hasNext()) { +- MerchantOffer merchantrecipe = (MerchantOffer) iterator.next(); +- ++ for (MerchantOffer merchantrecipe : this.getOffers()) { + // CraftBukkit start + VillagerReplenishTradeEvent event = new VillagerReplenishTradeEvent((org.bukkit.entity.Villager) this.getBukkitEntity(), merchantrecipe.asBukkit()); + Bukkit.getPluginManager().callEvent(event); +@@ -500,11 +541,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + } + + private void updateDemand() { +- Iterator iterator = this.getOffers().iterator(); +- +- while (iterator.hasNext()) { +- MerchantOffer merchantrecipe = (MerchantOffer) iterator.next(); +- ++ for (MerchantOffer merchantrecipe : this.getOffers()) { + merchantrecipe.updateDemand(); + } + +@@ -514,10 +551,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + int i = this.getPlayerReputation(player); + + if (i != 0) { +- Iterator iterator = this.getOffers().iterator(); +- +- while (iterator.hasNext()) { +- MerchantOffer merchantrecipe = (MerchantOffer) iterator.next(); ++ for (MerchantOffer merchantrecipe : this.getOffers()) { + if (merchantrecipe.ignoreDiscounts) continue; // Paper - Add ignore discounts API + + merchantrecipe.addToSpecialPriceDiff(-Mth.floor((float) i * merchantrecipe.getPriceMultiplier())); +@@ -527,10 +561,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + if (player.hasEffect(MobEffects.HERO_OF_THE_VILLAGE)) { + MobEffectInstance mobeffect = player.getEffect(MobEffects.HERO_OF_THE_VILLAGE); + int j = mobeffect.getAmplifier(); +- Iterator iterator1 = this.getOffers().iterator(); +- +- while (iterator1.hasNext()) { +- MerchantOffer merchantrecipe1 = (MerchantOffer) iterator1.next(); ++ for (MerchantOffer merchantrecipe1 : this.getOffers()) { + if (merchantrecipe1.ignoreDiscounts) continue; // Paper - Add ignore discounts API + double d0 = 0.3D + 0.0625D * (double) j; + int k = (int) Math.floor(d0 * (double) merchantrecipe1.getBaseCostA().getCount()); +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java +index 46c4a33c4cb82049aa60fc7fc47707dcbd8c733e..186fea0b916103e4f5220be7e3a05ac0a82e6b52 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java +@@ -382,4 +382,10 @@ public class CraftVillager extends CraftAbstractVillager implements Villager { + getHandle().getGossips().gossips.clear(); + } + // Paper end ++ ++ // Purpur start ++ public boolean isLobotomized() { ++ return getHandle().isLobotomized(); ++ } ++ // Purpur end + } diff --git a/patches/server/0041-Add-linear-format-from-LinearPurpur.patch b/patches/server/0041-Add-linear-format-from-LinearPurpur.patch new file mode 100644 index 0000000..c47de87 --- /dev/null +++ b/patches/server/0041-Add-linear-format-from-LinearPurpur.patch @@ -0,0 +1,415 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: AltronMaxX +Date: Tue, 6 Aug 2024 20:16:41 +0400 +Subject: [PATCH] Add linear format from LinearPurpur + + +diff --git a/build.gradle.kts b/build.gradle.kts +index 0a9bcd472022cd7ea57110dd24928d5be021d649..d9e0ebc78b67c2c35d3db5c24fb67785b2a24c19 100644 +--- a/build.gradle.kts ++++ b/build.gradle.kts +@@ -30,6 +30,10 @@ dependencies { + alsoShade(log4jPlugins.output) + implementation("io.netty:netty-codec-haproxy:4.1.97.Final") // Paper - Add support for proxy protocol + // Paper end ++ // LinearPaper start ++ implementation("com.github.luben:zstd-jni:1.5.6-3") ++ implementation("org.lz4:lz4-java:1.8.0") ++ // LinearPaper end + implementation("org.apache.logging.log4j:log4j-iostreams:2.22.1") // Paper - remove exclusion + implementation("org.ow2.asm:asm-commons:9.7") + implementation("org.spongepowered:configurate-yaml:4.2.0-SNAPSHOT") // Paper - config files +diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java +index 73df26b27146bbad2106d57b22dd3c792ed3dd1d..d2f209dd61f4412478a4b8fbe3489787d30bf74b 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java ++++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/ChunkSystemRegionFileStorage.java +@@ -7,8 +7,8 @@ public interface ChunkSystemRegionFileStorage { + + public boolean moonrise$doesRegionFileNotExistNoIO(final int chunkX, final int chunkZ); + +- public RegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ); ++ public org.stupidcraft.linearpaper.region.IRegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ); // LinearPaper // Leaf + +- public RegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException; ++ public org.stupidcraft.linearpaper.region.IRegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException; // LinearPaper // Leaf + + } +diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java +index 3218cbf84f54daf06e84442d5eb1a36d8da6b215..ec9b27177dc526510e86d85f48f167b44c01ac62 100644 +--- a/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java ++++ b/src/main/java/ca/spottedleaf/moonrise/patches/chunk_system/io/RegionFileIOThread.java +@@ -1043,9 +1043,9 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread { + return ((ChunkSystemRegionFileStorage)(Object)this.getCache()).moonrise$doesRegionFileNotExistNoIO(chunkX, chunkZ); + } + +- public T computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function function) { ++ public T computeForRegionFile(final int chunkX, final int chunkZ, final boolean existingOnly, final Function function) { // LinearPaper + final RegionFileStorage cache = this.getCache(); +- final RegionFile regionFile; ++ final org.stupidcraft.linearpaper.region.IRegionFile regionFile; // LinearPaper + synchronized (cache) { + try { + if (existingOnly) { +@@ -1061,9 +1061,9 @@ public final class RegionFileIOThread extends PrioritisedQueueExecutorThread { + } + } + +- public T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function function) { ++ public T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function function) { // LinearPaper + final RegionFileStorage cache = this.getCache(); +- final RegionFile regionFile; ++ final org.stupidcraft.linearpaper.region.IRegionFile regionFile; // LinearPaper + + synchronized (cache) { + regionFile = ((ChunkSystemRegionFileStorage)(Object)cache).moonrise$getRegionFileIfLoaded(chunkX, chunkZ); +diff --git a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +index 4801dc90dbb3863beda1f0b89e6d30b8c0d20b0d..89ba3bdd1aa5612fece2c8df807963fc056e2ee8 100644 +--- a/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java ++++ b/src/main/java/net/edenor/foldenor/config/FoldenorConfig.java +@@ -2,6 +2,7 @@ package net.edenor.foldenor.config; + + import com.google.common.base.Throwables; + import com.google.common.collect.ImmutableMap; ++import com.mojang.logging.LogUtils; + import net.minecraft.core.registries.BuiltInRegistries; + import net.minecraft.server.MinecraftServer; + import net.minecraft.world.entity.EntityType; +@@ -9,6 +10,8 @@ import org.bukkit.Bukkit; + import org.bukkit.configuration.ConfigurationSection; + import org.bukkit.configuration.InvalidConfigurationException; + import org.bukkit.configuration.file.YamlConfiguration; ++import org.slf4j.Logger; ++import org.stupidcraft.linearpaper.region.EnumRegionFileExtension; + + import java.io.File; + import java.io.IOException; +@@ -19,10 +22,12 @@ import java.util.Collections; + import java.util.List; + import java.util.Map; + import java.util.logging.Level; ++ + public class FoldenorConfig { + protected static final String HEADER = "This is the main configuration file for Foldenor."; + protected static File CONFIG_FILE; + public static YamlConfiguration config; ++ private static final Logger logger = LogUtils.getLogger(); + + public static int version; + static boolean verbose; +@@ -57,6 +62,12 @@ public class FoldenorConfig { + public static int villagerLobotomizeCheckInterval = 100; + // Purpur Villager lobotomize + ++ public static String regionFormatTypeName = "MCA"; ++ public static int linearCompressionLevel = 1; ++ public static boolean throwOnUnknownExtension = false; ++ public static int linearFlushFrequency = 5; ++ public static EnumRegionFileExtension regionFormatType; ++ + public static void init(File configFile) { + init(configFile, true); + } +@@ -103,6 +114,8 @@ public class FoldenorConfig { + + readMiscSettings(); + ++ readRegionFormatSettings(); ++ + try { + readDynamicActivationOfBrains(); + } catch (IOException e) { +@@ -142,6 +155,25 @@ public class FoldenorConfig { + "Use the new Virtual Thread introduced in JDK 21 for CraftAsyncScheduler."); + } + ++ private static void readRegionFormatSettings() { ++ regionFormatTypeName = getString("region-format.type", regionFormatTypeName, "Available names: MCA, LINEAR"); ++ throwOnUnknownExtension = getBoolean("region-format.throw-on-unknown-extension", throwOnUnknownExtension); ++ linearCompressionLevel = getInt("region-format.linear.compression-level", linearCompressionLevel); ++ linearFlushFrequency = getInt("region-format.linear.flush-frequency", linearFlushFrequency); ++ ++ regionFormatType = EnumRegionFileExtension.fromName(regionFormatTypeName); ++ if (regionFormatType == EnumRegionFileExtension.UNKNOWN) { ++ logger.error("Unknown region file type {} ! Falling back to MCA format.", regionFormatTypeName); ++ regionFormatType = EnumRegionFileExtension.MCA; ++ } ++ ++ if (linearCompressionLevel > 23 || linearCompressionLevel < 1) { ++ logger.error("Linear region compression level should be between 1 and 22 in config: {}", linearCompressionLevel); ++ logger.error("Falling back to compression level 1."); ++ linearCompressionLevel = 1; ++ } ++ } ++ + private static void readDynamicActivationOfBrains() throws IOException { + dearEnabled = getBoolean("dab.enabled", true); + startDistance = getInt("dab.start-distance", 12, +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 87a722ca353c5ec20320e71c745fad7b3f33f08f..31900f484c82d41253e8d2471560d2f7222de719 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -1001,10 +1001,10 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> progressMap = Reference2FloatMaps.synchronize(new Reference2FloatOpenHashMap()); + volatile Component status = Component.translatable("optimizeWorld.stage.counting"); +- static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mca$"); ++ static final Pattern REGEX = Pattern.compile("^r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.(linear | mca)$"); // LinearPaper + final DimensionDataStorage overworldDataStorage; + + public WorldUpgrader(LevelStorageSource.LevelStorageAccess session, DataFixer dataFixer, RegistryAccess dynamicRegistryManager, boolean eraseCache, boolean recreateRegionFiles) { +@@ -400,7 +400,7 @@ public class WorldUpgrader { + + private static List getAllChunkPositions(RegionStorageInfo key, Path regionDirectory) { + File[] afile = regionDirectory.toFile().listFiles((file, s) -> { +- return s.endsWith(".mca"); ++ return s.endsWith(".linear") || s.endsWith(".mca"); // LinearPaper + }); + + if (afile == null) { +@@ -420,7 +420,7 @@ public class WorldUpgrader { + List list1 = Lists.newArrayList(); + + try { +- RegionFile regionfile = new RegionFile(key, file.toPath(), regionDirectory, true); ++ org.stupidcraft.linearpaper.region.IRegionFile regionfile = org.stupidcraft.linearpaper.region.IRegionFileFactory.getAbstractRegionFile(key, file.toPath(), regionDirectory, true); // LinearPaper + + try { + for (int i1 = 0; i1 < 32; ++i1) { +@@ -483,7 +483,7 @@ public class WorldUpgrader { + + protected abstract boolean tryProcessOnePosition(T storage, ChunkPos chunkPos, ResourceKey worldKey); + +- private void onFileFinished(RegionFile regionFile) { ++ private void onFileFinished(org.stupidcraft.linearpaper.region.IRegionFile regionFile) { // LinearPaper + if (WorldUpgrader.this.recreateRegionFiles) { + if (this.previousWriteFuture != null) { + this.previousWriteFuture.join(); +@@ -508,7 +508,7 @@ public class WorldUpgrader { + } + } + +- static record FileToUpgrade(RegionFile file, List chunksToUpgrade) { ++ static record FileToUpgrade(org.stupidcraft.linearpaper.region.IRegionFile file, List chunksToUpgrade) { // LinearPaper + + } + +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java +index eb0389ad86300665b6e057bcfa1d7c068dc6c6ab..e666d2f9347b23a00ec83b3b9541431dc9bd3f25 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java +@@ -28,7 +28,7 @@ import net.minecraft.nbt.NbtIo; // Paper + import net.minecraft.world.level.ChunkPos; + import org.slf4j.Logger; + +-public class RegionFile implements AutoCloseable { ++public class RegionFile implements org.stupidcraft.linearpaper.region.IRegionFile, AutoCloseable { // LinearPaper + + private static final Logger LOGGER = LogUtils.getLogger(); + private static final int SECTOR_BYTES = 4096; +@@ -129,7 +129,7 @@ public class RegionFile implements AutoCloseable { + } + + // note: only call for CHUNK regionfiles +- boolean recalculateHeader() throws IOException { ++ public boolean recalculateHeader() throws IOException { // LinearPaper - make public + if (!this.canRecalcHeader) { + return false; + } +@@ -928,10 +928,10 @@ public class RegionFile implements AutoCloseable { + private static int getChunkIndex(int x, int z) { + return (x & 31) + (z & 31) * 32; + } +- synchronized boolean isOversized(int x, int z) { ++ public synchronized boolean isOversized(int x, int z) { // LinearPaper - make public + return this.oversized[getChunkIndex(x, z)] == 1; + } +- synchronized void setOversized(int x, int z, boolean oversized) throws IOException { ++ public synchronized void setOversized(int x, int z, boolean oversized) throws IOException { // LinearPaper - make public + final int offset = getChunkIndex(x, z); + boolean previous = this.oversized[offset] == 1; + this.oversized[offset] = (byte) (oversized ? 1 : 0); +@@ -970,7 +970,7 @@ public class RegionFile implements AutoCloseable { + return this.path.getParent().resolve(this.path.getFileName().toString().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt"); + } + +- synchronized CompoundTag getOversizedData(int x, int z) throws IOException { ++ public synchronized CompoundTag getOversizedData(int x, int z) throws IOException { // LinearPaper - make public + Path file = getOversizedFile(x, z); + try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new InflaterInputStream(Files.newInputStream(file))))) { + return NbtIo.read((java.io.DataInput) out); +diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +index 40689256711cc94a806ca1da346f4f62eda31526..88c0a8176177ef251908a82b24f571953b1fe467 100644 +--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java ++++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java +@@ -9,6 +9,8 @@ import java.io.DataOutputStream; + import java.io.IOException; + import java.nio.file.Path; + import javax.annotation.Nullable; ++ ++import net.edenor.foldenor.config.FoldenorConfig; + import net.minecraft.FileUtil; + import net.minecraft.nbt.CompoundTag; + import net.minecraft.nbt.NbtAccounter; +@@ -21,7 +23,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + + public static final String ANVIL_EXTENSION = ".mca"; + private static final int MAX_CACHE_SIZE = 256; +- public final Long2ObjectLinkedOpenHashMap regionCache = new Long2ObjectLinkedOpenHashMap(); ++ public final Long2ObjectLinkedOpenHashMap regionCache = new Long2ObjectLinkedOpenHashMap(); // LinearPaper + private final RegionStorageInfo info; + private final Path folder; + private final boolean sync; +@@ -30,7 +32,11 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + private static final int REGION_SHIFT = 5; + private static final int MAX_NON_EXISTING_CACHE = 1024 * 64; + private final it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet nonExistingRegionFiles = new it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet(MAX_NON_EXISTING_CACHE+1); ++ // Leaf start - Linear region format + private static String getRegionFileName(final int chunkX, final int chunkZ) { ++ if (FoldenorConfig.regionFormatType == org.stupidcraft.linearpaper.region.EnumRegionFileExtension.LINEAR) { ++ return "r." + (chunkX >> REGION_SHIFT) + "." + (chunkZ >> REGION_SHIFT) + ".linear"; ++ } + return "r." + (chunkX >> REGION_SHIFT) + "." + (chunkZ >> REGION_SHIFT) + ".mca"; + } + +@@ -66,15 +72,15 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + } + + @Override +- public synchronized final RegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ) { ++ public synchronized final org.stupidcraft.linearpaper.region.IRegionFile moonrise$getRegionFileIfLoaded(final int chunkX, final int chunkZ) { // LinearPaper + return this.regionCache.getAndMoveToFirst(ChunkPos.asLong(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT)); + } + + @Override +- public synchronized final RegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException { ++ public synchronized final org.stupidcraft.linearpaper.region.IRegionFile moonrise$getRegionFileIfExists(final int chunkX, final int chunkZ) throws IOException { // LinearPaper + final long key = ChunkPos.asLong(chunkX >> REGION_SHIFT, chunkZ >> REGION_SHIFT); + +- RegionFile ret = this.regionCache.getAndMoveToFirst(key); ++ org.stupidcraft.linearpaper.region.IRegionFile ret = this.regionCache.getAndMoveToFirst(key); // LinearPaper + if (ret != null) { + return ret; + } +@@ -98,7 +104,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + + FileUtil.createDirectoriesSafe(this.folder); + +- ret = new RegionFile(this.info, regionPath, this.folder, this.sync); ++ ret = org.stupidcraft.linearpaper.region.IRegionFileFactory.getAbstractRegionFile(this.info, regionPath, this.folder, this.sync); // LinearPaper + + this.regionCache.putAndMoveToFirst(key, ret); + +@@ -115,7 +121,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + @Nullable + public static ChunkPos getRegionFileCoordinates(Path file) { + String fileName = file.getFileName().toString(); +- if (!fileName.startsWith("r.") || !fileName.endsWith(".mca")) { ++ if (!fileName.startsWith("r.") || !fileName.endsWith(".mca") || !fileName.endsWith(".linear")) { // LinearPaper + return null; + } + +@@ -143,7 +149,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + this.isChunkData = isChunkDataFolder(this.folder); // Paper - recalculate region file headers + } + +- public RegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - public ++ public org.stupidcraft.linearpaper.region.IRegionFile getRegionFile(ChunkPos chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - public // LinearPaper + // Paper start - rewrite chunk system + if (existingOnly) { + return this.moonrise$getRegionFileIfExists(chunkcoordintpair.x, chunkcoordintpair.z); +@@ -151,7 +157,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + synchronized (this) { + final long key = ChunkPos.asLong(chunkcoordintpair.x >> REGION_SHIFT, chunkcoordintpair.z >> REGION_SHIFT); + +- RegionFile ret = this.regionCache.getAndMoveToFirst(key); ++ org.stupidcraft.linearpaper.region.IRegionFile ret = this.regionCache.getAndMoveToFirst(key); // LinearPaper + if (ret != null) { + return ret; + } +@@ -160,13 +166,13 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + this.regionCache.removeLast().close(); + } + +- final Path regionPath = this.folder.resolve(getRegionFileName(chunkcoordintpair.x, chunkcoordintpair.z)); ++ final Path regionPath = this.folder.resolve(getRegionFileName(chunkcoordintpair.x, chunkcoordintpair.z)); // LinearPaper + + this.createRegionFile(key); + + FileUtil.createDirectoriesSafe(this.folder); + +- ret = new RegionFile(this.info, regionPath, this.folder, this.sync); ++ ret = org.stupidcraft.linearpaper.region.IRegionFileFactory.getAbstractRegionFile(this.info, regionPath, this.folder, this.sync); // LinearPaper + + this.regionCache.putAndMoveToFirst(key, ret); + +@@ -180,7 +186,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + org.apache.logging.log4j.LogManager.getLogger().fatal(msg + " (" + file.toString().replaceAll(".+[\\\\/]", "") + " - " + x + "," + z + ") Go clean it up to remove this message. /minecraft:tp " + (x<<4)+" 128 "+(z<<4) + " - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); + } + +- private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { ++ private static CompoundTag readOversizedChunk(org.stupidcraft.linearpaper.region.IRegionFile regionfile, ChunkPos chunkCoordinate) throws IOException { // LinearPaper + synchronized (regionfile) { + try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) { + CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z); +@@ -215,7 +221,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + @Nullable + public CompoundTag read(ChunkPos pos) throws IOException { + // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing +- RegionFile regionfile = this.getRegionFile(pos, true); ++ org.stupidcraft.linearpaper.region.IRegionFile regionfile = this.getRegionFile(pos, true); // LinearPaper + if (regionfile == null) { + return null; + } +@@ -279,7 +285,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + + public void scanChunk(ChunkPos chunkPos, StreamTagVisitor scanner) throws IOException { + // CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing +- RegionFile regionfile = this.getRegionFile(chunkPos, true); ++ org.stupidcraft.linearpaper.region.IRegionFile regionfile = this.getRegionFile(chunkPos, true); // LinearPaper + if (regionfile == null) { + return; + } +@@ -309,7 +315,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + } + + public void write(ChunkPos pos, @Nullable CompoundTag nbt) throws IOException { // Paper - public +- RegionFile regionfile = this.getRegionFile(pos, nbt == null); // CraftBukkit // Paper - rewrite chunk system ++ org.stupidcraft.linearpaper.region.IRegionFile regionfile = this.getRegionFile(pos, nbt == null); // CraftBukkit // Paper - rewrite chunk system // LinearPaper + // Paper start - rewrite chunk system + if (regionfile == null) { + // if the RegionFile doesn't exist, no point in deleting from it +@@ -368,7 +374,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + // Paper start - rewrite chunk system + synchronized (this) { + final ExceptionCollector exceptionCollector = new ExceptionCollector<>(); +- for (final RegionFile regionFile : this.regionCache.values()) { ++ for (final org.stupidcraft.linearpaper.region.IRegionFile regionFile : this.regionCache.values()) { // LinearPape + try { + regionFile.close(); + } catch (final IOException ex) { +@@ -385,7 +391,7 @@ public class RegionFileStorage implements AutoCloseable, ca.spottedleaf.moonrise + // Paper start - rewrite chunk system + synchronized (this) { + final ExceptionCollector exceptionCollector = new ExceptionCollector<>(); +- for (final RegionFile regionFile : this.regionCache.values()) { ++ for (final org.stupidcraft.linearpaper.region.IRegionFile regionFile : this.regionCache.values()) { // LinearPaper + try { + regionFile.flush(); + } catch (final IOException ex) { diff --git a/patches/unused/0011-Force-kick-when-safety-kicking-failed.patch b/patches/unused/0011-Force-kick-when-safety-kicking-failed.patch deleted file mode 100644 index 0d02ea9..0000000 --- a/patches/unused/0011-Force-kick-when-safety-kicking-failed.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Sat, 16 Sep 2023 21:40:52 +0400 -Subject: [PATCH] Force-kick-when-safety-kicking-failed - - -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index fc301fcf812b79ac7ceae710132a762f101b82b2..5eeaeed63e60dfeaad81ac577b79df85506e1728 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -6,6 +6,7 @@ import com.google.common.collect.ImmutableSet; - import com.google.common.io.BaseEncoding; - import com.mojang.authlib.GameProfile; - import com.mojang.datafixers.util.Pair; -+import io.papermc.paper.adventure.PaperAdventure; - import it.unimi.dsi.fastutil.shorts.ShortArraySet; - import it.unimi.dsi.fastutil.shorts.ShortSet; - import java.io.ByteArrayOutputStream; -@@ -74,6 +75,7 @@ import net.minecraft.network.protocol.game.ClientboundStopSoundPacket; - import net.minecraft.network.protocol.game.ClientboundTabListPacket; - import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket; - import net.minecraft.resources.ResourceLocation; -+import net.minecraft.server.MinecraftServer; - import net.minecraft.server.PlayerAdvancements; - import net.minecraft.server.level.ChunkMap; - import net.minecraft.server.level.ServerLevel; -@@ -577,7 +579,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - //org.spigotmc.AsyncCatcher.catchOp("player kick"); // Folia - region threading - no longer needed - final ServerGamePacketListenerImpl connection = this.getHandle().connection; - if (connection != null) { -- connection.disconnect(message == null ? net.kyori.adventure.text.Component.empty() : message, cause); -+ MinecraftServer.LOGGER.info("Kicking Player: " + this.getName()); -+ if (connection.isDisconnected() || !connection.connection.isConnected() || !connection.connection.channel.isActive()) { -+ MinecraftServer.LOGGER.warn("Detected Player Stuck, Force Disconnecting..."); -+ connection.connection.disconnect(message == null ? PaperAdventure.asVanilla(net.kyori.adventure.text.Component.empty()) : PaperAdventure.asVanilla(message)); -+ connection.connection.channel.close(); -+ } else { -+ connection.disconnect(message == null ? net.kyori.adventure.text.Component.empty() : message, cause); -+ } - } - } - diff --git a/patches/wip/0016-Leaves-Protocol-Core.patch b/patches/wip/0016-Leaves-Protocol-Core.patch deleted file mode 100644 index a13a964..0000000 --- a/patches/wip/0016-Leaves-Protocol-Core.patch +++ /dev/null @@ -1,575 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: AltronMaxX -Date: Fri, 22 Dec 2023 19:49:27 +0400 -Subject: [PATCH] Leaves-Protocol-Core - - -diff --git a/src/main/java/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java b/src/main/java/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java -index af86f752c33a2990405fea058b7c41c437ba9d46..3b671a777944732f62e914d696759a4e506a64c1 100644 ---- a/src/main/java/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java -+++ b/src/main/java/net/minecraft/network/protocol/common/ServerboundCustomPayloadPacket.java -@@ -21,6 +21,11 @@ public record ServerboundCustomPayloadPacket(CustomPacketPayload payload) implem - private static CustomPacketPayload readPayload(ResourceLocation id, FriendlyByteBuf buf) { - FriendlyByteBuf.Reader packetdataserializer_a = (FriendlyByteBuf.Reader) ServerboundCustomPayloadPacket.KNOWN_TYPES.get(id); - -+ CustomPacketPayload leavesPayload = top.leavesmc.leaves.protocol.core.LeavesProtocolManager.getPayload(id, buf); -+ if (leavesPayload != null) { -+ return leavesPayload; -+ } -+ - return (CustomPacketPayload) (packetdataserializer_a != null ? (CustomPacketPayload) packetdataserializer_a.apply(buf) : readUnknownPayload(id, buf)); - } - -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 95d3ae694ad6f50d91a00b99f7b3e44fa1f2f1e6..2e306c0b937e740269681892fe4c0ecccc71f925 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -1851,6 +1851,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop>> KNOWN_TYPES = new HashMap<>(); -+ private static final Map> KNOW_RECEIVERS = new HashMap<>(); -+ -+ private static final List TICKERS = new ArrayList<>(); -+ private static final List PLAYER_JOIN = new ArrayList<>(); -+ private static final List PLAYER_LEAVE = new ArrayList<>(); -+ private static final List RELOAD_SERVER = new ArrayList<>(); -+ private static final Map> MINECRAFT_REGISTER = new HashMap<>(); -+ -+ public static void init() { -+ for (Class clazz : getClasses("top.leavesmc.leaves.protocol")) { -+ final LeavesProtocol protocol = clazz.getAnnotation(LeavesProtocol.class); -+ if (protocol != null) { -+ Set methods; -+ try { -+ Method[] publicMethods = clazz.getMethods(); -+ Method[] privateMethods = clazz.getDeclaredMethods(); -+ methods = new HashSet<>(publicMethods.length + privateMethods.length, 1.0f); -+ Collections.addAll(methods, publicMethods); -+ Collections.addAll(methods, privateMethods); -+ } catch (NoClassDefFoundError e) { -+ e.printStackTrace(); -+ return; -+ } -+ -+ Map> map = new HashMap<>(); -+ for (final Method method : methods) { -+ if (method.isBridge() || method.isSynthetic() || !Modifier.isStatic(method.getModifiers())) { -+ continue; -+ } -+ -+ method.setAccessible(true); -+ -+ final ProtocolHandler.Init init = method.getAnnotation(ProtocolHandler.Init.class); -+ if (init != null) { -+ try { -+ method.invoke(null); -+ } catch (InvocationTargetException | IllegalAccessException e) { -+ e.printStackTrace(); -+ } -+ continue; -+ } -+ -+ final ProtocolHandler.PayloadReceiver receiver = method.getAnnotation(ProtocolHandler.PayloadReceiver.class); -+ if (receiver != null) { -+ try { -+ map.put(receiver, receiver.payload().getConstructor(ResourceLocation.class, FriendlyByteBuf.class)); -+ } catch (NoSuchMethodException e) { -+ e.printStackTrace(); -+ continue; -+ } -+ -+ if (!KNOW_RECEIVERS.containsKey(protocol)) { -+ KNOW_RECEIVERS.put(protocol, new HashMap<>()); -+ } -+ -+ KNOW_RECEIVERS.get(protocol).put(receiver, method); -+ continue; -+ } -+ -+ final ProtocolHandler.Ticker ticker = method.getAnnotation(ProtocolHandler.Ticker.class); -+ if (ticker != null) { -+ TICKERS.add(method); -+ continue; -+ } -+ -+ final ProtocolHandler.PlayerJoin playerJoin = method.getAnnotation(ProtocolHandler.PlayerJoin.class); -+ if (playerJoin != null) { -+ PLAYER_JOIN.add(method); -+ continue; -+ } -+ -+ final ProtocolHandler.PlayerLeave playerLeave = method.getAnnotation(ProtocolHandler.PlayerLeave.class); -+ if (playerLeave != null) { -+ PLAYER_LEAVE.add(method); -+ continue; -+ } -+ -+ final ProtocolHandler.ReloadServer reloadServer = method.getAnnotation(ProtocolHandler.ReloadServer.class); -+ if (reloadServer != null) { -+ RELOAD_SERVER.add(method); -+ continue; -+ } -+ -+ final ProtocolHandler.MinecraftRegister minecraftRegister = method.getAnnotation(ProtocolHandler.MinecraftRegister.class); -+ if (minecraftRegister != null) { -+ if (!MINECRAFT_REGISTER.containsKey(protocol)) { -+ MINECRAFT_REGISTER.put(protocol, new HashMap<>()); -+ } -+ -+ MINECRAFT_REGISTER.get(protocol).put(minecraftRegister, method); -+ } -+ } -+ KNOWN_TYPES.put(protocol, map); -+ } -+ } -+ } -+ -+ public static CustomPacketPayload getPayload(ResourceLocation id, FriendlyByteBuf buf) { -+ for (LeavesProtocol protocol : KNOWN_TYPES.keySet()) { -+ if (!protocol.namespace().equals(id.getNamespace()) && !ArrayUtils.contains(protocol.namespaces(), id.getNamespace())) { -+ continue; -+ } -+ -+ Map> map = KNOWN_TYPES.get(protocol); -+ for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) { -+ if (receiver.ignoreId() || receiver.payloadId().equals(id.getPath()) || ArrayUtils.contains(receiver.payloadIds(), id.getPath())) { -+ try { -+ return map.get(receiver).newInstance(id, buf); -+ } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ } -+ return null; -+ } -+ -+ public static void handlePayload(ServerPlayer player, CustomPacketPayload payload) { -+ for (LeavesProtocol protocol : KNOW_RECEIVERS.keySet()) { -+ if (!protocol.namespace().equals(payload.id().getNamespace()) && !ArrayUtils.contains(protocol.namespaces(), payload.id().getNamespace())) { -+ continue; -+ } -+ -+ Map map = KNOW_RECEIVERS.get(protocol); -+ for (ProtocolHandler.PayloadReceiver receiver : map.keySet()) { -+ if (payload.getClass() == receiver.payload()) { -+ if (receiver.ignoreId() || receiver.payloadId().equals(payload.id().getPath()) || -+ ArrayUtils.contains(receiver.payloadIds(), payload.id().getPath())) { -+ try { -+ map.get(receiver).invoke(null, player, payload); -+ } catch (InvocationTargetException | IllegalAccessException e) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ } -+ } -+ } -+ -+ public static void handleTick() { -+ if (!TICKERS.isEmpty()) { -+ try { -+ for (Method method : TICKERS) { -+ method.invoke(null); -+ } -+ } catch (InvocationTargetException | IllegalAccessException e) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ -+ public static void handlePlayerJoin(ServerPlayer player) { -+ if (!PLAYER_JOIN.isEmpty()) { -+ try { -+ for (Method method : PLAYER_JOIN) { -+ method.invoke(null, player); -+ } -+ } catch (InvocationTargetException | IllegalAccessException e) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ -+ public static void handlePlayerLeave(ServerPlayer player) { -+ if (!PLAYER_LEAVE.isEmpty()) { -+ try { -+ for (Method method : PLAYER_LEAVE) { -+ method.invoke(null, player); -+ } -+ } catch (InvocationTargetException | IllegalAccessException e) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ -+ public static void handleServerReload() { -+ if (!RELOAD_SERVER.isEmpty()) { -+ try { -+ for (Method method : RELOAD_SERVER) { -+ method.invoke(null); -+ } -+ } catch (InvocationTargetException | IllegalAccessException e) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ -+ public static void handleMinecraftRegister(String channelId, ServerPlayer player) { -+ for (LeavesProtocol protocol : MINECRAFT_REGISTER.keySet()) { -+ String[] channel = channelId.split(":"); -+ if (!protocol.namespace().equals(channel[0]) && !ArrayUtils.contains(protocol.namespaces(), channel[0])) { -+ continue; -+ } -+ -+ Map map = MINECRAFT_REGISTER.get(protocol); -+ for (ProtocolHandler.MinecraftRegister register : map.keySet()) { -+ if (register.ignoreId() || register.channelId().equals(channel[1]) || -+ ArrayUtils.contains(register.channelIds(), channel[1])) { -+ try { -+ map.get(register).invoke(null, player); -+ } catch (InvocationTargetException | IllegalAccessException e) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ } -+ } -+ -+ public static 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) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ } catch (IOException e) { -+ e.printStackTrace(); -+ } -+ return classes; -+ } -+ -+ 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) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ } -+ } -+ -+ private static void findClassesInPackageByJar(String packageName, Enumeration entries, String packageDirName, Set> classes) { -+ while (entries.hasMoreElements()) { -+ JarEntry entry = entries.nextElement(); -+ String name = entry.getName(); -+ if (name.charAt(0) == '/') { -+ name = name.substring(1); -+ } -+ if (name.startsWith(packageDirName)) { -+ int idx = name.lastIndexOf('/'); -+ 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) { -+ e.printStackTrace(); -+ } -+ } -+ } -+ } -+ } -+ -+ public record EmptyPayload(ResourceLocation id) implements CustomPacketPayload { -+ -+ public EmptyPayload(ResourceLocation location, FriendlyByteBuf buf) { -+ this(location); -+ } -+ -+ @Override -+ public void write(@NotNull FriendlyByteBuf buf) { -+ } -+ } -+ -+ public record LeavesPayload(FriendlyByteBuf data, ResourceLocation id) implements CustomPacketPayload { -+ -+ public LeavesPayload(ResourceLocation location, FriendlyByteBuf buf) { -+ this(new FriendlyByteBuf(buf.readBytes(buf.readableBytes())), location); -+ } -+ -+ @Override -+ public void write(FriendlyByteBuf buf) { -+ buf.writeBytes(data); -+ } -+ } -+} -diff --git a/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolHandler.java b/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolHandler.java -new file mode 100644 -index 0000000000000000000000000000000000000000..d696f001d2576d1b61cc732c81f22eb52205072b ---- /dev/null -+++ b/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolHandler.java -@@ -0,0 +1,65 @@ -+package top.leavesmc.leaves.protocol.core; -+ -+import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -+ -+import java.lang.annotation.ElementType; -+import java.lang.annotation.Retention; -+import java.lang.annotation.RetentionPolicy; -+import java.lang.annotation.Target; -+ -+public class ProtocolHandler { -+ -+ @Target(ElementType.METHOD) -+ @Retention(RetentionPolicy.RUNTIME) -+ public @interface Init { -+ -+ } -+ -+ @Target(ElementType.METHOD) -+ @Retention(RetentionPolicy.RUNTIME) -+ public @interface PayloadReceiver { -+ -+ Class payload(); -+ -+ String[] payloadIds() default {}; -+ -+ String payloadId() default ""; -+ -+ boolean ignoreId() default false; -+ } -+ -+ @Target(ElementType.METHOD) -+ @Retention(RetentionPolicy.RUNTIME) -+ public @interface Ticker { -+ int delay() default 0; -+ } -+ -+ @Target(ElementType.METHOD) -+ @Retention(RetentionPolicy.RUNTIME) -+ public @interface PlayerJoin { -+ -+ } -+ -+ @Target(ElementType.METHOD) -+ @Retention(RetentionPolicy.RUNTIME) -+ public @interface PlayerLeave { -+ -+ } -+ -+ @Target(ElementType.METHOD) -+ @Retention(RetentionPolicy.RUNTIME) -+ public @interface ReloadServer { -+ -+ } -+ -+ @Target(ElementType.METHOD) -+ @Retention(RetentionPolicy.RUNTIME) -+ public @interface MinecraftRegister { -+ -+ String channelId() default ""; -+ -+ String[] channelIds() default {}; -+ -+ boolean ignoreId() default false; -+ } -+} -diff --git a/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolUtils.java b/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolUtils.java -new file mode 100644 -index 0000000000000000000000000000000000000000..5282c5ad3d26d06ab685ddaaf6fd9a4d49559717 ---- /dev/null -+++ b/src/main/java/top/leavesmc/leaves/protocol/core/ProtocolUtils.java -@@ -0,0 +1,36 @@ -+package top.leavesmc.leaves.protocol.core; -+ -+import net.minecraft.network.FriendlyByteBuf; -+import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; -+import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -+import net.minecraft.resources.ResourceLocation; -+import net.minecraft.server.level.ServerPlayer; -+import org.jetbrains.annotations.NotNull; -+ -+import java.util.function.Consumer; -+ -+public class ProtocolUtils { -+ -+ public static void sendEmptyPayloadPacket(ServerPlayer player, ResourceLocation id) { -+ player.connection.send(new ClientboundCustomPayloadPacket(new LeavesProtocolManager.EmptyPayload(id))); -+ } -+ -+ public static void sendPayloadPacket(ServerPlayer player, ResourceLocation id, Consumer consumer) { -+ player.connection.send(new ClientboundCustomPayloadPacket(new CustomPacketPayload() { -+ @Override -+ public void write(@NotNull FriendlyByteBuf buf) { -+ consumer.accept(buf); -+ } -+ -+ @Override -+ @NotNull -+ public ResourceLocation id() { -+ return id; -+ } -+ })); -+ } -+ -+ public static void sendPayloadPacket(ServerPlayer player, CustomPacketPayload payload) { -+ player.connection.send(new ClientboundCustomPayloadPacket(payload)); -+ } -+}