p a t c h e s
This commit is contained in:
parent
e208af9741
commit
0358549f7b
19 changed files with 263 additions and 286 deletions
|
@ -1950,13 +1950,15 @@ index 0000000000000000000000000000000000000000..d0c77068e9a53d1b8bbad0f3f6b420d6
|
|||
+}
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java b/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..9df0006c1a283f77c4d01d9fce9062fc1c9bbb1f
|
||||
index 0000000000000000000000000000000000000000..66f6423d2732d23809fe86418537e35d40d24373
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/util/set/OptimizedSmallEnumSet.java
|
||||
@@ -0,0 +1,67 @@
|
||||
@@ -0,0 +1,92 @@
|
||||
+package com.destroystokyo.paper.util.set;
|
||||
+
|
||||
+import java.util.Collection;
|
||||
+import java.util.function.Consumer;
|
||||
+import java.util.function.Predicate;
|
||||
+
|
||||
+/**
|
||||
+ * @author Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
|
@ -2020,6 +2022,29 @@ index 0000000000000000000000000000000000000000..9df0006c1a283f77c4d01d9fce9062fc
|
|||
+ public boolean hasCommonElements(final OptimizedSmallEnumSet<E> other) {
|
||||
+ return (other.backingSet & this.backingSet) != 0;
|
||||
+ }
|
||||
+
|
||||
+ public void forEach(final E[] values, final Consumer<E> action) {
|
||||
+ long iterator = this.getBackingSet();
|
||||
+ int wrappedGoalSize = this.size();
|
||||
+ for (int i = 0; i < wrappedGoalSize; ++i) {
|
||||
+ final E type = values[Long.numberOfTrailingZeros(iterator)];
|
||||
+ iterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(iterator);
|
||||
+ action.accept(type);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public boolean anyMatch(final E[] values, final Predicate<E> predicate) {
|
||||
+ long iterator = this.getBackingSet();
|
||||
+ int wrappedGoalSize = this.size();
|
||||
+ for (int i = 0; i < wrappedGoalSize; ++i) {
|
||||
+ final E type = values[Long.numberOfTrailingZeros(iterator)];
|
||||
+ iterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(iterator);
|
||||
+ if (predicate.test(type)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/chunk/SingleThreadChunkRegionManager.java b/src/main/java/io/papermc/paper/chunk/SingleThreadChunkRegionManager.java
|
||||
new file mode 100644
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <spottedleaf@spottedleaf.dev>
|
||||
Date: Mon, 6 Apr 2020 17:53:29 -0700
|
||||
Subject: [PATCH] Optimize GoalSelector Goal.Flag Set operations
|
||||
|
||||
Optimise the stream.anyMatch statement to move to a bitset
|
||||
where we can replace the call with a single bitwise operation.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
|
||||
index 6667ecc4b7eded4e20a415cef1e1b1179e6710b8..4379b9948f1eecfe6fd7dea98e298ad5f761019a 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
|
||||
@@ -4,7 +4,8 @@ import java.util.EnumSet;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
public abstract class Goal {
|
||||
- private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class);
|
||||
+ private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
|
||||
+ private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
|
||||
|
||||
public abstract boolean canUse();
|
||||
|
||||
@@ -30,8 +31,10 @@ public abstract class Goal {
|
||||
}
|
||||
|
||||
public void setFlags(EnumSet<Goal.Flag> controls) {
|
||||
- this.flags.clear();
|
||||
- this.flags.addAll(controls);
|
||||
+ // Paper start - remove streams from pathfindergoalselector
|
||||
+ this.goalTypes.clear();
|
||||
+ this.goalTypes.addAllUnchecked(controls);
|
||||
+ // Paper end - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,8 +42,10 @@ public abstract class Goal {
|
||||
return this.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
- public EnumSet<Goal.Flag> getFlags() {
|
||||
- return this.flags;
|
||||
+ // Paper start - remove streams from pathfindergoalselector
|
||||
+ public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
|
||||
+ return this.goalTypes;
|
||||
+ // Paper end - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
protected int adjustedTickDelay(int ticks) {
|
||||
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 2bb32378b19a21c94ff3ec8ed32fc9d6f0ad0fdb..ad82395c46943e91e7f65d8d67824cc0c965362b 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
|
||||
@@ -30,10 +30,12 @@ public class GoalSelector {
|
||||
private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
|
||||
public final Set<WrappedGoal> availableGoals = Sets.newLinkedHashSet();
|
||||
private final Supplier<ProfilerFiller> profiler;
|
||||
- private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
|
||||
+ private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
|
||||
+ private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
|
||||
private int tickCount;
|
||||
private int newGoalRate = 3;
|
||||
private int curRate;
|
||||
+ private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from pathfindergoalselector
|
||||
|
||||
public GoalSelector(Supplier<ProfilerFiller> profiler) {
|
||||
this.profiler = profiler;
|
||||
@@ -63,32 +65,33 @@ public class GoalSelector {
|
||||
}
|
||||
// Paper end
|
||||
public void removeGoal(Goal goal) {
|
||||
- this.availableGoals.stream().filter((wrappedGoal) -> {
|
||||
- return wrappedGoal.getGoal() == goal;
|
||||
- }).filter(WrappedGoal::isRunning).forEach(WrappedGoal::stop);
|
||||
- this.availableGoals.removeIf((wrappedGoal) -> {
|
||||
- return wrappedGoal.getGoal() == goal;
|
||||
- });
|
||||
- }
|
||||
-
|
||||
- private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> controls) {
|
||||
- for(Goal.Flag flag : goal.getFlags()) {
|
||||
- if (controls.contains(flag)) {
|
||||
- return true;
|
||||
+ // Paper start - remove streams from pathfindergoalselector
|
||||
+ for (java.util.Iterator<WrappedGoal> iterator = this.availableGoals.iterator(); iterator.hasNext();) {
|
||||
+ WrappedGoal goalWrapped = iterator.next();
|
||||
+ if (goalWrapped.getGoal() != goal) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (goalWrapped.isRunning()) {
|
||||
+ goalWrapped.stop();
|
||||
}
|
||||
+ iterator.remove();
|
||||
}
|
||||
+ // Paper end - remove streams from pathfindergoalselector
|
||||
+ }
|
||||
|
||||
- return false;
|
||||
+ private static boolean goalContainsAnyFlags(WrappedGoal goal, com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> controls) {
|
||||
+ return goal.getFlags().hasCommonElements(controls); // Paper
|
||||
}
|
||||
|
||||
private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map<Goal.Flag, WrappedGoal> goalsByControl) {
|
||||
- for(Goal.Flag flag : goal.getFlags()) {
|
||||
+ // Paper start
|
||||
+ return !goal.getFlags().anyMatch(GOAL_FLAG_VALUES, flag -> {
|
||||
if (!goalsByControl.getOrDefault(flag, NO_GOAL).canBeReplacedBy(goal)) {
|
||||
- return false;
|
||||
+ return true;
|
||||
}
|
||||
- }
|
||||
-
|
||||
- return true;
|
||||
+ return false;
|
||||
+ });
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
@@ -96,7 +99,7 @@ public class GoalSelector {
|
||||
profilerFiller.push("goalCleanup");
|
||||
|
||||
for(WrappedGoal wrappedGoal : this.availableGoals) {
|
||||
- if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.disabledFlags) || !wrappedGoal.canContinueToUse())) {
|
||||
+ if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) {
|
||||
wrappedGoal.stop();
|
||||
}
|
||||
}
|
||||
@@ -114,12 +117,14 @@ public class GoalSelector {
|
||||
profilerFiller.push("goalUpdate");
|
||||
|
||||
for(WrappedGoal wrappedGoal2 : this.availableGoals) {
|
||||
- if (!wrappedGoal2.isRunning() && !goalContainsAnyFlags(wrappedGoal2, this.disabledFlags) && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) && wrappedGoal2.canUse()) {
|
||||
- for(Goal.Flag flag : wrappedGoal2.getFlags()) {
|
||||
+ // Paper start
|
||||
+ if (!wrappedGoal2.isRunning() && !goalContainsAnyFlags(wrappedGoal2, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) && wrappedGoal2.canUse()) {
|
||||
+ wrappedGoal2.getFlags().forEach(GOAL_FLAG_VALUES, flag -> {
|
||||
+ // Paper end
|
||||
WrappedGoal wrappedGoal3 = this.lockedFlags.getOrDefault(flag, NO_GOAL);
|
||||
wrappedGoal3.stop();
|
||||
this.lockedFlags.put(flag, wrappedGoal2);
|
||||
- }
|
||||
+ }); // Paper
|
||||
|
||||
wrappedGoal2.start();
|
||||
}
|
||||
@@ -155,11 +160,11 @@ public class GoalSelector {
|
||||
}
|
||||
|
||||
public void disableControlFlag(Goal.Flag control) {
|
||||
- this.disabledFlags.add(control);
|
||||
+ this.goalTypes.addUnchecked(control); // Paper - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
public void enableControlFlag(Goal.Flag control) {
|
||||
- this.disabledFlags.remove(control);
|
||||
+ this.goalTypes.removeUnchecked(control); // Paper - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
public void setControlFlag(Goal.Flag control, boolean enabled) {
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
|
||||
index 6665ce5f48316e626907e6937d5ef1bc398a7ebd..51deb4455cac055ffa455e4f34aa30858d2fb448 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
|
||||
@@ -69,8 +69,10 @@ public class WrappedGoal extends Goal {
|
||||
}
|
||||
|
||||
@Override
|
||||
- public EnumSet<Goal.Flag> getFlags() {
|
||||
+ // Paper start - remove streams from pathfindergoalselector
|
||||
+ public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
|
||||
return this.goal.getFlags();
|
||||
+ // Paper end - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
583
patches/server/0392-Improved-Watchdog-Support.patch
Normal file
583
patches/server/0392-Improved-Watchdog-Support.patch
Normal file
|
@ -0,0 +1,583 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Sun, 12 Apr 2020 15:50:48 -0400
|
||||
Subject: [PATCH] Improved Watchdog Support
|
||||
|
||||
Forced Watchdog Crash support and Improve Async Shutdown
|
||||
|
||||
If the request to shut down the server is received while we are in
|
||||
a watchdog hang, immediately treat it as a crash and begin the shutdown
|
||||
process. Shutdown process is now improved to also shutdown cleanly when
|
||||
not using restart scripts either.
|
||||
|
||||
If a server is deadlocked, a server owner can send SIGUP (or any other signal
|
||||
the JVM understands to shut down as it currently does) and the watchdog
|
||||
will no longer need to wait until the full timeout, allowing you to trigger
|
||||
a close process and try to shut the server down gracefully, saving player and
|
||||
world data.
|
||||
|
||||
Previously there was no way to trigger this outside of waiting for a full watchdog
|
||||
timeout, which may be set to a really long time...
|
||||
|
||||
Additionally, fix everything to do with shutting the server down asynchronously.
|
||||
|
||||
Previously, nearly everything about the process was fragile and unsafe. Main might
|
||||
not have actually been frozen, and might still be manipulating state.
|
||||
|
||||
Or, some reuest might ask main to do something in the shutdown but main is dead.
|
||||
|
||||
Or worse, other things might start closing down items such as the Console or Thread Pool
|
||||
before we are fully shutdown.
|
||||
|
||||
This change tries to resolve all of these issues by moving everything into the stop
|
||||
method and guaranteeing only one thread is stopping the server.
|
||||
|
||||
We then issue Thread Death to the main thread of another thread initiates the stop process.
|
||||
We have to ensure Thread Death propagates correctly though to stop main completely.
|
||||
|
||||
This is to ensure that if main isn't truely stuck, it's not manipulating state we are trying to save.
|
||||
|
||||
This also moves all plugins who register "delayed init" tasks to occur just before "Done" so they
|
||||
are properly accounted for and wont trip watchdog on init.
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java
|
||||
index e3b74dbdf8e14219a56fab939f3174e0c2f66de6..218f5bafeed8551b55b91c7fccaf6935c8b631ca 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/Metrics.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/Metrics.java
|
||||
@@ -92,7 +92,12 @@ public class Metrics {
|
||||
* Starts the Scheduler which submits our data every 30 minutes.
|
||||
*/
|
||||
private void startSubmitting() {
|
||||
- final Runnable submitTask = this::submitData;
|
||||
+ final Runnable submitTask = () -> {
|
||||
+ if (MinecraftServer.getServer().hasStopped()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ submitData();
|
||||
+ };
|
||||
|
||||
// Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution of requests on the
|
||||
// bStats backend. To circumvent this problem, we introduce some randomness into the initial and second delay.
|
||||
diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java
|
||||
index e3b605695e3b837246f72ccb364af06ea48bda45..62c3c597732e6fb30ed5367d902ea8763507a6b8 100644
|
||||
--- a/src/main/java/net/minecraft/CrashReport.java
|
||||
+++ b/src/main/java/net/minecraft/CrashReport.java
|
||||
@@ -232,6 +232,7 @@ public class CrashReport {
|
||||
}
|
||||
|
||||
public static CrashReport forThrowable(Throwable cause, String title) {
|
||||
+ if (cause instanceof ThreadDeath) com.destroystokyo.paper.util.SneakyThrow.sneaky(cause); // Paper
|
||||
while (cause instanceof CompletionException && cause.getCause() != null) {
|
||||
cause = cause.getCause();
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index 56682365381d34784b63d36ab50a1deedfb73e74..77820cd4d464f2582b57f60d34bc0c2f3622e4ae 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -297,7 +297,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
|
||||
public int autosavePeriod;
|
||||
public Commands vanillaCommandDispatcher;
|
||||
- private boolean forceTicks;
|
||||
+ public boolean forceTicks; // Paper
|
||||
// CraftBukkit end
|
||||
// Spigot start
|
||||
public static final int TPS = 20;
|
||||
@@ -308,6 +308,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
// Spigot end
|
||||
public static long currentTickLong = 0L; // Paper
|
||||
|
||||
+ public volatile Thread shutdownThread; // Paper
|
||||
+ public volatile boolean abnormalExit = false; // Paper
|
||||
+
|
||||
public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) {
|
||||
AtomicReference<S> atomicreference = new AtomicReference();
|
||||
Thread thread = new Thread(() -> {
|
||||
@@ -914,6 +917,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
// CraftBukkit start
|
||||
private boolean hasStopped = false;
|
||||
+ public volatile boolean hasFullyShutdown = false; // Paper
|
||||
private final Object stopLock = new Object();
|
||||
public final boolean hasStopped() {
|
||||
synchronized (this.stopLock) {
|
||||
@@ -928,6 +932,19 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
if (this.hasStopped) return;
|
||||
this.hasStopped = true;
|
||||
}
|
||||
+ // Paper start - kill main thread, and kill it hard
|
||||
+ shutdownThread = Thread.currentThread();
|
||||
+ org.spigotmc.WatchdogThread.doStop(); // Paper
|
||||
+ if (!isSameThread()) {
|
||||
+ MinecraftServer.LOGGER.info("Stopping main thread (Ignore any thread death message you see! - DO NOT REPORT THREAD DEATH TO PAPER)");
|
||||
+ while (this.getRunningThread().isAlive()) {
|
||||
+ this.getRunningThread().stop();
|
||||
+ try {
|
||||
+ Thread.sleep(1);
|
||||
+ } catch (InterruptedException e) {}
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
// CraftBukkit end
|
||||
MinecraftServer.LOGGER.info("Stopping server");
|
||||
MinecraftTimings.stopServer(); // Paper
|
||||
@@ -991,7 +1008,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
this.getProfileCache().save(false); // Paper
|
||||
}
|
||||
// Spigot end
|
||||
+ // Paper start - move final shutdown items here
|
||||
+ LOGGER.info("Flushing Chunk IO");
|
||||
com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.close(true, true); // Paper
|
||||
+ LOGGER.info("Closing Thread Pool");
|
||||
+ Util.shutdownExecutors(); // Paper
|
||||
+ LOGGER.info("Closing Server");
|
||||
+ try {
|
||||
+ net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
|
||||
+ } catch (Exception e) {
|
||||
+ }
|
||||
+ this.onServerExit();
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
public String getLocalIp() {
|
||||
@@ -1084,6 +1112,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
protected void runServer() {
|
||||
try {
|
||||
+ long serverStartTime = Util.getNanos(); // Paper
|
||||
if (this.initServer()) {
|
||||
this.nextTickTime = Util.getMillis();
|
||||
this.status.setDescription(new TextComponent(this.motd));
|
||||
@@ -1091,6 +1120,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
this.updateStatusIcon(this.status);
|
||||
|
||||
// Spigot start
|
||||
+ // Paper start - move done tracking
|
||||
+ LOGGER.info("Running delayed init tasks");
|
||||
+ this.server.getScheduler().mainThreadHeartbeat(this.tickCount); // run all 1 tick delay tasks during init,
|
||||
+ // this is going to be the first thing the tick process does anyways, so move done and run it after
|
||||
+ // everything is init before watchdog tick.
|
||||
+ // anything at 3+ won't be caught here but also will trip watchdog....
|
||||
+ // tasks are default scheduled at -1 + delay, and first tick will tick at 1
|
||||
+ String doneTime = String.format(java.util.Locale.ROOT, "%.3fs", (double) (Util.getNanos() - serverStartTime) / 1.0E9D);
|
||||
+ LOGGER.info("Done ({})! For help, type \"help\"", doneTime);
|
||||
+ // Paper end
|
||||
+
|
||||
+ org.spigotmc.WatchdogThread.tick(); // Paper
|
||||
org.spigotmc.WatchdogThread.hasStarted = true; // Paper
|
||||
Arrays.fill( recentTps, 20 );
|
||||
long start = System.nanoTime(), curTime, tickSection = start; // Paper - Further improve server tick loop
|
||||
@@ -1148,6 +1189,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
this.onServerCrash((CrashReport) null);
|
||||
}
|
||||
} catch (Throwable throwable) {
|
||||
+ // Paper start
|
||||
+ if (throwable instanceof ThreadDeath) {
|
||||
+ MinecraftServer.LOGGER.error("Main thread terminated by WatchDog due to hard crash", throwable);
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end
|
||||
MinecraftServer.LOGGER.error("Encountered an unexpected exception", throwable);
|
||||
// Spigot Start
|
||||
if ( throwable.getCause() != null )
|
||||
@@ -1187,14 +1234,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
this.profileCache.clearExecutor();
|
||||
}
|
||||
|
||||
- org.spigotmc.WatchdogThread.doStop(); // Spigot
|
||||
+ //org.spigotmc.WatchdogThread.doStop(); // Spigot // Paper - move into stop
|
||||
// CraftBukkit start - Restore terminal to original settings
|
||||
try {
|
||||
- net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
|
||||
+ //net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Move into stop
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
// CraftBukkit end
|
||||
- this.onServerExit();
|
||||
+ //this.onServerExit(); // Paper - moved into stop
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1233,6 +1280,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
@Override
|
||||
public TickTask wrapRunnable(Runnable runnable) {
|
||||
+ // Paper start - anything that does try to post to main during watchdog crash, run on watchdog
|
||||
+ if (this.hasStopped && Thread.currentThread().equals(shutdownThread)) {
|
||||
+ runnable.run();
|
||||
+ runnable = () -> {};
|
||||
+ }
|
||||
+ // Paper end
|
||||
return new TickTask(this.tickCount, runnable);
|
||||
}
|
||||
|
||||
@@ -1461,6 +1514,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
try {
|
||||
crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
|
||||
} catch (Throwable t) {
|
||||
+ if (throwable instanceof ThreadDeath) { throw (ThreadDeath)throwable; } // Paper
|
||||
throw new RuntimeException("Error generating crash report", t);
|
||||
}
|
||||
// Spigot End
|
||||
@@ -1893,7 +1947,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
this.packRepository.setSelected(datapacks);
|
||||
this.worldData.setDataPackConfig(MinecraftServer.getSelectedPacks(this.packRepository));
|
||||
datapackresources.updateGlobals();
|
||||
- this.getPlayerList().saveAll();
|
||||
+ if (Thread.currentThread() != this.serverThread) return; // Paper
|
||||
+ //this.getPlayerList().savePlayers(); // Paper - we don't need to do this
|
||||
this.getPlayerList().reloadResources();
|
||||
this.functionManager.replaceLibrary(this.resources.getFunctionLibrary());
|
||||
this.structureManager.onResourceManagerReload(this.resources.getResourceManager());
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index 049eb5693dc98e1d0ec3bd88c73a41fdb2f59bff..0716aaf29f9d76240a0de4ca02daba442b36ec7d 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -281,7 +281,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
long j = Util.getNanos() - i;
|
||||
String s = String.format(Locale.ROOT, "%.3fs", (double) j / 1.0E9D);
|
||||
|
||||
- DedicatedServer.LOGGER.info("Done ({})! For help, type \"help\"", s);
|
||||
+ //DedicatedServer.LOGGER.info("Done ({})! For help, type \"help\"", s); // Paper moved to after init
|
||||
if (dedicatedserverproperties.announcePlayerAchievements != null) {
|
||||
((GameRules.BooleanValue) this.getGameRules().getRule(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)).set(dedicatedserverproperties.announcePlayerAchievements, this);
|
||||
}
|
||||
@@ -438,7 +438,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
//this.remoteStatusListener.b(); // Paper - don't wait for remote connections
|
||||
}
|
||||
|
||||
- System.exit(0); // CraftBukkit
|
||||
+ hasFullyShutdown = true; // Paper
|
||||
+ System.exit(this.abnormalExit ? 70 : 0); // CraftBukkit // Paper
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -764,7 +765,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
@Override
|
||||
public void stopServer() {
|
||||
super.stopServer();
|
||||
- Util.shutdownExecutors();
|
||||
+ //Util.shutdownExecutors(); // Paper - moved into super
|
||||
SkullBlockEntity.clear();
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 847d1b447e3796cc03e26df46b92815a76707b81..0758f0983c17e7e4b5b5aea64ea12e990f7a22e4 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -540,6 +540,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
MutableBoolean mutableboolean = new MutableBoolean();
|
||||
|
||||
do {
|
||||
+ boolean isShuttingDown = level.getServer().hasStopped(); // Paper
|
||||
mutableboolean.setFalse();
|
||||
list.stream().map((playerchunk) -> {
|
||||
CompletableFuture completablefuture;
|
||||
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
index af93692bb5cc232397cec69ce2bd836a956550ef..fd0aea068fe4bec0bd02a372afd7475314ceb88a 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -512,7 +512,7 @@ public abstract class PlayerList {
|
||||
this.cserver.getPluginManager().callEvent(playerQuitEvent);
|
||||
entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage());
|
||||
|
||||
- entityplayer.doTick(); // SPIGOT-924
|
||||
+ if (server.isSameThread()) entityplayer.doTick(); // SPIGOT-924 // Paper - don't tick during emergency shutdowns (Watchdog)
|
||||
// CraftBukkit end
|
||||
|
||||
// Paper start - Remove from collideRule team if needed
|
||||
diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
|
||||
index 7bf4bf5cb2c1b54a7e2733091f48f3a824336d36..dcce05d2f4ab16424db4ab103a12188e207a457b 100644
|
||||
--- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
|
||||
+++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
|
||||
@@ -148,6 +148,7 @@ public abstract class BlockableEventLoop<R extends Runnable> implements Profiler
|
||||
try {
|
||||
task.run();
|
||||
} catch (Exception var3) {
|
||||
+ if (var3.getCause() instanceof ThreadDeath) throw var3; // Paper
|
||||
LOGGER.fatal("Error executing task on {}", this.name(), var3);
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index 6ec5a6239b6144b2e3f9edcafdfd6fed6de6cbcd..46adef4de2f9e5888dce1e669619dd0386b90470 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -830,6 +830,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
try {
|
||||
tickConsumer.accept(entity);
|
||||
} catch (Throwable throwable) {
|
||||
+ if (throwable instanceof ThreadDeath) throw throwable; // Paper
|
||||
// Paper start - Prevent tile entity and entity crashes
|
||||
final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level.getWorld().getName(), entity.getX(), entity.getY(), entity.getZ());
|
||||
MinecraftServer.LOGGER.error(msg, throwable);
|
||||
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 fc0ac2a5ad24951f05a18607318e5b5edf4f3463..c76e46afa685fbaa5c83aff4ef72b5fe32bc6d10 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -1067,6 +1067,7 @@ public class LevelChunk extends ChunkAccess {
|
||||
|
||||
gameprofilerfiller.pop();
|
||||
} catch (Throwable throwable) {
|
||||
+ if (throwable instanceof ThreadDeath) throw throwable; // Paper
|
||||
// Paper start - Prevent tile entity and entity crashes
|
||||
final String msg = String.format("BlockEntity threw exception at %s:%s,%s,%s", LevelChunk.this.getLevel().getWorld().getName(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ());
|
||||
net.minecraft.server.MinecraftServer.LOGGER.error(msg, throwable);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index b9b5c7c3d4e991dd62ed7acb33e02c2523fe5d46..c7e2ce7d2fa80c6c97ebe94ec5c36c9eb7356f45 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -2049,7 +2049,7 @@ public final class CraftServer implements Server {
|
||||
|
||||
@Override
|
||||
public boolean isPrimaryThread() {
|
||||
- return Thread.currentThread().equals(console.serverThread); // Paper - Fix issues with detecting main thread properly
|
||||
+ return Thread.currentThread().equals(console.serverThread) || Thread.currentThread().equals(net.minecraft.server.MinecraftServer.getServer().shutdownThread); // Paper - Fix issues with detecting main thread properly, the only time Watchdog will be used is during a crash shutdown which is a "try our best" scenario
|
||||
}
|
||||
|
||||
// Paper start
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
index 867c0802557375b63e69907bdb3ccd66644f1418..1ade239c9aacf2334c6dee773e0b37f3969723da 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
@@ -12,6 +12,8 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
+import net.minecraft.util.ExceptionCollector;
|
||||
+import net.minecraft.world.level.lighting.LayerLightEventListener;
|
||||
import net.minecrell.terminalconsole.TerminalConsoleAppender; // Paper
|
||||
|
||||
public class Main {
|
||||
@@ -162,6 +164,36 @@ public class Main {
|
||||
|
||||
OptionSet options = null;
|
||||
|
||||
+ // Paper start - preload logger classes to avoid plugins mixing versions
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.Core");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.appender.AsyncAppender");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.Appender");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.ContextDataInjector");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.Filter");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.ErrorHandler");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.LogEvent");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.Logger");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.LoggerContext");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.LogEventListener");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.AbstractLogEvent");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.message.AsynchronouslyFormattable");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.message.FormattedMessage");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.message.ParameterizedMessage");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.message.Message");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.message.MessageFactory");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.message.TimestampMessage");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.message.SimpleMessage");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncLogger");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncLoggerContext");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncQueueFullPolicy");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncLoggerDisruptor");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.async.RingBufferLogEvent");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.async.DisruptorUtil");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.async.RingBufferLogEventHandler");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.impl.ThrowableProxy");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.impl.ExtendedClassInfo");
|
||||
+ tryPreloadClass("org.apache.logging.log4j.core.impl.ExtendedStackTraceElement");
|
||||
+ // Paper end
|
||||
try {
|
||||
options = parser.parse(args);
|
||||
} catch (joptsimple.OptionException ex) {
|
||||
@@ -261,8 +293,64 @@ public class Main {
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
+ // Paper start
|
||||
+ // load some required classes to avoid errors during shutdown if jar is replaced
|
||||
+ // also to guarantee our version loads over plugins
|
||||
+ tryPreloadClass("com.destroystokyo.paper.util.SneakyThrow");
|
||||
+ tryPreloadClass("com.google.common.collect.Iterators$PeekingImpl");
|
||||
+ tryPreloadClass("com.google.common.collect.MapMakerInternalMap$Values");
|
||||
+ tryPreloadClass("com.google.common.collect.MapMakerInternalMap$ValueIterator");
|
||||
+ tryPreloadClass("com.google.common.collect.MapMakerInternalMap$WriteThroughEntry");
|
||||
+ tryPreloadClass("com.google.common.collect.Iterables");
|
||||
+ for (int i = 1; i <= 15; i++) {
|
||||
+ tryPreloadClass("com.google.common.collect.Iterables$" + i, false);
|
||||
+ }
|
||||
+ tryPreloadClass("org.apache.commons.lang3.mutable.MutableBoolean");
|
||||
+ tryPreloadClass("org.apache.commons.lang3.mutable.MutableInt");
|
||||
+ tryPreloadClass("org.jline.terminal.impl.MouseSupport");
|
||||
+ tryPreloadClass("org.jline.terminal.impl.MouseSupport$1");
|
||||
+ tryPreloadClass("org.jline.terminal.Terminal$MouseTracking");
|
||||
+ tryPreloadClass("co.aikar.timings.TimingHistory");
|
||||
+ tryPreloadClass("co.aikar.timings.TimingHistory$MinuteReport");
|
||||
+ tryPreloadClass("io.netty.channel.AbstractChannelHandlerContext");
|
||||
+ tryPreloadClass("io.netty.channel.AbstractChannelHandlerContext$11");
|
||||
+ tryPreloadClass("io.netty.channel.AbstractChannelHandlerContext$12");
|
||||
+ tryPreloadClass("io.netty.channel.AbstractChannel$AbstractUnsafe$8");
|
||||
+ tryPreloadClass("io.netty.util.concurrent.DefaultPromise");
|
||||
+ tryPreloadClass("io.netty.util.concurrent.DefaultPromise$1");
|
||||
+ tryPreloadClass("io.netty.util.internal.PromiseNotificationUtil");
|
||||
+ tryPreloadClass("io.netty.util.internal.SystemPropertyUtil");
|
||||
+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler");
|
||||
+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$1");
|
||||
+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$2");
|
||||
+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$3");
|
||||
+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$4");
|
||||
+ tryPreloadClass("org.slf4j.helpers.MessageFormatter");
|
||||
+ tryPreloadClass("org.slf4j.helpers.FormattingTuple");
|
||||
+ tryPreloadClass("org.slf4j.helpers.BasicMarker");
|
||||
+ tryPreloadClass("org.slf4j.helpers.Util");
|
||||
+ tryPreloadClass("com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent");
|
||||
+ tryPreloadClass("com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent");
|
||||
+ // Minecraft, seen during saving
|
||||
+ tryPreloadClass(LayerLightEventListener.DummyLightLayerEventListener.class.getName());
|
||||
+ tryPreloadClass(LayerLightEventListener.class.getName());
|
||||
+ tryPreloadClass(ExceptionCollector.class.getName());
|
||||
+ // Paper end
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Paper start
|
||||
+ private static void tryPreloadClass(String className) {
|
||||
+ tryPreloadClass(className, true);
|
||||
+ }
|
||||
+ private static void tryPreloadClass(String className, boolean printError) {
|
||||
+ try {
|
||||
+ Class.forName(className);
|
||||
+ } catch (ClassNotFoundException e) {
|
||||
+ if (printError) System.err.println("An expected class " + className + " was not found for preloading: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
+ // Paper end
|
||||
|
||||
private static List<String> asList(String... params) {
|
||||
return Arrays.asList(params);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
|
||||
index b4a19d80bbf71591f25729fd0e98590350cb31d0..d752720f2f234b9dbd2117333fee1bfad663ec02 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
|
||||
@@ -12,12 +12,27 @@ public class ServerShutdownThread extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
+ // Paper start - try to shutdown on main
|
||||
+ server.safeShutdown(false, false);
|
||||
+ for (int i = 1000; i > 0 && !server.hasStopped(); i -= 100) {
|
||||
+ Thread.sleep(100);
|
||||
+ }
|
||||
+ if (server.hasStopped()) {
|
||||
+ while (!server.hasFullyShutdown) Thread.sleep(1000);
|
||||
+ return;
|
||||
+ }
|
||||
+ // Looks stalled, close async
|
||||
org.spigotmc.AsyncCatcher.enabled = false; // Spigot
|
||||
org.spigotmc.AsyncCatcher.shuttingDown = true; // Paper
|
||||
+ server.forceTicks = true;
|
||||
this.server.close();
|
||||
+ while (!server.hasFullyShutdown) Thread.sleep(1000);
|
||||
+ } catch (InterruptedException e) {
|
||||
+ e.printStackTrace();
|
||||
+ // Paper end
|
||||
} finally {
|
||||
try {
|
||||
- net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
|
||||
+ //net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Move into stop
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
|
||||
index a142a56a920e153ed84c08cece993f10d76f7793..92d97a5810a379b427a99b4c63fb9844d823a84f 100644
|
||||
--- a/src/main/java/org/spigotmc/RestartCommand.java
|
||||
+++ b/src/main/java/org/spigotmc/RestartCommand.java
|
||||
@@ -139,7 +139,7 @@ public class RestartCommand extends Command
|
||||
// Paper end
|
||||
|
||||
// Paper start - copied from above and modified to return if the hook registered
|
||||
- private static boolean addShutdownHook(String restartScript)
|
||||
+ public static boolean addShutdownHook(String restartScript)
|
||||
{
|
||||
String[] split = restartScript.split( " " );
|
||||
if ( split.length > 0 && new File( split[0] ).isFile() )
|
||||
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
index 1ffb208094f521883ef0e23baf5fb29380b14273..4d271cae88c16ed2419f896c728fdff612540500 100644
|
||||
--- a/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
@@ -12,6 +12,7 @@ import org.bukkit.Bukkit;
|
||||
public class WatchdogThread extends Thread
|
||||
{
|
||||
|
||||
+ public static final boolean DISABLE_WATCHDOG = Boolean.getBoolean("disable.watchdog"); // Paper
|
||||
private static WatchdogThread instance;
|
||||
private long timeoutTime;
|
||||
private boolean restart;
|
||||
@@ -40,6 +41,7 @@ public class WatchdogThread extends Thread
|
||||
{
|
||||
if ( WatchdogThread.instance == null )
|
||||
{
|
||||
+ if (timeoutTime <= 0) timeoutTime = 300; // Paper
|
||||
WatchdogThread.instance = new WatchdogThread( timeoutTime * 1000L, restart );
|
||||
WatchdogThread.instance.start();
|
||||
} else
|
||||
@@ -71,12 +73,13 @@ public class WatchdogThread extends Thread
|
||||
// Paper start
|
||||
Logger log = Bukkit.getServer().getLogger();
|
||||
long currentTime = WatchdogThread.monotonicMillis();
|
||||
- if ( this.lastTick != 0 && this.timeoutTime > 0 && currentTime > this.lastTick + this.earlyWarningEvery && !Boolean.getBoolean("disable.watchdog")) // Paper - Add property to disable
|
||||
+ MinecraftServer server = MinecraftServer.getServer();
|
||||
+ if ( this.lastTick != 0 && this.timeoutTime > 0 && WatchdogThread.hasStarted && (!server.isRunning() || (currentTime > this.lastTick + this.earlyWarningEvery && !DISABLE_WATCHDOG) )) // Paper - add property to disable
|
||||
{
|
||||
- boolean isLongTimeout = currentTime > lastTick + timeoutTime;
|
||||
+ boolean isLongTimeout = currentTime > lastTick + timeoutTime || (!server.isRunning() && !server.hasStopped() && currentTime > lastTick + 1000);
|
||||
// Don't spam early warning dumps
|
||||
if ( !isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay)) continue;
|
||||
- if ( !isLongTimeout && MinecraftServer.getServer().hasStopped()) continue; // Don't spam early watchdog warnings during shutdown, we'll come back to this...
|
||||
+ if ( !isLongTimeout && server.hasStopped()) continue; // Don't spam early watchdog warnings during shutdown, we'll come back to this...
|
||||
lastEarlyWarning = currentTime;
|
||||
if (isLongTimeout) {
|
||||
// Paper end
|
||||
@@ -118,7 +121,7 @@ public class WatchdogThread extends Thread
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
|
||||
com.destroystokyo.paper.io.chunk.ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper
|
||||
- WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log );
|
||||
+ WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( server.serverThread.getId(), Integer.MAX_VALUE ), log );
|
||||
log.log( Level.SEVERE, "------------------------------" );
|
||||
//
|
||||
// Paper start - Only print full dump on long timeouts
|
||||
@@ -138,9 +141,25 @@ public class WatchdogThread extends Thread
|
||||
|
||||
if ( isLongTimeout )
|
||||
{
|
||||
- if ( this.restart && !MinecraftServer.getServer().hasStopped() )
|
||||
+ if ( !server.hasStopped() )
|
||||
{
|
||||
- RestartCommand.restart();
|
||||
+ AsyncCatcher.enabled = false; // Disable async catcher incase it interferes with us
|
||||
+ AsyncCatcher.shuttingDown = true;
|
||||
+ server.forceTicks = true;
|
||||
+ if (restart) {
|
||||
+ RestartCommand.addShutdownHook( SpigotConfig.restartScript );
|
||||
+ }
|
||||
+ // try one last chance to safe shutdown on main incase it 'comes back'
|
||||
+ server.abnormalExit = true;
|
||||
+ server.safeShutdown(false, restart);
|
||||
+ try {
|
||||
+ Thread.sleep(1000);
|
||||
+ } catch (InterruptedException e) {
|
||||
+ e.printStackTrace();
|
||||
+ }
|
||||
+ if (!server.hasStopped()) {
|
||||
+ server.close();
|
||||
+ }
|
||||
}
|
||||
break;
|
||||
} // Paper end
|
||||
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
||||
index 3dc317e466e1b93dff030794dd7f29ca1b266778..d285dbec16272db6b8a71865e05924ad66087407 100644
|
||||
--- a/src/main/resources/log4j2.xml
|
||||
+++ b/src/main/resources/log4j2.xml
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
-<Configuration status="WARN" packages="com.mojang.util">
|
||||
+<Configuration status="WARN" packages="com.mojang.util" shutdownHook="disable">
|
||||
<Appenders>
|
||||
<Queue name="ServerGuiConsole">
|
||||
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />
|
43
patches/server/0393-Optimize-Pathfinding.patch
Normal file
43
patches/server/0393-Optimize-Pathfinding.patch
Normal file
|
@ -0,0 +1,43 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Thu, 3 Mar 2016 02:02:07 -0600
|
||||
Subject: [PATCH] Optimize Pathfinding
|
||||
|
||||
Prevents pathfinding from spamming failures for things such as
|
||||
arrow attacks.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
|
||||
index e675eca77a0a1718cdaceefa20b026dffdcc5508..5884fb42f0880585dee843b98a6ea470a1508e46 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
|
||||
@@ -193,9 +193,29 @@ public abstract class PathNavigation {
|
||||
return this.moveTo(this.createPath(x, y, z, 1), speed);
|
||||
}
|
||||
|
||||
+ // Paper start - optimise pathfinding
|
||||
+ private int lastFailure = 0;
|
||||
+ private int pathfindFailures = 0;
|
||||
+ // Paper end
|
||||
+
|
||||
public boolean moveTo(Entity entity, double speed) {
|
||||
+ // Paper start - Pathfinding optimizations
|
||||
+ if (this.pathfindFailures > 10 && this.path == null && net.minecraft.server.MinecraftServer.currentTick < this.lastFailure + 40) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end
|
||||
Path path = this.createPath(entity, 1);
|
||||
- return path != null && this.moveTo(path, speed);
|
||||
+ // Paper start - Pathfinding optimizations
|
||||
+ if (path != null && this.moveTo(path, speed)) {
|
||||
+ this.lastFailure = 0;
|
||||
+ this.pathfindFailures = 0;
|
||||
+ return true;
|
||||
+ } else {
|
||||
+ this.pathfindFailures++;
|
||||
+ this.lastFailure = net.minecraft.server.MinecraftServer.currentTick;
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
public boolean moveTo(@Nullable Path path, double speed) {
|
48
patches/server/0394-Reduce-Either-Optional-allocation.patch
Normal file
48
patches/server/0394-Reduce-Either-Optional-allocation.patch
Normal file
|
@ -0,0 +1,48 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <spottedleaf@spottedleaf.dev>
|
||||
Date: Mon, 6 Apr 2020 18:35:09 -0700
|
||||
Subject: [PATCH] Reduce Either Optional allocation
|
||||
|
||||
In order to get chunk values, we shouldn't need to create
|
||||
an optional each time.
|
||||
|
||||
diff --git a/src/main/java/com/mojang/datafixers/util/Either.java b/src/main/java/com/mojang/datafixers/util/Either.java
|
||||
index a90adac7bd7ebd423f480e9ae0f44cb9d521fa4f..3f65fe71024928e35111fc6719a290aab9a6859e 100644
|
||||
--- a/src/main/java/com/mojang/datafixers/util/Either.java
|
||||
+++ b/src/main/java/com/mojang/datafixers/util/Either.java
|
||||
@@ -22,7 +22,7 @@ public abstract class Either<L, R> implements App<Either.Mu<R>, L> {
|
||||
}
|
||||
|
||||
private static final class Left<L, R> extends Either<L, R> {
|
||||
- private final L value;
|
||||
+ private final L value; private Optional<L> valueOptional; // Paper - reduce the optional allocation...
|
||||
|
||||
public Left(final L value) {
|
||||
this.value = value;
|
||||
@@ -51,7 +51,7 @@ public abstract class Either<L, R> implements App<Either.Mu<R>, L> {
|
||||
|
||||
@Override
|
||||
public Optional<L> left() {
|
||||
- return Optional.of(value);
|
||||
+ return this.valueOptional == null ? this.valueOptional = Optional.of(this.value) : this.valueOptional; // Paper - reduce the optional allocation...
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -83,7 +83,7 @@ public abstract class Either<L, R> implements App<Either.Mu<R>, L> {
|
||||
}
|
||||
|
||||
private static final class Right<L, R> extends Either<L, R> {
|
||||
- private final R value;
|
||||
+ private final R value; private Optional<R> valueOptional; // Paper - reduce the optional allocation...
|
||||
|
||||
public Right(final R value) {
|
||||
this.value = value;
|
||||
@@ -117,7 +117,7 @@ public abstract class Either<L, R> implements App<Either.Mu<R>, L> {
|
||||
|
||||
@Override
|
||||
public Optional<R> right() {
|
||||
- return Optional.of(value);
|
||||
+ return this.valueOptional == null ? this.valueOptional = Optional.of(this.value) : this.valueOptional; // Paper - reduce the optional allocation...
|
||||
}
|
||||
|
||||
@Override
|
|
@ -0,0 +1,50 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <spottedleaf@spottedleaf.dev>
|
||||
Date: Mon, 6 Apr 2020 17:39:25 -0700
|
||||
Subject: [PATCH] Reduce memory footprint of NBTTagCompound
|
||||
|
||||
Fastutil maps are going to have a lower memory footprint - which
|
||||
is important because we clone chunk data after reading it for safety.
|
||||
So, reduce the impact of the clone on GC.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/nbt/CompoundTag.java b/src/main/java/net/minecraft/nbt/CompoundTag.java
|
||||
index d0b523387a194d1649469e8d861b0b78a2f4e0b6..be2bd47a509a03e78c380cf749cd476f332ab03d 100644
|
||||
--- a/src/main/java/net/minecraft/nbt/CompoundTag.java
|
||||
+++ b/src/main/java/net/minecraft/nbt/CompoundTag.java
|
||||
@@ -35,7 +35,7 @@ public class CompoundTag implements Tag {
|
||||
if (i > 512) {
|
||||
throw new RuntimeException("Tried to read NBT tag with too high complexity, depth > 512");
|
||||
} else {
|
||||
- Map<String, Tag> map = Maps.newHashMap();
|
||||
+ it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<String, Tag> map = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f); // Paper - reduce memory footprint of NBTTagCompound
|
||||
|
||||
byte b;
|
||||
while((b = CompoundTag.readNamedTagType(dataInput, nbtAccounter)) != 0) {
|
||||
@@ -129,7 +129,7 @@ public class CompoundTag implements Tag {
|
||||
}
|
||||
|
||||
public CompoundTag() {
|
||||
- this(Maps.newHashMap());
|
||||
+ this(new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f)); // Paper - reduce memory footprint of NBTTagCompound
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -435,8 +435,16 @@ public class CompoundTag implements Tag {
|
||||
|
||||
@Override
|
||||
public CompoundTag copy() {
|
||||
- Map<String, Tag> map = Maps.newHashMap(Maps.transformValues(this.tags, Tag::copy));
|
||||
- return new CompoundTag(map);
|
||||
+ // Paper start - reduce memory footprint of NBTTagCompound
|
||||
+ it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<String, Tag> ret = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(this.tags.size(), 0.8f);
|
||||
+ java.util.Iterator<java.util.Map.Entry<String, Tag>> iterator = (this.tags instanceof it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap) ? ((it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap)this.tags).object2ObjectEntrySet().fastIterator() : this.tags.entrySet().iterator();
|
||||
+ while (iterator.hasNext()) {
|
||||
+ Map.Entry<String, Tag> entry = iterator.next();
|
||||
+ ret.put(entry.getKey(), entry.getValue().copy());
|
||||
+ }
|
||||
+
|
||||
+ return new CompoundTag(ret);
|
||||
+ // Paper end - reduce memory footprint of NBTTagCompound
|
||||
}
|
||||
|
||||
@Override
|
|
@ -0,0 +1,50 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shane Freeder <theboyetronic@gmail.com>
|
||||
Date: Mon, 13 Apr 2020 07:31:44 +0100
|
||||
Subject: [PATCH] Prevent opening inventories when frozen
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
index d507adcb538933fcf36e9a4bfb561106d509c26f..9cbca14b0a111e57a1d01bcbcf2164ab8b53b1a5 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -612,7 +612,7 @@ public class ServerPlayer extends Player {
|
||||
containerUpdateDelay = level.paperConfig.containerUpdateTickRate;
|
||||
}
|
||||
// Paper end
|
||||
- if (!this.level.isClientSide && !this.containerMenu.stillValid(this)) {
|
||||
+ if (!this.level.isClientSide && this.containerMenu != this.inventoryMenu && (isImmobile() || !this.containerMenu.stillValid(this))) { // Paper - auto close while frozen
|
||||
this.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); // Paper
|
||||
this.containerMenu = this.inventoryMenu;
|
||||
}
|
||||
@@ -1479,7 +1479,7 @@ public class ServerPlayer extends Player {
|
||||
} else {
|
||||
// CraftBukkit start
|
||||
this.containerMenu = container;
|
||||
- this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), container.getTitle()));
|
||||
+ if (!isImmobile()) this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), container.getTitle())); // Paper
|
||||
// CraftBukkit end
|
||||
this.initMenu(container);
|
||||
return OptionalInt.of(this.containerCounter);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
||||
index 76a08aaf5106a5e8d0a24e9d966817574ec26068..9ad94aea2959082dfd44edd63c0a5aa1cec1e655 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
||||
@@ -322,7 +322,7 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
|
||||
if (adventure$title == null) adventure$title = io.papermc.paper.adventure.PaperAdventure.LEGACY_SECTION_UXRC.deserialize(container.getBukkitView().getTitle()); // Paper
|
||||
|
||||
//player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, CraftChatMessage.fromString(title)[0])); // Paper // Paper - comment
|
||||
- player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
|
||||
+ if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
|
||||
player.containerMenu = container;
|
||||
player.initMenu(container);
|
||||
}
|
||||
@@ -396,7 +396,7 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
|
||||
net.kyori.adventure.text.Component adventure$title = inventory.title(); // Paper
|
||||
if (adventure$title == null) adventure$title = io.papermc.paper.adventure.PaperAdventure.LEGACY_SECTION_UXRC.deserialize(inventory.getTitle()); // Paper
|
||||
//player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, windowType, CraftChatMessage.fromString(title)[0])); // Paper - comment
|
||||
- player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
|
||||
+ if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
|
||||
player.containerMenu = container;
|
||||
player.initMenu(container);
|
||||
}
|
53
patches/server/0397-Optimise-ArraySetSorted-removeIf.patch
Normal file
53
patches/server/0397-Optimise-ArraySetSorted-removeIf.patch
Normal file
|
@ -0,0 +1,53 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Wed, 15 Apr 2020 18:23:28 -0700
|
||||
Subject: [PATCH] Optimise ArraySetSorted#removeIf
|
||||
|
||||
Remove iterator allocation and ensure the call is always O(n)
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/util/SortedArraySet.java b/src/main/java/net/minecraft/util/SortedArraySet.java
|
||||
index d1b2ba24ef54e01c6249c3b2ca16e80f03c001a6..5f1c4c6b9e36f2d6ec43b82cc0e2cae24b800dc4 100644
|
||||
--- a/src/main/java/net/minecraft/util/SortedArraySet.java
|
||||
+++ b/src/main/java/net/minecraft/util/SortedArraySet.java
|
||||
@@ -22,6 +22,41 @@ public class SortedArraySet<T> extends AbstractSet<T> {
|
||||
this.contents = (T[])castRawArray(new Object[initialCapacity]);
|
||||
}
|
||||
}
|
||||
+ // Paper start - optimise removeIf
|
||||
+ @Override
|
||||
+ public boolean removeIf(java.util.function.Predicate<? super T> filter) {
|
||||
+ // prev. impl used an iterator, which could be n^2 and creates garbage
|
||||
+ int i = 0, len = this.size;
|
||||
+ T[] backingArray = this.contents;
|
||||
+
|
||||
+ for (;;) {
|
||||
+ if (i >= len) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ if (!filter.test(backingArray[i])) {
|
||||
+ ++i;
|
||||
+ continue;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ // we only want to write back to backingArray if we really need to
|
||||
+
|
||||
+ int lastIndex = i; // this is where new elements are shifted to
|
||||
+
|
||||
+ for (; i < len; ++i) {
|
||||
+ T curr = backingArray[i];
|
||||
+ if (!filter.test(curr)) { // if test throws we're screwed
|
||||
+ backingArray[lastIndex++] = curr;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // cleanup end
|
||||
+ Arrays.fill(backingArray, lastIndex, len, null);
|
||||
+ this.size = lastIndex;
|
||||
+ return true;
|
||||
+ }
|
||||
+ // Paper end - optimise removeIf
|
||||
|
||||
public static <T extends Comparable<T>> SortedArraySet<T> create() {
|
||||
return create(10);
|
|
@ -0,0 +1,30 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Wed, 15 Apr 2020 17:56:07 -0700
|
||||
Subject: [PATCH] Don't run entity collision code if not needed
|
||||
|
||||
Will not run if max entity craming is disabled and
|
||||
the max collisions per entity is less than or equal to 0
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
index 06c32d50d6672d2816c8cb1b4ef0dc038ffce817..ba3aae873c2bd23a1bb35bb7746907d0bcfd0dd3 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -3261,10 +3261,16 @@ public abstract class LivingEntity extends Entity {
|
||||
protected void serverAiStep() {}
|
||||
|
||||
protected void pushEntities() {
|
||||
+ // Paper start - don't run getEntities if we're not going to use its result
|
||||
+ int i = this.level.getGameRules().getInt(GameRules.RULE_MAX_ENTITY_CRAMMING);
|
||||
+ if (i <= 0 && level.paperConfig.maxCollisionsPerEntity <= 0) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - don't run getEntities if we're not going to use its result
|
||||
List<Entity> list = this.level.getEntities((Entity) this, this.getBoundingBox(), EntitySelector.pushableBy(this));
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
- int i = this.level.getGameRules().getInt(GameRules.RULE_MAX_ENTITY_CRAMMING);
|
||||
+ // Paper - move up
|
||||
int j;
|
||||
|
||||
if (i > 0 && list.size() > i - 1 && this.random.nextInt(4) == 0) {
|
152
patches/server/0399-Implement-Player-Client-Options-API.patch
Normal file
152
patches/server/0399-Implement-Player-Client-Options-API.patch
Normal file
|
@ -0,0 +1,152 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MiniDigger <admin@minidigger.me>
|
||||
Date: Mon, 20 Jan 2020 21:38:15 +0100
|
||||
Subject: [PATCH] Implement Player Client Options API
|
||||
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperSkinParts.java b/src/main/java/com/destroystokyo/paper/PaperSkinParts.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..b6f4400df3d8ec7e06a996de54f8cabba57885e1
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperSkinParts.java
|
||||
@@ -0,0 +1,74 @@
|
||||
+package com.destroystokyo.paper;
|
||||
+
|
||||
+import com.google.common.base.Objects;
|
||||
+
|
||||
+import java.util.StringJoiner;
|
||||
+
|
||||
+public class PaperSkinParts implements SkinParts {
|
||||
+
|
||||
+ private final int raw;
|
||||
+
|
||||
+ public PaperSkinParts(int raw) {
|
||||
+ this.raw = raw;
|
||||
+ }
|
||||
+
|
||||
+ public boolean hasCapeEnabled() {
|
||||
+ return (raw & 1) == 1;
|
||||
+ }
|
||||
+
|
||||
+ public boolean hasJacketEnabled() {
|
||||
+ return (raw >> 1 & 1) == 1;
|
||||
+ }
|
||||
+
|
||||
+ public boolean hasLeftSleeveEnabled() {
|
||||
+ return (raw >> 2 & 1) == 1;
|
||||
+ }
|
||||
+
|
||||
+ public boolean hasRightSleeveEnabled() {
|
||||
+ return (raw >> 3 & 1) == 1;
|
||||
+ }
|
||||
+
|
||||
+ public boolean hasLeftPantsEnabled() {
|
||||
+ return (raw >> 4 & 1) == 1;
|
||||
+ }
|
||||
+
|
||||
+ public boolean hasRightPantsEnabled() {
|
||||
+ return (raw >> 5 & 1) == 1;
|
||||
+ }
|
||||
+
|
||||
+ public boolean hasHatsEnabled() {
|
||||
+ return (raw >> 6 & 1) == 1;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int getRaw() {
|
||||
+ return raw;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean equals(Object o) {
|
||||
+ if (this == o) return true;
|
||||
+ if (o == null || getClass() != o.getClass()) return false;
|
||||
+ PaperSkinParts that = (PaperSkinParts) o;
|
||||
+ return raw == that.raw;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int hashCode() {
|
||||
+ return Objects.hashCode(raw);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String toString() {
|
||||
+ return new StringJoiner(", ", PaperSkinParts.class.getSimpleName() + "[", "]")
|
||||
+ .add("raw=" + raw)
|
||||
+ .add("cape=" + hasCapeEnabled())
|
||||
+ .add("jacket=" + hasJacketEnabled())
|
||||
+ .add("leftSleeve=" + hasLeftSleeveEnabled())
|
||||
+ .add("rightSleeve=" + hasRightSleeveEnabled())
|
||||
+ .add("leftPants=" + hasLeftPantsEnabled())
|
||||
+ .add("rightPants=" + hasRightPantsEnabled())
|
||||
+ .add("hats=" + hasHatsEnabled())
|
||||
+ .toString();
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
index 9cbca14b0a111e57a1d01bcbcf2164ab8b53b1a5..cdb0eb8e21299ca70ed7ed5c1195d07f44e47838 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -1830,6 +1830,7 @@ public class ServerPlayer extends Player {
|
||||
public String locale = null; // CraftBukkit - add, lowercase // Paper - default to null
|
||||
public java.util.Locale adventure$locale = java.util.Locale.US; // Paper
|
||||
public void updateOptions(ServerboundClientInformationPacket packet) {
|
||||
+ new com.destroystokyo.paper.event.player.PlayerClientOptionsChangeEvent(getBukkitEntity(), packet.language, packet.viewDistance, com.destroystokyo.paper.ClientOption.ChatVisibility.valueOf(packet.chatVisibility().name()), packet.chatColors(), new com.destroystokyo.paper.PaperSkinParts(packet.modelCustomisation()), packet.mainHand() == HumanoidArm.LEFT ? MainHand.LEFT : MainHand.RIGHT).callEvent(); // Paper - settings event
|
||||
// CraftBukkit start
|
||||
if (getMainArm() != packet.mainHand()) {
|
||||
PlayerChangedMainHandEvent event = new PlayerChangedMainHandEvent(this.getBukkitEntity(), getMainArm() == HumanoidArm.LEFT ? MainHand.LEFT : MainHand.RIGHT);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
index 131a8fcf8b0e9ddb5e9f456c494ddea27d4cb88e..ac535aef823ebe286847dafb8b0678de5d3128a1 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
|
||||
@@ -544,6 +544,24 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
||||
public void setSendViewDistance(int viewDistance) {
|
||||
throw new NotImplementedException("Per-Player View Distance APIs need further understanding to properly implement (There are per world view distances though!)"); // TODO
|
||||
}
|
||||
+
|
||||
+ @Override
|
||||
+ public <T> T getClientOption(com.destroystokyo.paper.ClientOption<T> type) {
|
||||
+ if(com.destroystokyo.paper.ClientOption.SKIN_PARTS.equals(type)) {
|
||||
+ return type.getType().cast(new com.destroystokyo.paper.PaperSkinParts(getHandle().getEntityData().get(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION)));
|
||||
+ } else if(com.destroystokyo.paper.ClientOption.CHAT_COLORS_ENABLED.equals(type)) {
|
||||
+ return type.getType().cast(getHandle().canChatInColor());
|
||||
+ } else if(com.destroystokyo.paper.ClientOption.CHAT_VISIBILITY.equals(type)) {
|
||||
+ return type.getType().cast(getHandle().getChatVisibility() == null ? com.destroystokyo.paper.ClientOption.ChatVisibility.UNKNOWN : com.destroystokyo.paper.ClientOption.ChatVisibility.valueOf(getHandle().getChatVisibility().name()));
|
||||
+ } else if(com.destroystokyo.paper.ClientOption.LOCALE.equals(type)) {
|
||||
+ return type.getType().cast(getLocale());
|
||||
+ } else if(com.destroystokyo.paper.ClientOption.MAIN_HAND.equals(type)) {
|
||||
+ return type.getType().cast(getMainHand());
|
||||
+ } else if(com.destroystokyo.paper.ClientOption.VIEW_DISTANCE.equals(type)) {
|
||||
+ return type.getType().cast(getClientViewDistance());
|
||||
+ }
|
||||
+ throw new RuntimeException("Unknown settings type");
|
||||
+ }
|
||||
// Paper end
|
||||
|
||||
@Override
|
||||
diff --git a/src/test/java/io/papermc/paper/world/TranslationKeyTest.java b/src/test/java/io/papermc/paper/world/TranslationKeyTest.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..6cd015dc5a2e012ac827c2b2d9aa5542b0591afb
|
||||
--- /dev/null
|
||||
+++ b/src/test/java/io/papermc/paper/world/TranslationKeyTest.java
|
||||
@@ -0,0 +1,19 @@
|
||||
+package io.papermc.paper.world;
|
||||
+
|
||||
+import com.destroystokyo.paper.ClientOption;
|
||||
+import net.minecraft.network.chat.TranslatableComponent;
|
||||
+import net.minecraft.world.entity.player.ChatVisiblity;
|
||||
+import org.bukkit.Difficulty;
|
||||
+import org.junit.Assert;
|
||||
+import org.junit.Test;
|
||||
+
|
||||
+public class TranslationKeyTest {
|
||||
+
|
||||
+ @Test
|
||||
+ public void testChatVisibilityKeys() {
|
||||
+ for (ClientOption.ChatVisibility chatVisibility : ClientOption.ChatVisibility.values()) {
|
||||
+ if (chatVisibility == ClientOption.ChatVisibility.UNKNOWN) continue;
|
||||
+ Assert.assertEquals(chatVisibility + "'s translation key doesn't match", ChatVisiblity.valueOf(chatVisibility.name()).getKey(), chatVisibility.translationKey());
|
||||
+ }
|
||||
+ }
|
||||
+}
|
|
@ -0,0 +1,23 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Sat, 18 Apr 2020 15:59:41 -0400
|
||||
Subject: [PATCH] Don't crash if player is attempted to be removed from
|
||||
untracked chunk.
|
||||
|
||||
I suspect it deals with teleporting as it uses players current x/y/z
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
index 975f1aab48f04a9f3cae04141b3fb65247f0ffaf..1a6c75a33c6dfec63eabef0b8a0a5d8812aeeb4c 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
@@ -281,8 +281,8 @@ public abstract class DistanceManager {
|
||||
ObjectSet<ServerPlayer> objectset = (ObjectSet) this.playersPerChunk.get(i);
|
||||
if (objectset == null) return; // CraftBukkit - SPIGOT-6208
|
||||
|
||||
- objectset.remove(player);
|
||||
- if (objectset.isEmpty()) {
|
||||
+ if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully.
|
||||
+ if (objectset == null || objectset.isEmpty()) { // Paper
|
||||
this.playersPerChunk.remove(i);
|
||||
this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
|
||||
this.playerTicketManager.update(i, Integer.MAX_VALUE, false);
|
21
patches/server/0401-Broadcast-join-message-to-console.patch
Normal file
21
patches/server/0401-Broadcast-join-message-to-console.patch
Normal file
|
@ -0,0 +1,21 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: AvrooVulcan <avrovulcan.programming@gmail.com>
|
||||
Date: Fri, 17 Apr 2020 00:15:23 +0100
|
||||
Subject: [PATCH] Broadcast join message to console
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
index fd0aea068fe4bec0bd02a372afd7475314ceb88a..ccf03feb976d22f8c4ca4f60b6bc1d1d773c485f 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -293,7 +293,9 @@ public abstract class PlayerList {
|
||||
|
||||
if (jm != null && !jm.equals(net.kyori.adventure.text.Component.empty())) { // Paper - Adventure
|
||||
joinMessage = PaperAdventure.asVanilla(jm); // Paper - Adventure
|
||||
- this.server.getPlayerList().broadcastAll(new ClientboundChatPacket(joinMessage, ChatType.SYSTEM, Util.NIL_UUID)); // Paper - Adventure
|
||||
+ // Paper start - Removed sendAll for loop and broadcasted to console also
|
||||
+ this.server.getPlayerList().broadcastMessage(joinMessage, ChatType.SYSTEM, Util.NIL_UUID); // Paper - Adventure
|
||||
+ // Paper end
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue