a bunch more patches done
This commit is contained in:
parent
c3fc3de622
commit
dc12a6e9fe
41 changed files with 177 additions and 170 deletions
|
@ -1,24 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Vainomaa <mikroskeem@mikroskeem.eu>
|
||||
Date: Fri, 1 May 2020 17:39:26 +0300
|
||||
Subject: [PATCH] Expose game version
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 9e2adc414d43f0c91f222be5f082e74d9db173aa..271f4ab919788bfad7ce750b1dafc742debed674 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -573,6 +573,13 @@ public final class CraftServer implements Server {
|
||||
return this.bukkitVersion;
|
||||
}
|
||||
|
||||
+ // Paper start - expose game version
|
||||
+ @Override
|
||||
+ public String getMinecraftVersion() {
|
||||
+ return console.getServerVersion();
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public List<CraftPlayer> getOnlinePlayers() {
|
||||
return this.playerView;
|
|
@ -1,121 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Sun, 3 May 2020 22:35:09 -0400
|
||||
Subject: [PATCH] Optimize Voxel Shape Merging
|
||||
|
||||
This method shows up as super hot in profiler, and also a high "self" time.
|
||||
|
||||
Upon analyzing, it appears most usages of this method fall down to the final
|
||||
else statement of the nasty ternary.
|
||||
|
||||
Upon even further analyzation, it appears then the majority of those have a
|
||||
consistent list 1.... One with Infinity head and Tails.
|
||||
|
||||
First optimization is to detect these infinite states and immediately return that
|
||||
VoxelShapeMergerList so we can avoid testing the rest for most cases.
|
||||
|
||||
Break the method into 2 to help the JVM promote inlining of this fast path.
|
||||
|
||||
Then it was also noticed that VoxelShapeMergerList constructor is also a hotspot
|
||||
with a high self time...
|
||||
|
||||
Well, knowing that in most cases our list 1 is actualy the same value, it allows
|
||||
us to know that with an infinite list1, the result on the merger is essentially
|
||||
list2 as the final values.
|
||||
|
||||
This let us analyze the 2 potential states (Infinite with 2 sources or 4 sources)
|
||||
and compute a deterministic result for the MergerList values.
|
||||
|
||||
Additionally, this lets us avoid even allocating new objects for this too, further
|
||||
reducing memory usage.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java b/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java
|
||||
index 9e0afab2329e560c4b2512548dd4b02dd1a2e69f..06662dbff8180751a8684841aa35f709007078ae 100644
|
||||
--- a/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java
|
||||
+++ b/src/main/java/net/minecraft/world/phys/shapes/IndirectMerger.java
|
||||
@@ -10,12 +10,33 @@ public class IndirectMerger implements IndexMerger {
|
||||
private final int[] firstIndices;
|
||||
private final int[] secondIndices;
|
||||
private final int resultLength;
|
||||
+ // Paper start
|
||||
+ private static final int[] INFINITE_B_1 = new int[]{1, 1};
|
||||
+ private static final int[] INFINITE_B_0 = new int[]{0, 0};
|
||||
+ private static final int[] INFINITE_C = new int[]{0, 1};
|
||||
+ // Paper end
|
||||
|
||||
public IndirectMerger(DoubleList first, DoubleList second, boolean includeFirstOnly, boolean includeSecondOnly) {
|
||||
double d = Double.NaN;
|
||||
int i = first.size();
|
||||
int j = second.size();
|
||||
int k = i + j;
|
||||
+ // Paper start - optimize common path of infinity doublelist
|
||||
+ int size = first.size();
|
||||
+ double tail = first.getDouble(size - 1);
|
||||
+ double head = first.getDouble(0);
|
||||
+ if (head == Double.NEGATIVE_INFINITY && tail == Double.POSITIVE_INFINITY && !includeFirstOnly && !includeSecondOnly && (size == 2 || size == 4)) {
|
||||
+ this.result = second.toDoubleArray();
|
||||
+ this.resultLength = second.size();
|
||||
+ if (size == 2) {
|
||||
+ this.firstIndices = INFINITE_B_0;
|
||||
+ } else {
|
||||
+ this.firstIndices = INFINITE_B_1;
|
||||
+ }
|
||||
+ this.secondIndices = INFINITE_C;
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end
|
||||
this.result = new double[k];
|
||||
this.firstIndices = new int[k];
|
||||
this.secondIndices = new int[k];
|
||||
diff --git a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
|
||||
index 2d273be8145bbd86ffdf33358629da7fc08b4d4c..9176735c08a75854209f24113b0e78332249dc4d 100644
|
||||
--- a/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
|
||||
+++ b/src/main/java/net/minecraft/world/phys/shapes/Shapes.java
|
||||
@@ -247,9 +247,21 @@ public final class Shapes {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
- protected static IndexMerger createIndexMerger(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) {
|
||||
+ private static IndexMerger createIndexMerger(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) { // Paper - private
|
||||
+ // Paper start - fast track the most common scenario
|
||||
+ // doublelist is usually a DoubleArrayList with Infinite head/tails that falls to the final else clause
|
||||
+ // This is actually the most common path, so jump to it straight away
|
||||
+ if (first.getDouble(0) == Double.NEGATIVE_INFINITY && first.getDouble(first.size() - 1) == Double.POSITIVE_INFINITY) {
|
||||
+ return new IndirectMerger(first, second, includeFirst, includeSecond);
|
||||
+ }
|
||||
+ // Split out rest to hopefully inline the above
|
||||
+ return lessCommonMerge(size, first, second, includeFirst, includeSecond);
|
||||
+ }
|
||||
+
|
||||
+ private static IndexMerger lessCommonMerge(int size, DoubleList first, DoubleList second, boolean includeFirst, boolean includeSecond) {
|
||||
int i = first.size() - 1;
|
||||
int j = second.size() - 1;
|
||||
+ // Paper note - Rewrite below as optimized order if instead of nasty ternary
|
||||
if (first instanceof CubePointRange && second instanceof CubePointRange) {
|
||||
long l = lcm(i, j);
|
||||
if ((long)size * l <= 256L) {
|
||||
@@ -257,13 +269,22 @@ public final class Shapes {
|
||||
}
|
||||
}
|
||||
|
||||
- if (first.getDouble(i) < second.getDouble(0) - 1.0E-7D) {
|
||||
+ // Paper start - Identical happens more often than Disjoint
|
||||
+ if (i == j && Objects.equals(first, second)) {
|
||||
+ if (first instanceof IdenticalMerger) {
|
||||
+ return (IndexMerger) first;
|
||||
+ } else if (second instanceof IdenticalMerger) {
|
||||
+ return (IndexMerger) second;
|
||||
+ }
|
||||
+ return new IdenticalMerger(first);
|
||||
+ } else if (first.getDouble(i) < second.getDouble(0) - 1.0E-7D) {
|
||||
return new NonOverlappingMerger(first, second, false);
|
||||
} else if (second.getDouble(j) < first.getDouble(0) - 1.0E-7D) {
|
||||
return new NonOverlappingMerger(second, first, true);
|
||||
} else {
|
||||
- return (IndexMerger)(i == j && Objects.equals(first, second) ? new IdenticalMerger(first) : new IndirectMerger(first, second, includeFirst, includeSecond));
|
||||
+ return new IndirectMerger(first, second, includeFirst, includeSecond);
|
||||
}
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
public interface DoubleLineConsumer {
|
|
@ -1,30 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Mon, 4 May 2020 01:08:56 -0400
|
||||
Subject: [PATCH] Set cap on JDK per-thread native byte buffer cache
|
||||
|
||||
See: https://www.evanjones.ca/java-bytebuffer-leak.html
|
||||
|
||||
This is potentially a source of lots of native memory usage.
|
||||
|
||||
We are clearly seeing native usage upwards to 1-4GB which doesn't make sense.
|
||||
|
||||
Region File usage fixed in previous patch should of tecnically only been somewhat
|
||||
temporary until GC finally gets it some time later, but between all the various
|
||||
plugins doing IO on various threads, this hidden detail of the JDK could be
|
||||
keeping long lived large direct buffers in cache.
|
||||
|
||||
Set system properly at server startup if not set already to help protect from this.
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
index 7a5e7e8a680ead5128fbf5829bc47704715b94bd..fbe68bd4739d9a0e7d9bc4c3d5ba8ecfd2d13954 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
@@ -28,6 +28,7 @@ public class Main {
|
||||
}
|
||||
// Paper end
|
||||
// Todo: Installation script
|
||||
+ if (System.getProperty("jdk.nio.maxCachedBufferSize") == null) System.setProperty("jdk.nio.maxCachedBufferSize", "262144"); // Paper - cap per-thread NIO cache size
|
||||
OptionParser parser = new OptionParser() {
|
||||
{
|
||||
acceptsAll(Main.asList("?", "help"), "Show the help");
|
|
@ -1,87 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shane Freeder <theboyetronic@gmail.com>
|
||||
Date: Thu, 18 Feb 2021 20:23:28 +0000
|
||||
Subject: [PATCH] misc debugging dumps
|
||||
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/util/TraceUtil.java b/src/main/java/io/papermc/paper/util/TraceUtil.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..2d5494d2813b773e60ddba6790b750a9a08f21f8
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/TraceUtil.java
|
||||
@@ -0,0 +1,18 @@
|
||||
+package io.papermc.paper.util;
|
||||
+
|
||||
+import org.bukkit.Bukkit;
|
||||
+
|
||||
+public final class TraceUtil {
|
||||
+
|
||||
+ public static void dumpTraceForThread(Thread thread, String reason) {
|
||||
+ Bukkit.getLogger().warning(thread.getName() + ": " + reason);
|
||||
+ StackTraceElement[] trace = thread.getStackTrace();
|
||||
+ for (StackTraceElement traceElement : trace) {
|
||||
+ Bukkit.getLogger().warning("\tat " + traceElement);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static void dumpTraceForThread(String reason) {
|
||||
+ new Throwable(reason).printStackTrace();
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index 4ed65f1371013fb91fe60a0e1501e222a6e9c508..abb640370da876a18a3eba952b19cfa9d0f73743 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -915,6 +915,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
// CraftBukkit start
|
||||
private boolean hasStopped = false;
|
||||
public volatile boolean hasFullyShutdown = false; // Paper
|
||||
+ private boolean hasLoggedStop = false; // Paper
|
||||
private final Object stopLock = new Object();
|
||||
public final boolean hasStopped() {
|
||||
synchronized (this.stopLock) {
|
||||
@@ -929,6 +930,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
if (this.hasStopped) return;
|
||||
this.hasStopped = true;
|
||||
}
|
||||
+ if (!hasLoggedStop && isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper
|
||||
// Paper start - kill main thread, and kill it hard
|
||||
shutdownThread = Thread.currentThread();
|
||||
org.spigotmc.WatchdogThread.doStop(); // Paper
|
||||
@@ -1054,6 +1056,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
public void safeShutdown(boolean flag, boolean isRestarting) {
|
||||
this.isRestarting = isRestarting;
|
||||
+ this.hasLoggedStop = true; // Paper
|
||||
+ if (isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Server stopped"); // Paper
|
||||
// Paper end
|
||||
this.running = false;
|
||||
if (flag) {
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
index 0af65f1698e4ee9d94724f19b0abd61c437f18f2..d21f45d983bf3047811d2d73f4a38deb108ac402 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
|
||||
@@ -204,6 +204,11 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener
|
||||
} catch (Exception exception) {
|
||||
ServerLoginPacketListenerImpl.LOGGER.error("Couldn't place player in world", exception);
|
||||
TranslatableComponent chatmessage = new TranslatableComponent("multiplayer.disconnect.invalid_player_data");
|
||||
+ // Paper start
|
||||
+ if (MinecraftServer.getServer().isDebugging()) {
|
||||
+ exception.printStackTrace();
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
this.connection.send(new ClientboundDisconnectPacket(chatmessage));
|
||||
this.connection.disconnect(chatmessage);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 271f4ab919788bfad7ce750b1dafc742debed674..0ea163f4443bc07727675e3c05b9e1d68c52f78a 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -997,6 +997,7 @@ public final class CraftServer implements Server {
|
||||
plugin.getDescription().getFullName(),
|
||||
"This plugin is not properly shutting down its async tasks when it is being reloaded. This may cause conflicts with the newly loaded version of the plugin"
|
||||
));
|
||||
+ if (console.isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread(worker.getThread(), "still running"); // Paper
|
||||
}
|
||||
this.loadPlugins();
|
||||
this.enablePlugins(PluginLoadOrder.STARTUP);
|
|
@ -1,24 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Shane Freeder <theboyetronic@gmail.com>
|
||||
Date: Tue, 3 Mar 2020 05:26:40 +0000
|
||||
Subject: [PATCH] Prevent teleporting dead entities
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 47e841add3437c9ab271569e00b4b385ce2ced66..9f80408a7cf39f1f8b58c688b91de4731208e93d 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -1528,6 +1528,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
}
|
||||
|
||||
private void internalTeleport(double d0, double d1, double d2, float f, float f1, Set<ClientboundPlayerPositionPacket.RelativeArgument> set, boolean flag) {
|
||||
+ // Paper start
|
||||
+ if (player.isRemoved()) {
|
||||
+ LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName());
|
||||
+ if (server.isDebugging()) io.papermc.paper.util.TraceUtil.dumpTraceForThread("Attempt to teleport removed player");
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end
|
||||
// CraftBukkit start
|
||||
if (Float.isNaN(f)) {
|
||||
f = 0;
|
|
@ -1,640 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Sun, 20 Jun 2021 18:19:09 -0700
|
||||
Subject: [PATCH] Deobfuscate stacktraces in log messages, crash reports, and
|
||||
etc.
|
||||
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index 5a609a297ccfa66859b04eb907595741b8ec2160..398bedaba0a006bcd8ad562486ecd583d9d129ef 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -1,4 +1,6 @@
|
||||
+import io.papermc.paperweight.tasks.BaseTask
|
||||
import io.papermc.paperweight.util.*
|
||||
+import java.nio.file.Files
|
||||
|
||||
plugins {
|
||||
java
|
||||
@@ -19,6 +21,7 @@ dependencies {
|
||||
Scanning takes about 1-2 seconds so adding this speeds up the server start.
|
||||
*/
|
||||
implementation("org.apache.logging.log4j:log4j-core:2.14.1") // Paper - implementation
|
||||
+ annotationProcessor("org.apache.logging.log4j:log4j-core:2.14.1") // Paper - Needed to generate meta for our Log4j plugins
|
||||
// Paper end
|
||||
implementation("org.apache.logging.log4j:log4j-iostreams:2.17.1") // Paper
|
||||
implementation("org.ow2.asm:asm:9.2")
|
||||
@@ -31,6 +34,8 @@ dependencies {
|
||||
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.2")
|
||||
runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.2")
|
||||
|
||||
+ implementation("net.fabricmc:mapping-io:0.3.0") // Paper - needed to read mappings for stacktrace deobfuscation
|
||||
+
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
testImplementation("org.hamcrest:hamcrest-library:1.3")
|
||||
}
|
||||
@@ -89,6 +94,45 @@ tasks.shadowJar {
|
||||
}
|
||||
}
|
||||
|
||||
+// Paper start - include reobf mappings in jar for stacktrace deobfuscation
|
||||
+abstract class IncludeMappings : BaseTask() {
|
||||
+ @get:InputFile
|
||||
+ abstract val inputJar: RegularFileProperty
|
||||
+
|
||||
+ @get:InputFile
|
||||
+ abstract val mappings: RegularFileProperty
|
||||
+
|
||||
+ @get:OutputFile
|
||||
+ abstract val outputJar: RegularFileProperty
|
||||
+
|
||||
+ override fun init() {
|
||||
+ super.init()
|
||||
+ outputJar.convention(defaultOutput())
|
||||
+ }
|
||||
+
|
||||
+ @TaskAction
|
||||
+ private fun addMappings() {
|
||||
+ outputJar.get().asFile.parentFile.mkdirs()
|
||||
+ inputJar.get().asFile.copyTo(outputJar.get().asFile, overwrite = true)
|
||||
+ outputJar.get().path.openZip().use { fs ->
|
||||
+ val dir = fs.getPath("META-INF/mappings/")
|
||||
+ Files.createDirectories(dir)
|
||||
+ val target = dir.resolve("reobf.tiny")
|
||||
+ Files.copy(mappings.path, target)
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+val includeMappings = tasks.register<IncludeMappings>("includeMappings") {
|
||||
+ inputJar.set(tasks.fixJarForReobf.flatMap { it.outputJar })
|
||||
+ mappings.set(tasks.reobfJar.flatMap { it.mappingsFile })
|
||||
+}
|
||||
+
|
||||
+tasks.reobfJar {
|
||||
+ inputJar.set(includeMappings.flatMap { it.outputJar })
|
||||
+}
|
||||
+// Paper end - include reobf mappings in jar for stacktrace deobfuscation
|
||||
+
|
||||
tasks.test {
|
||||
exclude("org/bukkit/craftbukkit/inventory/ItemStack*Test.class")
|
||||
}
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
index 8158a00821727104a23958a76e19cd8b01cb4e5a..52ba4797d0f27b8aaac262fd5a33dab63e28ebab 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
@@ -430,4 +430,9 @@ public class PaperConfig {
|
||||
log("Async Chunks: Enabled - Chunks will be loaded much faster, without lag.");
|
||||
}
|
||||
}
|
||||
+
|
||||
+ public static boolean deobfuscateStacktraces = true;
|
||||
+ private static void loggerSettings() {
|
||||
+ deobfuscateStacktraces = getBoolean("settings.loggers.deobfuscate-stacktraces", deobfuscateStacktraces);
|
||||
+ }
|
||||
}
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
|
||||
index 0bb4aaa546939b67a5d22865190f30478a9337c1..d3e619655382e50e9ac9323ed942502d85c9599c 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
|
||||
@@ -91,7 +91,7 @@ public class SyncLoadFinder {
|
||||
|
||||
final JsonArray traces = new JsonArray();
|
||||
|
||||
- for (StackTraceElement element : pair.getFirst().stacktrace) {
|
||||
+ for (StackTraceElement element : io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(pair.getFirst().stacktrace)) {
|
||||
traces.add(String.valueOf(element));
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java b/src/main/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..c701ef3c287f62aa0ebfbdbd6da6ed82a1c7793c
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java
|
||||
@@ -0,0 +1,38 @@
|
||||
+package io.papermc.paper.logging;
|
||||
+
|
||||
+import io.papermc.paper.util.StacktraceDeobfuscator;
|
||||
+import org.apache.logging.log4j.core.Core;
|
||||
+import org.apache.logging.log4j.core.LogEvent;
|
||||
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
|
||||
+import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
||||
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+
|
||||
+@Plugin(
|
||||
+ name = "StacktraceDeobfuscatingRewritePolicy",
|
||||
+ category = Core.CATEGORY_NAME,
|
||||
+ elementType = "rewritePolicy",
|
||||
+ printObject = true
|
||||
+)
|
||||
+public final class StacktraceDeobfuscatingRewritePolicy implements RewritePolicy {
|
||||
+ private StacktraceDeobfuscatingRewritePolicy() {
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NonNull LogEvent rewrite(final @NonNull LogEvent rewrite) {
|
||||
+ final Throwable thrown = rewrite.getThrown();
|
||||
+ if (thrown != null) {
|
||||
+ StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thrown);
|
||||
+ return new Log4jLogEvent.Builder(rewrite)
|
||||
+ .setThrownProxy(null)
|
||||
+ .build();
|
||||
+ }
|
||||
+ return rewrite;
|
||||
+ }
|
||||
+
|
||||
+ @PluginFactory
|
||||
+ public static @NonNull StacktraceDeobfuscatingRewritePolicy createPolicy() {
|
||||
+ return new StacktraceDeobfuscatingRewritePolicy();
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/util/ObfHelper.java b/src/main/java/io/papermc/paper/util/ObfHelper.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..b8b17d046f836c8652ab094db00ab1af84971b2c
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/ObfHelper.java
|
||||
@@ -0,0 +1,146 @@
|
||||
+package io.papermc.paper.util;
|
||||
+
|
||||
+import java.io.IOException;
|
||||
+import java.io.InputStream;
|
||||
+import java.io.InputStreamReader;
|
||||
+import java.nio.charset.StandardCharsets;
|
||||
+import java.util.HashMap;
|
||||
+import java.util.HashSet;
|
||||
+import java.util.Map;
|
||||
+import java.util.Set;
|
||||
+import java.util.function.Function;
|
||||
+import java.util.stream.Collectors;
|
||||
+import net.fabricmc.mappingio.MappingReader;
|
||||
+import net.fabricmc.mappingio.format.MappingFormat;
|
||||
+import net.fabricmc.mappingio.tree.MappingTree;
|
||||
+import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public enum ObfHelper {
|
||||
+ INSTANCE;
|
||||
+
|
||||
+ public static final String MOJANG_PLUS_YARN_NAMESPACE = "mojang+yarn";
|
||||
+ public static final String SPIGOT_NAMESPACE = "spigot";
|
||||
+
|
||||
+ private final @Nullable Map<String, ClassMapping> mappingsByObfName;
|
||||
+ private final @Nullable Map<String, ClassMapping> mappingsByMojangName;
|
||||
+
|
||||
+ ObfHelper() {
|
||||
+ final @Nullable Set<ClassMapping> maps = loadMappingsIfPresent();
|
||||
+ if (maps != null) {
|
||||
+ this.mappingsByObfName = maps.stream().collect(Collectors.toUnmodifiableMap(ClassMapping::obfName, map -> map));
|
||||
+ this.mappingsByMojangName = maps.stream().collect(Collectors.toUnmodifiableMap(ClassMapping::mojangName, map -> map));
|
||||
+ } else {
|
||||
+ this.mappingsByObfName = null;
|
||||
+ this.mappingsByMojangName = null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public @Nullable Map<String, ClassMapping> mappingsByObfName() {
|
||||
+ return this.mappingsByObfName;
|
||||
+ }
|
||||
+
|
||||
+ public @Nullable Map<String, ClassMapping> mappingsByMojangName() {
|
||||
+ return this.mappingsByMojangName;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Attempts to get the obf name for a given class by its Mojang name. Will
|
||||
+ * return the input string if mappings are not present.
|
||||
+ *
|
||||
+ * @param fullyQualifiedMojangName fully qualified class name (dotted)
|
||||
+ * @return mapped or original fully qualified (dotted) class name
|
||||
+ */
|
||||
+ public String reobfClassName(final String fullyQualifiedMojangName) {
|
||||
+ if (this.mappingsByMojangName == null) {
|
||||
+ return fullyQualifiedMojangName;
|
||||
+ }
|
||||
+
|
||||
+ final ClassMapping map = this.mappingsByMojangName.get(fullyQualifiedMojangName);
|
||||
+ if (map == null) {
|
||||
+ return fullyQualifiedMojangName;
|
||||
+ }
|
||||
+
|
||||
+ return map.obfName();
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Attempts to get the Mojang name for a given class by its obf name. Will
|
||||
+ * return the input string if mappings are not present.
|
||||
+ *
|
||||
+ * @param fullyQualifiedObfName fully qualified class name (dotted)
|
||||
+ * @return mapped or original fully qualified (dotted) class name
|
||||
+ */
|
||||
+ public String deobfClassName(final String fullyQualifiedObfName) {
|
||||
+ if (this.mappingsByObfName == null) {
|
||||
+ return fullyQualifiedObfName;
|
||||
+ }
|
||||
+
|
||||
+ final ClassMapping map = this.mappingsByObfName.get(fullyQualifiedObfName);
|
||||
+ if (map == null) {
|
||||
+ return fullyQualifiedObfName;
|
||||
+ }
|
||||
+
|
||||
+ return map.mojangName();
|
||||
+ }
|
||||
+
|
||||
+ private static @Nullable Set<ClassMapping> loadMappingsIfPresent() {
|
||||
+ try (final @Nullable InputStream mappingsInputStream = ObfHelper.class.getClassLoader().getResourceAsStream("META-INF/mappings/reobf.tiny")) {
|
||||
+ if (mappingsInputStream == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ final MemoryMappingTree tree = new MemoryMappingTree();
|
||||
+ MappingReader.read(new InputStreamReader(mappingsInputStream, StandardCharsets.UTF_8), MappingFormat.TINY_2, tree);
|
||||
+ final Set<ClassMapping> classes = new HashSet<>();
|
||||
+
|
||||
+ final StringPool pool = new StringPool();
|
||||
+ for (final MappingTree.ClassMapping cls : tree.getClasses()) {
|
||||
+ final Map<String, String> methods = new HashMap<>();
|
||||
+
|
||||
+ for (final MappingTree.MethodMapping methodMapping : cls.getMethods()) {
|
||||
+ methods.put(
|
||||
+ pool.string(methodKey(
|
||||
+ methodMapping.getName(SPIGOT_NAMESPACE),
|
||||
+ methodMapping.getDesc(SPIGOT_NAMESPACE)
|
||||
+ )),
|
||||
+ pool.string(methodMapping.getName(MOJANG_PLUS_YARN_NAMESPACE))
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ final ClassMapping map = new ClassMapping(
|
||||
+ cls.getName(SPIGOT_NAMESPACE).replace('/', '.'),
|
||||
+ cls.getName(MOJANG_PLUS_YARN_NAMESPACE).replace('/', '.'),
|
||||
+ Map.copyOf(methods)
|
||||
+ );
|
||||
+ classes.add(map);
|
||||
+ }
|
||||
+
|
||||
+ return Set.copyOf(classes);
|
||||
+ } catch (final IOException ex) {
|
||||
+ System.err.println("Failed to load mappings for stacktrace deobfuscation.");
|
||||
+ ex.printStackTrace();
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static String methodKey(final String obfName, final String obfDescriptor) {
|
||||
+ return obfName + obfDescriptor;
|
||||
+ }
|
||||
+
|
||||
+ private static final class StringPool {
|
||||
+ private final Map<String, String> pool = new HashMap<>();
|
||||
+
|
||||
+ public String string(final String string) {
|
||||
+ return this.pool.computeIfAbsent(string, Function.identity());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public record ClassMapping(
|
||||
+ String obfName,
|
||||
+ String mojangName,
|
||||
+ Map<String, String> methodsByObf
|
||||
+ ) {}
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..3c3051c2917548faf7e72276fb642202b33d1794
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
|
||||
@@ -0,0 +1,163 @@
|
||||
+package io.papermc.paper.util;
|
||||
+
|
||||
+import com.destroystokyo.paper.PaperConfig;
|
||||
+import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
+import it.unimi.dsi.fastutil.ints.IntList;
|
||||
+import java.io.IOException;
|
||||
+import java.io.InputStream;
|
||||
+import java.util.Collections;
|
||||
+import java.util.HashMap;
|
||||
+import java.util.LinkedHashMap;
|
||||
+import java.util.Map;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+import org.objectweb.asm.ClassReader;
|
||||
+import org.objectweb.asm.ClassVisitor;
|
||||
+import org.objectweb.asm.Label;
|
||||
+import org.objectweb.asm.MethodVisitor;
|
||||
+import org.objectweb.asm.Opcodes;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public enum StacktraceDeobfuscator {
|
||||
+ INSTANCE;
|
||||
+
|
||||
+ private final Map<Class<?>, Map<String, IntList>> lineMapCache = Collections.synchronizedMap(new LinkedHashMap<>(128, 0.75f, true) {
|
||||
+ @Override
|
||||
+ protected boolean removeEldestEntry(final Map.Entry<Class<?>, Map<String, IntList>> eldest) {
|
||||
+ return this.size() > 127;
|
||||
+ }
|
||||
+ });
|
||||
+
|
||||
+ public void deobfuscateThrowable(final Throwable throwable) {
|
||||
+ if (!PaperConfig.deobfuscateStacktraces) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ throwable.setStackTrace(this.deobfuscateStacktrace(throwable.getStackTrace()));
|
||||
+ final Throwable cause = throwable.getCause();
|
||||
+ if (cause != null) {
|
||||
+ this.deobfuscateThrowable(cause);
|
||||
+ }
|
||||
+ for (final Throwable suppressed : throwable.getSuppressed()) {
|
||||
+ this.deobfuscateThrowable(suppressed);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public StackTraceElement[] deobfuscateStacktrace(final StackTraceElement[] traceElements) {
|
||||
+ if (!PaperConfig.deobfuscateStacktraces) {
|
||||
+ return traceElements;
|
||||
+ }
|
||||
+
|
||||
+ final @Nullable Map<String, ObfHelper.ClassMapping> mappings = ObfHelper.INSTANCE.mappingsByObfName();
|
||||
+ if (mappings == null || traceElements.length == 0) {
|
||||
+ return traceElements;
|
||||
+ }
|
||||
+ final StackTraceElement[] result = new StackTraceElement[traceElements.length];
|
||||
+ for (int i = 0; i < traceElements.length; i++) {
|
||||
+ final StackTraceElement element = traceElements[i];
|
||||
+
|
||||
+ final String className = element.getClassName();
|
||||
+ final String methodName = element.getMethodName();
|
||||
+
|
||||
+ final ObfHelper.ClassMapping classMapping = mappings.get(className);
|
||||
+ if (classMapping == null) {
|
||||
+ result[i] = element;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ final Class<?> clazz;
|
||||
+ try {
|
||||
+ clazz = Class.forName(className);
|
||||
+ } catch (final ClassNotFoundException ex) {
|
||||
+ throw new RuntimeException(ex);
|
||||
+ }
|
||||
+ final @Nullable String methodKey = this.determineMethodForLine(clazz, element.getLineNumber());
|
||||
+ final @Nullable String mappedMethodName = methodKey == null ? null : classMapping.methodsByObf().get(methodKey);
|
||||
+
|
||||
+ result[i] = new StackTraceElement(
|
||||
+ element.getClassLoaderName(),
|
||||
+ element.getModuleName(),
|
||||
+ element.getModuleVersion(),
|
||||
+ classMapping.mojangName(),
|
||||
+ mappedMethodName != null ? mappedMethodName : methodName,
|
||||
+ sourceFileName(classMapping.mojangName()),
|
||||
+ element.getLineNumber()
|
||||
+ );
|
||||
+ }
|
||||
+ return result;
|
||||
+ }
|
||||
+
|
||||
+ private @Nullable String determineMethodForLine(final Class<?> clazz, final int lineNumber) {
|
||||
+ final Map<String, IntList> lineMap = this.lineMapCache.computeIfAbsent(clazz, StacktraceDeobfuscator::buildLineMap);
|
||||
+ for (final var entry : lineMap.entrySet()) {
|
||||
+ final String methodKey = entry.getKey();
|
||||
+ final IntList lines = entry.getValue();
|
||||
+ for (int i = 0, linesSize = lines.size(); i < linesSize; i++) {
|
||||
+ final int num = lines.getInt(i);
|
||||
+ if (num == lineNumber) {
|
||||
+ return methodKey;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ private static String sourceFileName(final String fullClassName) {
|
||||
+ final int dot = fullClassName.lastIndexOf('.');
|
||||
+ final String className = dot == -1
|
||||
+ ? fullClassName
|
||||
+ : fullClassName.substring(dot + 1);
|
||||
+ final String rootClassName = className.split("\\$")[0];
|
||||
+ return rootClassName + ".java";
|
||||
+ }
|
||||
+
|
||||
+ private static Map<String, IntList> buildLineMap(final Class<?> key) {
|
||||
+ final Map<String, IntList> lineMap = new HashMap<>();
|
||||
+ final class LineCollectingMethodVisitor extends MethodVisitor {
|
||||
+ private final IntList lines = new IntArrayList();
|
||||
+ private final String name;
|
||||
+ private final String descriptor;
|
||||
+
|
||||
+ LineCollectingMethodVisitor(String name, String descriptor) {
|
||||
+ super(Opcodes.ASM9);
|
||||
+ this.name = name;
|
||||
+ this.descriptor = descriptor;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void visitLineNumber(int line, Label start) {
|
||||
+ super.visitLineNumber(line, start);
|
||||
+ this.lines.add(line);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void visitEnd() {
|
||||
+ super.visitEnd();
|
||||
+ lineMap.put(ObfHelper.methodKey(this.name, this.descriptor), this.lines);
|
||||
+ }
|
||||
+ }
|
||||
+ final ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM9) {
|
||||
+ @Override
|
||||
+ public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
+ return new LineCollectingMethodVisitor(name, descriptor);
|
||||
+ }
|
||||
+ };
|
||||
+ try {
|
||||
+ final @Nullable InputStream inputStream = StacktraceDeobfuscator.class.getClassLoader()
|
||||
+ .getResourceAsStream(key.getName().replace('.', '/') + ".class");
|
||||
+ if (inputStream == null) {
|
||||
+ throw new IllegalStateException("Could not find class file: " + key.getName());
|
||||
+ }
|
||||
+ final byte[] classData;
|
||||
+ try (inputStream) {
|
||||
+ classData = inputStream.readAllBytes();
|
||||
+ }
|
||||
+ final ClassReader reader = new ClassReader(classData);
|
||||
+ reader.accept(classVisitor, 0);
|
||||
+ } catch (final IOException ex) {
|
||||
+ throw new RuntimeException(ex);
|
||||
+ }
|
||||
+ return lineMap;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/util/TraceUtil.java b/src/main/java/io/papermc/paper/util/TraceUtil.java
|
||||
index 2d5494d2813b773e60ddba6790b750a9a08f21f8..7695bf44503f161523ea612ef8a884ae574a2e21 100644
|
||||
--- a/src/main/java/io/papermc/paper/util/TraceUtil.java
|
||||
+++ b/src/main/java/io/papermc/paper/util/TraceUtil.java
|
||||
@@ -6,13 +6,15 @@ public final class TraceUtil {
|
||||
|
||||
public static void dumpTraceForThread(Thread thread, String reason) {
|
||||
Bukkit.getLogger().warning(thread.getName() + ": " + reason);
|
||||
- StackTraceElement[] trace = thread.getStackTrace();
|
||||
+ StackTraceElement[] trace = StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(thread.getStackTrace());
|
||||
for (StackTraceElement traceElement : trace) {
|
||||
Bukkit.getLogger().warning("\tat " + traceElement);
|
||||
}
|
||||
}
|
||||
|
||||
public static void dumpTraceForThread(String reason) {
|
||||
- new Throwable(reason).printStackTrace();
|
||||
+ final Throwable throwable = new Throwable(reason);
|
||||
+ StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(throwable);
|
||||
+ throwable.printStackTrace();
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java
|
||||
index 4dd14d73a37b32288a64fbd67ee22c435b6e6d57..1a859fef0848cf23a672012e9764965ae1c07ec5 100644
|
||||
--- a/src/main/java/net/minecraft/CrashReport.java
|
||||
+++ b/src/main/java/net/minecraft/CrashReport.java
|
||||
@@ -30,6 +30,7 @@ public class CrashReport {
|
||||
private final SystemReport systemReport = new SystemReport();
|
||||
|
||||
public CrashReport(String message, Throwable cause) {
|
||||
+ io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(cause); // Paper
|
||||
this.title = message;
|
||||
this.exception = cause;
|
||||
this.systemReport.setDetail("CraftBukkit Information", new org.bukkit.craftbukkit.CraftCrashReport()); // CraftBukkit
|
||||
diff --git a/src/main/java/net/minecraft/CrashReportCategory.java b/src/main/java/net/minecraft/CrashReportCategory.java
|
||||
index 3941e14d1c3e6e688e28904948039c8b2200de5f..a4fda4a3bae9ce600e778b44cd3ef432a8b65667 100644
|
||||
--- a/src/main/java/net/minecraft/CrashReportCategory.java
|
||||
+++ b/src/main/java/net/minecraft/CrashReportCategory.java
|
||||
@@ -104,6 +104,7 @@ public class CrashReportCategory {
|
||||
} else {
|
||||
this.stackTrace = new StackTraceElement[stackTraceElements.length - 3 - ignoredCallCount];
|
||||
System.arraycopy(stackTraceElements, 3 + ignoredCallCount, this.stackTrace, 0, this.stackTrace.length);
|
||||
+ this.stackTrace = io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(this.stackTrace); // Paper
|
||||
return this.stackTrace.length;
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
||||
index a0d3d9e344f530b713167d0663634c40eefce5b5..c469688c2b55b1c93435a2ac6e8e4494ab7e5229 100644
|
||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
||||
@@ -66,13 +66,13 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
});
|
||||
public static final AttributeKey<ConnectionProtocol> ATTRIBUTE_PROTOCOL = AttributeKey.valueOf("protocol");
|
||||
public static final LazyLoadedValue<NioEventLoopGroup> NETWORK_WORKER_GROUP = new LazyLoadedValue<>(() -> {
|
||||
- return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
|
||||
+ return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
public static final LazyLoadedValue<EpollEventLoopGroup> NETWORK_EPOLL_WORKER_GROUP = new LazyLoadedValue<>(() -> {
|
||||
- return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build());
|
||||
+ return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
public static final LazyLoadedValue<DefaultEventLoopGroup> LOCAL_WORKER_GROUP = new LazyLoadedValue<>(() -> {
|
||||
- return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
|
||||
+ return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
private final PacketFlow receiving;
|
||||
private final Queue<Connection.PacketHolder> queue = Queues.newConcurrentLinkedQueue();
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/game/ServerboundChatPacket.java b/src/main/java/net/minecraft/network/protocol/game/ServerboundChatPacket.java
|
||||
index 5165170e3289fe3939497c56a71eae9fdaa4bcfa..8a0ced3f9b9099913ade4b71181aff6cafbc4ee6 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/game/ServerboundChatPacket.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/game/ServerboundChatPacket.java
|
||||
@@ -27,7 +27,7 @@ public class ServerboundChatPacket implements Packet<ServerGamePacketListener> {
|
||||
|
||||
// Spigot Start
|
||||
private static final java.util.concurrent.ExecutorService executors = java.util.concurrent.Executors.newCachedThreadPool(
|
||||
- new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon( true ).setNameFormat( "Async Chat Thread - #%d" ).build() );
|
||||
+ new com.google.common.util.concurrent.ThreadFactoryBuilder().setDaemon( true ).setNameFormat( "Async Chat Thread - #%d" ).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build() ); // Paper
|
||||
public void handle(final ServerGamePacketListener listener) {
|
||||
if ( !this.message.startsWith("/") )
|
||||
{
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index d0d058287fc1e87e944e99d4fc2cbd38eafb82b3..cec0082718c2a729044d6f19d74b8e4425816725 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -215,6 +215,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
}
|
||||
com.destroystokyo.paper.PaperConfig.registerCommands();
|
||||
com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now
|
||||
+ io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // load mappings for stacktrace deobf and etc.
|
||||
// Paper end
|
||||
|
||||
this.setPvpAllowed(dedicatedserverproperties.pvp);
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
index 7c820a8f9ae85eb6941dce0e6296775fb331d720..888e863b6d605bf587d0c071795a43d2157afa4d 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
@@ -52,10 +52,10 @@ public class ServerConnectionListener {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
public static final LazyLoadedValue<NioEventLoopGroup> SERVER_EVENT_GROUP = new LazyLoadedValue<>(() -> {
|
||||
- return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).build());
|
||||
+ return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
public static final LazyLoadedValue<EpollEventLoopGroup> SERVER_EPOLL_EVENT_GROUP = new LazyLoadedValue<>(() -> {
|
||||
- return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).build());
|
||||
+ return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
final MinecraftServer server;
|
||||
public volatile boolean running;
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
|
||||
index 3c1992e212a6d6f1db4d5b807b38d71913619fc0..9c1aff17aabd062640e3f451a2ef8c50a7c62f10 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
|
||||
@@ -40,9 +40,9 @@ 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());
|
||||
+ new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); // Paper
|
||||
private final Executor management = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
|
||||
- .setNameFormat("Craft Async Scheduler Management Thread").build());
|
||||
+ .setNameFormat("Craft Async Scheduler Management Thread").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); // Paper
|
||||
private final List<CraftTask> temp = new ArrayList<>();
|
||||
|
||||
CraftAsyncScheduler() {
|
||||
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
index 4d271cae88c16ed2419f896c728fdff612540500..dcfbe77bdb25d9c58ffb7b75c48bdb580bc0de47 100644
|
||||
--- a/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
@@ -106,7 +106,7 @@ public class WatchdogThread extends Thread
|
||||
log.log( Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity" );
|
||||
log.log( Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated" );
|
||||
log.log( Level.SEVERE, org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getMessage());
|
||||
- for ( StackTraceElement stack : org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace() )
|
||||
+ for ( StackTraceElement stack : io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace()) ) // Paper
|
||||
{
|
||||
log.log( Level.SEVERE, "\t\t" + stack );
|
||||
}
|
||||
@@ -194,7 +194,7 @@ public class WatchdogThread extends Thread
|
||||
}
|
||||
log.log( Level.SEVERE, "\tStack:" );
|
||||
//
|
||||
- for ( StackTraceElement stack : thread.getStackTrace() )
|
||||
+ for ( StackTraceElement stack : io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(thread.getStackTrace()) ) // Paper
|
||||
{
|
||||
log.log( Level.SEVERE, "\t\t" + stack );
|
||||
}
|
||||
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
||||
index d285dbec16272db6b8a71865e05924ad66087407..1a05d23ff886b015fb9396f119822c678a47ec6f 100644
|
||||
--- a/src/main/resources/log4j2.xml
|
||||
+++ b/src/main/resources/log4j2.xml
|
||||
@@ -30,10 +30,14 @@
|
||||
<DefaultRolloverStrategy max="1000"/>
|
||||
</RollingRandomAccessFile>
|
||||
<Async name="Async">
|
||||
+ <AppenderRef ref="rewrite"/>
|
||||
+ </Async>
|
||||
+ <Rewrite name="rewrite">
|
||||
+ <StacktraceDeobfuscatingRewritePolicy />
|
||||
<AppenderRef ref="File"/>
|
||||
<AppenderRef ref="TerminalConsole" level="info"/>
|
||||
<AppenderRef ref="ServerGuiConsole" level="info"/>
|
||||
- </Async>
|
||||
+ </Rewrite>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="info">
|
|
@ -1,911 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MiniDigger <admin@benndorf.dev>
|
||||
Date: Fri, 3 Jan 2020 16:26:19 +0100
|
||||
Subject: [PATCH] Implement Mob Goal API
|
||||
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index 398bedaba0a006bcd8ad562486ecd583d9d129ef..6b46f9c94dc051579ca5b51755caad19505e874a 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -36,6 +36,7 @@ dependencies {
|
||||
|
||||
implementation("net.fabricmc:mapping-io:0.3.0") // Paper - needed to read mappings for stacktrace deobfuscation
|
||||
|
||||
+ testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
testImplementation("org.hamcrest:hamcrest-library:1.3")
|
||||
}
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..181abe014baba9ac51064c003381281a8fa43fe4
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
||||
@@ -0,0 +1,367 @@
|
||||
+package com.destroystokyo.paper.entity.ai;
|
||||
+
|
||||
+import com.destroystokyo.paper.entity.RangedEntity;
|
||||
+import com.destroystokyo.paper.util.set.OptimizedSmallEnumSet;
|
||||
+import com.google.common.collect.BiMap;
|
||||
+import com.google.common.collect.HashBiMap;
|
||||
+import io.papermc.paper.util.ObfHelper;
|
||||
+import java.lang.reflect.Constructor;
|
||||
+import java.util.EnumSet;
|
||||
+import java.util.HashMap;
|
||||
+import java.util.HashSet;
|
||||
+import java.util.Map;
|
||||
+import java.util.Set;
|
||||
+import net.minecraft.world.entity.FlyingMob;
|
||||
+import net.minecraft.world.entity.PathfinderMob;
|
||||
+import net.minecraft.world.entity.TamableAnimal;
|
||||
+import net.minecraft.world.entity.ai.goal.Goal;
|
||||
+import net.minecraft.world.entity.ambient.AmbientCreature;
|
||||
+import net.minecraft.world.entity.animal.AbstractFish;
|
||||
+import net.minecraft.world.entity.animal.AbstractGolem;
|
||||
+import net.minecraft.world.entity.animal.AbstractSchoolingFish;
|
||||
+import net.minecraft.world.entity.animal.Animal;
|
||||
+import net.minecraft.world.entity.animal.Pufferfish;
|
||||
+import net.minecraft.world.entity.animal.ShoulderRidingEntity;
|
||||
+import net.minecraft.world.entity.animal.SnowGolem;
|
||||
+import net.minecraft.world.entity.animal.WaterAnimal;
|
||||
+import net.minecraft.world.entity.animal.horse.AbstractChestedHorse;
|
||||
+import net.minecraft.world.entity.boss.wither.WitherBoss;
|
||||
+import net.minecraft.world.entity.monster.AbstractIllager;
|
||||
+import net.minecraft.world.entity.monster.EnderMan;
|
||||
+import net.minecraft.world.entity.monster.PatrollingMonster;
|
||||
+import net.minecraft.world.entity.monster.RangedAttackMob;
|
||||
+import net.minecraft.world.entity.monster.SpellcasterIllager;
|
||||
+import net.minecraft.world.entity.monster.ZombifiedPiglin;
|
||||
+import net.minecraft.world.entity.monster.piglin.AbstractPiglin;
|
||||
+import org.bukkit.NamespacedKey;
|
||||
+import org.bukkit.entity.AbstractHorse;
|
||||
+import org.bukkit.entity.AbstractSkeleton;
|
||||
+import org.bukkit.entity.AbstractVillager;
|
||||
+import org.bukkit.entity.Ageable;
|
||||
+import org.bukkit.entity.Ambient;
|
||||
+import org.bukkit.entity.Animals;
|
||||
+import org.bukkit.entity.Bat;
|
||||
+import org.bukkit.entity.Bee;
|
||||
+import org.bukkit.entity.Blaze;
|
||||
+import org.bukkit.entity.Cat;
|
||||
+import org.bukkit.entity.CaveSpider;
|
||||
+import org.bukkit.entity.ChestedHorse;
|
||||
+import org.bukkit.entity.Chicken;
|
||||
+import org.bukkit.entity.Cod;
|
||||
+import org.bukkit.entity.Cow;
|
||||
+import org.bukkit.entity.Creature;
|
||||
+import org.bukkit.entity.Creeper;
|
||||
+import org.bukkit.entity.Dolphin;
|
||||
+import org.bukkit.entity.Donkey;
|
||||
+import org.bukkit.entity.Drowned;
|
||||
+import org.bukkit.entity.ElderGuardian;
|
||||
+import org.bukkit.entity.EnderDragon;
|
||||
+import org.bukkit.entity.Enderman;
|
||||
+import org.bukkit.entity.Endermite;
|
||||
+import org.bukkit.entity.Evoker;
|
||||
+import org.bukkit.entity.Fish;
|
||||
+import org.bukkit.entity.Flying;
|
||||
+import org.bukkit.entity.Fox;
|
||||
+import org.bukkit.entity.Ghast;
|
||||
+import org.bukkit.entity.Giant;
|
||||
+import org.bukkit.entity.Golem;
|
||||
+import org.bukkit.entity.Guardian;
|
||||
+import org.bukkit.entity.Hoglin;
|
||||
+import org.bukkit.entity.Horse;
|
||||
+import org.bukkit.entity.Husk;
|
||||
+import org.bukkit.entity.Illager;
|
||||
+import org.bukkit.entity.Illusioner;
|
||||
+import org.bukkit.entity.IronGolem;
|
||||
+import org.bukkit.entity.Llama;
|
||||
+import org.bukkit.entity.MagmaCube;
|
||||
+import org.bukkit.entity.Mob;
|
||||
+import org.bukkit.entity.Monster;
|
||||
+import org.bukkit.entity.Mule;
|
||||
+import org.bukkit.entity.MushroomCow;
|
||||
+import org.bukkit.entity.Ocelot;
|
||||
+import org.bukkit.entity.Panda;
|
||||
+import org.bukkit.entity.Parrot;
|
||||
+import org.bukkit.entity.Phantom;
|
||||
+import org.bukkit.entity.Pig;
|
||||
+import org.bukkit.entity.PigZombie;
|
||||
+import org.bukkit.entity.Piglin;
|
||||
+import org.bukkit.entity.PiglinAbstract;
|
||||
+import org.bukkit.entity.PiglinBrute;
|
||||
+import org.bukkit.entity.Pillager;
|
||||
+import org.bukkit.entity.PolarBear;
|
||||
+import org.bukkit.entity.PufferFish;
|
||||
+import org.bukkit.entity.Rabbit;
|
||||
+import org.bukkit.entity.Raider;
|
||||
+import org.bukkit.entity.Ravager;
|
||||
+import org.bukkit.entity.Salmon;
|
||||
+import org.bukkit.entity.Sheep;
|
||||
+import org.bukkit.entity.Shulker;
|
||||
+import org.bukkit.entity.Silverfish;
|
||||
+import org.bukkit.entity.Skeleton;
|
||||
+import org.bukkit.entity.SkeletonHorse;
|
||||
+import org.bukkit.entity.Slime;
|
||||
+import org.bukkit.entity.Snowman;
|
||||
+import org.bukkit.entity.Spellcaster;
|
||||
+import org.bukkit.entity.Spider;
|
||||
+import org.bukkit.entity.Squid;
|
||||
+import org.bukkit.entity.Stray;
|
||||
+import org.bukkit.entity.Strider;
|
||||
+import org.bukkit.entity.Tameable;
|
||||
+import org.bukkit.entity.TraderLlama;
|
||||
+import org.bukkit.entity.TropicalFish;
|
||||
+import org.bukkit.entity.Turtle;
|
||||
+import org.bukkit.entity.Vex;
|
||||
+import org.bukkit.entity.Villager;
|
||||
+import org.bukkit.entity.Vindicator;
|
||||
+import org.bukkit.entity.WanderingTrader;
|
||||
+import org.bukkit.entity.WaterMob;
|
||||
+import org.bukkit.entity.Witch;
|
||||
+import org.bukkit.entity.Wither;
|
||||
+import org.bukkit.entity.WitherSkeleton;
|
||||
+import org.bukkit.entity.Wolf;
|
||||
+import org.bukkit.entity.Zoglin;
|
||||
+import org.bukkit.entity.Zombie;
|
||||
+import org.bukkit.entity.ZombieHorse;
|
||||
+import org.bukkit.entity.ZombieVillager;
|
||||
+
|
||||
+public class MobGoalHelper {
|
||||
+
|
||||
+ private static final BiMap<String, String> deobfuscationMap = HashBiMap.create();
|
||||
+ private static final Map<Class<? extends Goal>, Class<? extends Mob>> entityClassCache = new HashMap<>();
|
||||
+ private static final Map<Class<? extends net.minecraft.world.entity.Mob>, Class<? extends Mob>> bukkitMap = new HashMap<>();
|
||||
+
|
||||
+ static final Set<String> ignored = new HashSet<>();
|
||||
+
|
||||
+ static {
|
||||
+ // TODO these kinda should be checked on each release, in case obfuscation changes
|
||||
+ deobfuscationMap.put("abstract_skeleton_1", "abstract_skeleton_melee");
|
||||
+
|
||||
+ ignored.add("goal_selector_1");
|
||||
+ ignored.add("goal_selector_2");
|
||||
+ ignored.add("selector_1");
|
||||
+ ignored.add("selector_2");
|
||||
+ ignored.add("wrapped");
|
||||
+
|
||||
+ bukkitMap.put(net.minecraft.world.entity.Mob.class, Mob.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.AgeableMob.class, Ageable.class);
|
||||
+ bukkitMap.put(AmbientCreature.class, Ambient.class);
|
||||
+ bukkitMap.put(Animal.class, Animals.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.ambient.Bat.class, Bat.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Bee.class, Bee.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Blaze.class, Blaze.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Cat.class, Cat.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.CaveSpider.class, CaveSpider.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Chicken.class, Chicken.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Cod.class, Cod.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Cow.class, Cow.class);
|
||||
+ bukkitMap.put(PathfinderMob.class, Creature.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Creeper.class, Creeper.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Dolphin.class, Dolphin.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Drowned.class, Drowned.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.boss.enderdragon.EnderDragon.class, EnderDragon.class);
|
||||
+ bukkitMap.put(EnderMan.class, Enderman.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Endermite.class, Endermite.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Evoker.class, Evoker.class);
|
||||
+ bukkitMap.put(AbstractFish.class, Fish.class);
|
||||
+ bukkitMap.put(AbstractSchoolingFish.class, Fish.class); // close enough
|
||||
+ bukkitMap.put(FlyingMob.class, Flying.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Fox.class, Fox.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Ghast.class, Ghast.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Giant.class, Giant.class);
|
||||
+ bukkitMap.put(AbstractGolem.class, Golem.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Guardian.class, Guardian.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.ElderGuardian.class, ElderGuardian.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.Horse.class, Horse.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.AbstractHorse.class, AbstractHorse.class);
|
||||
+ bukkitMap.put(AbstractChestedHorse.class, ChestedHorse.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.Donkey.class, Donkey.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.Mule.class, Mule.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.SkeletonHorse.class, SkeletonHorse.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.ZombieHorse.class, ZombieHorse.class);
|
||||
+ bukkitMap.put(AbstractIllager.class, Illager.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Illusioner.class, Illusioner.class);
|
||||
+ bukkitMap.put(SpellcasterIllager.class, Spellcaster.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.IronGolem.class, IronGolem.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.Llama.class, Llama.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.horse.TraderLlama.class, TraderLlama.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.MagmaCube.class, MagmaCube.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Monster.class, Monster.class);
|
||||
+ bukkitMap.put(PatrollingMonster.class, Raider.class); // close enough
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.MushroomCow.class, MushroomCow.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Ocelot.class, Ocelot.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Panda.class, Panda.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Parrot.class, Parrot.class);
|
||||
+ bukkitMap.put(ShoulderRidingEntity.class, Parrot.class); // close enough
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Phantom.class, Phantom.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Pig.class, Pig.class);
|
||||
+ bukkitMap.put(ZombifiedPiglin.class, PigZombie.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Pillager.class, Pillager.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.PolarBear.class, PolarBear.class);
|
||||
+ bukkitMap.put(Pufferfish.class, PufferFish.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Rabbit.class, Rabbit.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.raid.Raider.class, Raider.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Ravager.class, Ravager.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Salmon.class, Salmon.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Sheep.class, Sheep.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Shulker.class, Shulker.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Silverfish.class, Silverfish.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Skeleton.class, Skeleton.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.AbstractSkeleton.class, AbstractSkeleton.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Stray.class, Stray.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.WitherSkeleton.class, WitherSkeleton.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Slime.class, Slime.class);
|
||||
+ bukkitMap.put(SnowGolem.class, Snowman.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Spider.class, Spider.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Squid.class, Squid.class);
|
||||
+ bukkitMap.put(TamableAnimal.class, Tameable.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.TropicalFish.class, TropicalFish.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Turtle.class, Turtle.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Vex.class, Vex.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.npc.Villager.class, Villager.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.npc.AbstractVillager.class, AbstractVillager.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.npc.WanderingTrader.class, WanderingTrader.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Vindicator.class, Vindicator.class);
|
||||
+ bukkitMap.put(WaterAnimal.class, WaterMob.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Witch.class, Witch.class);
|
||||
+ bukkitMap.put(WitherBoss.class, Wither.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.Wolf.class, Wolf.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Zombie.class, Zombie.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Husk.class, Husk.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.ZombieVillager.class, ZombieVillager.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.hoglin.Hoglin.class, Hoglin.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.piglin.Piglin.class, Piglin.class);
|
||||
+ bukkitMap.put(AbstractPiglin.class, PiglinAbstract.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.piglin.PiglinBrute.class, PiglinBrute.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Strider.class, Strider.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.monster.Zoglin.class, Zoglin.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.GlowSquid.class, org.bukkit.entity.GlowSquid.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.axolotl.Axolotl.class, org.bukkit.entity.Axolotl.class);
|
||||
+ bukkitMap.put(net.minecraft.world.entity.animal.goat.Goat.class, org.bukkit.entity.Goat.class);
|
||||
+ }
|
||||
+
|
||||
+ public static String getUsableName(Class<?> clazz) {
|
||||
+ String name = ObfHelper.INSTANCE.deobfClassName(clazz.getName());
|
||||
+ name = name.substring(name.lastIndexOf(".") + 1);
|
||||
+ boolean flag = false;
|
||||
+ // inner classes
|
||||
+ if (name.contains("$")) {
|
||||
+ String cut = name.substring(name.indexOf("$") + 1);
|
||||
+ if (cut.length() <= 2) {
|
||||
+ name = name.replace("Entity", "");
|
||||
+ name = name.replace("$", "_");
|
||||
+ flag = true;
|
||||
+ } else {
|
||||
+ // mapped, wooo
|
||||
+ name = cut;
|
||||
+ }
|
||||
+ }
|
||||
+ name = name.replace("PathfinderGoal", "");
|
||||
+ name = name.replace("TargetGoal", "");
|
||||
+ name = name.replace("Goal", "");
|
||||
+ StringBuilder sb = new StringBuilder();
|
||||
+ for (char c : name.toCharArray()) {
|
||||
+ if (c >= 'A' && c <= 'Z') {
|
||||
+ sb.append("_");
|
||||
+ sb.append(Character.toLowerCase(c));
|
||||
+ } else {
|
||||
+ sb.append(c);
|
||||
+ }
|
||||
+ }
|
||||
+ name = sb.toString();
|
||||
+ name = name.replaceFirst("_", "");
|
||||
+
|
||||
+ if (flag && !deobfuscationMap.containsKey(name.toLowerCase()) && !ignored.contains(name)) {
|
||||
+ System.out.println("need to map " + clazz.getName() + " (" + name.toLowerCase() + ")");
|
||||
+ }
|
||||
+
|
||||
+ // did we rename this key?
|
||||
+ return deobfuscationMap.getOrDefault(name, name);
|
||||
+ }
|
||||
+
|
||||
+ public static EnumSet<GoalType> vanillaToPaper(OptimizedSmallEnumSet<Goal.Flag> types) {
|
||||
+ EnumSet<GoalType> goals = EnumSet.noneOf(GoalType.class);
|
||||
+ for (GoalType type : GoalType.values()) {
|
||||
+ if (types.hasElement(paperToVanilla(type))) {
|
||||
+ goals.add(type);
|
||||
+ }
|
||||
+ }
|
||||
+ return goals;
|
||||
+ }
|
||||
+
|
||||
+ public static GoalType vanillaToPaper(Goal.Flag type) {
|
||||
+ switch (type) {
|
||||
+ case MOVE:
|
||||
+ return GoalType.MOVE;
|
||||
+ case LOOK:
|
||||
+ return GoalType.LOOK;
|
||||
+ case JUMP:
|
||||
+ return GoalType.JUMP;
|
||||
+ case UNKNOWN_BEHAVIOR:
|
||||
+ return GoalType.UNKNOWN_BEHAVIOR;
|
||||
+ case TARGET:
|
||||
+ return GoalType.TARGET;
|
||||
+ default:
|
||||
+ throw new IllegalArgumentException("Unknown vanilla mob goal type " + type.name());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static EnumSet<Goal.Flag> paperToVanilla(EnumSet<GoalType> types) {
|
||||
+ EnumSet<Goal.Flag> goals = EnumSet.noneOf(Goal.Flag.class);
|
||||
+ for (GoalType type : types) {
|
||||
+ goals.add(paperToVanilla(type));
|
||||
+ }
|
||||
+ return goals;
|
||||
+ }
|
||||
+
|
||||
+ public static Goal.Flag paperToVanilla(GoalType type) {
|
||||
+ switch (type) {
|
||||
+ case MOVE:
|
||||
+ return Goal.Flag.MOVE;
|
||||
+ case LOOK:
|
||||
+ return Goal.Flag.LOOK;
|
||||
+ case JUMP:
|
||||
+ return Goal.Flag.JUMP;
|
||||
+ case UNKNOWN_BEHAVIOR:
|
||||
+ return Goal.Flag.UNKNOWN_BEHAVIOR;
|
||||
+ case TARGET:
|
||||
+ return Goal.Flag.TARGET;
|
||||
+ default:
|
||||
+ throw new IllegalArgumentException("Unknown paper mob goal type " + type.name());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static <T extends Mob> GoalKey<T> getKey(Class<? extends Goal> goalClass) {
|
||||
+ String name = getUsableName(goalClass);
|
||||
+ if (ignored.contains(name)) {
|
||||
+ //noinspection unchecked
|
||||
+ return (GoalKey<T>) GoalKey.of(Mob.class, NamespacedKey.minecraft(name));
|
||||
+ }
|
||||
+ return GoalKey.of(getEntity(goalClass), NamespacedKey.minecraft(name));
|
||||
+ }
|
||||
+
|
||||
+ public static <T extends Mob> Class<T> getEntity(Class<? extends Goal> goalClass) {
|
||||
+ //noinspection unchecked
|
||||
+ return (Class<T>) entityClassCache.computeIfAbsent(goalClass, key -> {
|
||||
+ for (Constructor<?> ctor : key.getDeclaredConstructors()) {
|
||||
+ for (int i = 0; i < ctor.getParameterCount(); i++) {
|
||||
+ Class<?> param = ctor.getParameterTypes()[i];
|
||||
+ if (net.minecraft.world.entity.Mob.class.isAssignableFrom(param)) {
|
||||
+ //noinspection unchecked
|
||||
+ return toBukkitClass((Class<? extends net.minecraft.world.entity.Mob>) param);
|
||||
+ } else if (RangedAttackMob.class.isAssignableFrom(param)) {
|
||||
+ return RangedEntity.class;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ throw new RuntimeException("Can't figure out applicable entity for mob goal " + goalClass); // maybe just return EntityInsentient?
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ public static Class<? extends Mob> toBukkitClass(Class<? extends net.minecraft.world.entity.Mob> nmsClass) {
|
||||
+ Class<? extends Mob> bukkitClass = bukkitMap.get(nmsClass);
|
||||
+ if (bukkitClass == null) {
|
||||
+ throw new RuntimeException("Can't figure out applicable bukkit entity for nms entity " + nmsClass); // maybe just return Mob?
|
||||
+ }
|
||||
+ return bukkitClass;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/PaperCustomGoal.java b/src/main/java/com/destroystokyo/paper/entity/ai/PaperCustomGoal.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..26c745dd9ccdfdd5c5039f2acc5201b9b91fb274
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/PaperCustomGoal.java
|
||||
@@ -0,0 +1,53 @@
|
||||
+package com.destroystokyo.paper.entity.ai;
|
||||
+
|
||||
+import org.bukkit.entity.Mob;
|
||||
+
|
||||
+/**
|
||||
+ * Wraps api in vanilla
|
||||
+ */
|
||||
+public class PaperCustomGoal<T extends Mob> extends net.minecraft.world.entity.ai.goal.Goal {
|
||||
+
|
||||
+ private final Goal<T> handle;
|
||||
+
|
||||
+ public PaperCustomGoal(Goal<T> handle) {
|
||||
+ this.handle = handle;
|
||||
+
|
||||
+ this.setFlags(MobGoalHelper.paperToVanilla(handle.getTypes()));
|
||||
+ if (this.getFlags().size() == 0) {
|
||||
+ this.getFlags().addUnchecked(Flag.UNKNOWN_BEHAVIOR);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean canUse() {
|
||||
+ return handle.shouldActivate();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean canContinueToUse() {
|
||||
+ return handle.shouldStayActive();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void start() {
|
||||
+ handle.start();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void stop() {
|
||||
+ handle.stop();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void tick() {
|
||||
+ handle.tick();
|
||||
+ }
|
||||
+
|
||||
+ public Goal<T> getHandle() {
|
||||
+ return handle;
|
||||
+ }
|
||||
+
|
||||
+ public GoalKey<T> getKey() {
|
||||
+ return handle.getKey();
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/PaperMobGoals.java b/src/main/java/com/destroystokyo/paper/entity/ai/PaperMobGoals.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..336cc3c3b43bacf4f3661fa0bb0736b273f65418
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/PaperMobGoals.java
|
||||
@@ -0,0 +1,213 @@
|
||||
+package com.destroystokyo.paper.entity.ai;
|
||||
+
|
||||
+import java.util.Collection;
|
||||
+import java.util.EnumSet;
|
||||
+import java.util.HashSet;
|
||||
+import java.util.LinkedList;
|
||||
+import java.util.List;
|
||||
+import java.util.Set;
|
||||
+import net.minecraft.world.entity.ai.goal.GoalSelector;
|
||||
+import net.minecraft.world.entity.ai.goal.WrappedGoal;
|
||||
+import org.bukkit.craftbukkit.entity.CraftMob;
|
||||
+import org.bukkit.entity.Mob;
|
||||
+
|
||||
+public class PaperMobGoals implements MobGoals {
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> void addGoal(T mob, int priority, Goal<T> goal) {
|
||||
+ CraftMob craftMob = (CraftMob) mob;
|
||||
+ getHandle(craftMob, goal.getTypes()).addGoal(priority, new PaperCustomGoal<>(goal));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> void removeGoal(T mob, Goal<T> goal) {
|
||||
+ CraftMob craftMob = (CraftMob) mob;
|
||||
+ if (goal instanceof PaperCustomGoal) {
|
||||
+ getHandle(craftMob, goal.getTypes()).removeGoal((net.minecraft.world.entity.ai.goal.Goal) goal);
|
||||
+ } else if (goal instanceof PaperVanillaGoal) {
|
||||
+ getHandle(craftMob, goal.getTypes()).removeGoal(((PaperVanillaGoal<?>) goal).getHandle());
|
||||
+ } else {
|
||||
+ List<net.minecraft.world.entity.ai.goal.Goal> toRemove = new LinkedList<>();
|
||||
+ for (WrappedGoal item : getHandle(craftMob, goal.getTypes()).availableGoals) {
|
||||
+ if (item.getGoal() instanceof PaperCustomGoal) {
|
||||
+ //noinspection unchecked
|
||||
+ if (((PaperCustomGoal<T>) item.getGoal()).getHandle() == goal) {
|
||||
+ toRemove.add(item.getGoal());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for (net.minecraft.world.entity.ai.goal.Goal g : toRemove) {
|
||||
+ getHandle(craftMob, goal.getTypes()).removeGoal(g);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> void removeAllGoals(T mob) {
|
||||
+ for (GoalType type : GoalType.values()) {
|
||||
+ removeAllGoals(mob, type);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> void removeAllGoals(T mob, GoalType type) {
|
||||
+ for (Goal<T> goal : getAllGoals(mob, type)) {
|
||||
+ removeGoal(mob, goal);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> void removeGoal(T mob, GoalKey<T> key) {
|
||||
+ for (Goal<T> goal : getGoals(mob, key)) {
|
||||
+ removeGoal(mob, goal);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> boolean hasGoal(T mob, GoalKey<T> key) {
|
||||
+ for (Goal<T> g : getAllGoals(mob)) {
|
||||
+ if (g.getKey().equals(key)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> Goal<T> getGoal(T mob, GoalKey<T> key) {
|
||||
+ for (Goal<T> g : getAllGoals(mob)) {
|
||||
+ if (g.getKey().equals(key)) {
|
||||
+ return g;
|
||||
+ }
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> Collection<Goal<T>> getGoals(T mob, GoalKey<T> key) {
|
||||
+ Set<Goal<T>> goals = new HashSet<>();
|
||||
+ for (Goal<T> g : getAllGoals(mob)) {
|
||||
+ if (g.getKey().equals(key)) {
|
||||
+ goals.add(g);
|
||||
+ }
|
||||
+ }
|
||||
+ return goals;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> Collection<Goal<T>> getAllGoals(T mob) {
|
||||
+ Set<Goal<T>> goals = new HashSet<>();
|
||||
+ for (GoalType type : GoalType.values()) {
|
||||
+ goals.addAll(getAllGoals(mob, type));
|
||||
+ }
|
||||
+ return goals;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> Collection<Goal<T>> getAllGoals(T mob, GoalType type) {
|
||||
+ CraftMob craftMob = (CraftMob) mob;
|
||||
+ Set<Goal<T>> goals = new HashSet<>();
|
||||
+ for (WrappedGoal item : getHandle(craftMob, type).availableGoals) {
|
||||
+ if (!item.getGoal().getFlags().hasElement(MobGoalHelper.paperToVanilla(type))) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (item.getGoal() instanceof PaperCustomGoal) {
|
||||
+ //noinspection unchecked
|
||||
+ goals.add(((PaperCustomGoal<T>) item.getGoal()).getHandle());
|
||||
+ } else {
|
||||
+ goals.add(item.getGoal().asPaperVanillaGoal());
|
||||
+ }
|
||||
+ }
|
||||
+ return goals;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> Collection<Goal<T>> getAllGoalsWithout(T mob, GoalType type) {
|
||||
+ CraftMob craftMob = (CraftMob) mob;
|
||||
+ Set<Goal<T>> goals = new HashSet<>();
|
||||
+ for (GoalType internalType : GoalType.values()) {
|
||||
+ if (internalType == type) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ for (WrappedGoal item : getHandle(craftMob, internalType).availableGoals) {
|
||||
+ if (item.getGoal().getFlags().hasElement(MobGoalHelper.paperToVanilla(type))) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (item.getGoal() instanceof PaperCustomGoal) {
|
||||
+ //noinspection unchecked
|
||||
+ goals.add(((PaperCustomGoal<T>) item.getGoal()).getHandle());
|
||||
+ } else {
|
||||
+ goals.add(item.getGoal().asPaperVanillaGoal());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return goals;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> Collection<Goal<T>> getRunningGoals(T mob) {
|
||||
+ Set<Goal<T>> goals = new HashSet<>();
|
||||
+ for (GoalType type : GoalType.values()) {
|
||||
+ goals.addAll(getRunningGoals(mob, type));
|
||||
+ }
|
||||
+ return goals;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> Collection<Goal<T>> getRunningGoals(T mob, GoalType type) {
|
||||
+ CraftMob craftMob = (CraftMob) mob;
|
||||
+ Set<Goal<T>> goals = new HashSet<>();
|
||||
+ getHandle(craftMob, type).getRunningGoals()
|
||||
+ .filter(item -> item.getGoal().getFlags().hasElement(MobGoalHelper.paperToVanilla(type)))
|
||||
+ .forEach(item -> {
|
||||
+ if (item.getGoal() instanceof PaperCustomGoal) {
|
||||
+ //noinspection unchecked
|
||||
+ goals.add(((PaperCustomGoal<T>) item.getGoal()).getHandle());
|
||||
+ } else {
|
||||
+ goals.add(item.getGoal().asPaperVanillaGoal());
|
||||
+ }
|
||||
+ });
|
||||
+ return goals;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public <T extends Mob> Collection<Goal<T>> getRunningGoalsWithout(T mob, GoalType type) {
|
||||
+ CraftMob craftMob = (CraftMob) mob;
|
||||
+ Set<Goal<T>> goals = new HashSet<>();
|
||||
+ for (GoalType internalType : GoalType.values()) {
|
||||
+ if (internalType == type) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ getHandle(craftMob, internalType).getRunningGoals()
|
||||
+ .filter(item -> !item.getGoal().getFlags().hasElement(MobGoalHelper.paperToVanilla(type)))
|
||||
+ .forEach(item -> {
|
||||
+ if (item.getGoal() instanceof PaperCustomGoal) {
|
||||
+ //noinspection unchecked
|
||||
+ goals.add(((PaperCustomGoal<T>) item.getGoal()).getHandle());
|
||||
+ } else {
|
||||
+ goals.add(item.getGoal().asPaperVanillaGoal());
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+ return goals;
|
||||
+ }
|
||||
+
|
||||
+ private GoalSelector getHandle(CraftMob mob, EnumSet<GoalType> types) {
|
||||
+ if (types.contains(GoalType.TARGET)) {
|
||||
+ return mob.getHandle().targetSelector;
|
||||
+ } else {
|
||||
+ return mob.getHandle().goalSelector;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private GoalSelector getHandle(CraftMob mob, GoalType type) {
|
||||
+ if (type == GoalType.TARGET) {
|
||||
+ return mob.getHandle().targetSelector;
|
||||
+ } else {
|
||||
+ return mob.getHandle().goalSelector;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/PaperVanillaGoal.java b/src/main/java/com/destroystokyo/paper/entity/ai/PaperVanillaGoal.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0d30e0b21b9024df939a9d070bd4a99b217e7c12
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/PaperVanillaGoal.java
|
||||
@@ -0,0 +1,61 @@
|
||||
+package com.destroystokyo.paper.entity.ai;
|
||||
+
|
||||
+import java.util.EnumSet;
|
||||
+import net.minecraft.world.entity.ai.goal.Goal;
|
||||
+import org.bukkit.entity.Mob;
|
||||
+
|
||||
+/**
|
||||
+ * Wraps vanilla in api
|
||||
+ */
|
||||
+public class PaperVanillaGoal<T extends Mob> implements VanillaGoal<T> {
|
||||
+
|
||||
+ private final Goal handle;
|
||||
+ private final GoalKey<T> key;
|
||||
+
|
||||
+ private final EnumSet<GoalType> types;
|
||||
+
|
||||
+ public PaperVanillaGoal(Goal handle) {
|
||||
+ this.handle = handle;
|
||||
+ this.key = MobGoalHelper.getKey(handle.getClass());
|
||||
+ this.types = MobGoalHelper.vanillaToPaper(handle.getFlags());
|
||||
+ }
|
||||
+
|
||||
+ public Goal getHandle() {
|
||||
+ return handle;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean shouldActivate() {
|
||||
+ return handle.canUse();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean shouldStayActive() {
|
||||
+ return handle.canContinueToUse();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void start() {
|
||||
+ handle.start();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void stop() {
|
||||
+ handle.stop();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void tick() {
|
||||
+ handle.tick();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public GoalKey<T> getKey() {
|
||||
+ return key;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public EnumSet<GoalType> getTypes() {
|
||||
+ return types;
|
||||
+ }
|
||||
+}
|
||||
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 4379b9948f1eecfe6fd7dea98e298ad5f761019a..3f081183521603824430709886a9cc313c28e7cb 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
|
||||
@@ -7,6 +7,14 @@ public abstract class Goal {
|
||||
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
|
||||
|
||||
+ // Paper start make sure goaltypes is never empty
|
||||
+ public Goal() {
|
||||
+ if (this.goalTypes.size() == 0) {
|
||||
+ this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
public abstract boolean canUse();
|
||||
|
||||
public boolean canContinueToUse() {
|
||||
@@ -34,6 +42,10 @@ public abstract class Goal {
|
||||
// Paper start - remove streams from pathfindergoalselector
|
||||
this.goalTypes.clear();
|
||||
this.goalTypes.addAllUnchecked(controls);
|
||||
+ // make sure its never empty
|
||||
+ if (this.goalTypes.size() == 0) {
|
||||
+ this.goalTypes.addUnchecked(Flag.UNKNOWN_BEHAVIOR);
|
||||
+ }
|
||||
// Paper end - remove streams from pathfindergoalselector
|
||||
}
|
||||
|
||||
@@ -56,7 +68,19 @@ public abstract class Goal {
|
||||
return Mth.positiveCeilDiv(serverTicks, 2);
|
||||
}
|
||||
|
||||
+ // Paper start - mob goal api
|
||||
+ private com.destroystokyo.paper.entity.ai.PaperVanillaGoal<?> vanillaGoal = null;
|
||||
+ public <T extends org.bukkit.entity.Mob> com.destroystokyo.paper.entity.ai.Goal<T> asPaperVanillaGoal() {
|
||||
+ if(this.vanillaGoal == null) {
|
||||
+ this.vanillaGoal = new com.destroystokyo.paper.entity.ai.PaperVanillaGoal<>(this);
|
||||
+ }
|
||||
+ //noinspection unchecked
|
||||
+ return (com.destroystokyo.paper.entity.ai.Goal<T>) this.vanillaGoal;
|
||||
+ }
|
||||
+ // Paper end - mob goal api
|
||||
+
|
||||
public static enum Flag {
|
||||
+ UNKNOWN_BEHAVIOR, // Paper - add UNKNOWN_BEHAVIOR
|
||||
MOVE,
|
||||
LOOK,
|
||||
JUMP,
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 0ea163f4443bc07727675e3c05b9e1d68c52f78a..44c5938edda0aeab0b65f1179ef7dad6356462da 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -2670,5 +2670,11 @@ public final class CraftServer implements Server {
|
||||
public boolean isStopping() {
|
||||
return net.minecraft.server.MinecraftServer.getServer().hasStopped();
|
||||
}
|
||||
+
|
||||
+ private com.destroystokyo.paper.entity.ai.MobGoals mobGoals = new com.destroystokyo.paper.entity.ai.PaperMobGoals();
|
||||
+ @Override
|
||||
+ public com.destroystokyo.paper.entity.ai.MobGoals getMobGoals() {
|
||||
+ return mobGoals;
|
||||
+ }
|
||||
// Paper end
|
||||
}
|
||||
diff --git a/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java b/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..b2d510459bcf90a3611f3d91dae4ccc3d29b4079
|
||||
--- /dev/null
|
||||
+++ b/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java
|
||||
@@ -0,0 +1,103 @@
|
||||
+package com.destroystokyo.paper.entity.ai;
|
||||
+
|
||||
+import org.junit.Assert;
|
||||
+import org.junit.Test;
|
||||
+
|
||||
+import java.lang.reflect.Field;
|
||||
+import java.lang.reflect.Modifier;
|
||||
+import java.util.ArrayList;
|
||||
+import java.util.Collections;
|
||||
+import java.util.List;
|
||||
+import java.util.stream.Collectors;
|
||||
+
|
||||
+import org.bukkit.entity.Mob;
|
||||
+
|
||||
+import io.github.classgraph.ClassGraph;
|
||||
+import io.github.classgraph.ScanResult;
|
||||
+
|
||||
+public class VanillaMobGoalTest {
|
||||
+
|
||||
+ @Test
|
||||
+ public void testKeys() {
|
||||
+ List<GoalKey<?>> deprecated = new ArrayList<>();
|
||||
+ List<GoalKey<?>> keys = new ArrayList<>();
|
||||
+ for (Field field : VanillaGoal.class.getFields()) {
|
||||
+ if (field.getType().equals(GoalKey.class)) {
|
||||
+ try {
|
||||
+ GoalKey<?> goalKey = (GoalKey<?>) field.get(null);
|
||||
+ if (field.getAnnotation(Deprecated.class) != null) {
|
||||
+ deprecated.add(goalKey);
|
||||
+ } else {
|
||||
+ keys.add(goalKey);
|
||||
+ }
|
||||
+ } catch (IllegalAccessException e) {
|
||||
+ System.out.println("Skipping " + field.getName() + ": " + e.getMessage());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ List<Class<?>> classes;
|
||||
+ try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft").scan()) {
|
||||
+ classes = scanResult.getSubclasses(net.minecraft.world.entity.ai.goal.Goal.class.getName()).loadClasses();
|
||||
+ }
|
||||
+
|
||||
+ List<GoalKey<?>> vanillaNames = classes.stream()
|
||||
+ .filter(VanillaMobGoalTest::hasNoEnclosingClass)
|
||||
+ .filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
|
||||
+ .filter(clazz -> !net.minecraft.world.entity.ai.goal.WrappedGoal.class.equals(clazz)) // TODO - properly fix
|
||||
+ .map(goalClass -> MobGoalHelper.getKey((Class<? extends net.minecraft.world.entity.ai.goal.Goal>) goalClass))
|
||||
+ .collect(Collectors.toList());
|
||||
+
|
||||
+ List<GoalKey<?>> missingFromAPI = new ArrayList<>(vanillaNames);
|
||||
+ missingFromAPI.removeAll(keys);
|
||||
+ missingFromAPI.removeIf(k -> MobGoalHelper.ignored.contains(k.getNamespacedKey().getKey()));
|
||||
+ List<GoalKey<?>> missingFromVanilla = new ArrayList<>(keys);
|
||||
+ missingFromVanilla.removeAll(vanillaNames);
|
||||
+
|
||||
+ boolean shouldFail = false;
|
||||
+ if (missingFromAPI.size() != 0) {
|
||||
+ System.out.println("Missing from API: ");
|
||||
+ for (GoalKey<?> key : missingFromAPI) {
|
||||
+ System.out.println("GoalKey<" + key.getEntityClass().getSimpleName() + "> " + key.getNamespacedKey().getKey().toUpperCase() +
|
||||
+ " = GoalKey.of(" + key.getEntityClass().getSimpleName() + ".class, NamespacedKey.minecraft(\"" + key.getNamespacedKey().getKey() + "\"));");
|
||||
+ }
|
||||
+ shouldFail = true;
|
||||
+ }
|
||||
+ if (missingFromVanilla.size() != 0) {
|
||||
+ System.out.println("Missing from vanilla: ");
|
||||
+ missingFromVanilla.forEach(System.out::println);
|
||||
+ shouldFail = true;
|
||||
+ }
|
||||
+
|
||||
+ if (deprecated.size() != 0) {
|
||||
+ System.out.println("Deprecated (might want to remove them at some point): ");
|
||||
+ deprecated.forEach(System.out::println);
|
||||
+ }
|
||||
+
|
||||
+ if (shouldFail) Assert.fail("See above");
|
||||
+ }
|
||||
+
|
||||
+ private static boolean hasNoEnclosingClass(Class<?> clazz) {
|
||||
+ return clazz.getEnclosingClass() == null || hasNoEnclosingClass(clazz.getSuperclass());
|
||||
+ }
|
||||
+
|
||||
+ @Test
|
||||
+ public void testBukkitMap() {
|
||||
+ List<Class<?>> classes;
|
||||
+ try (ScanResult scanResult = new ClassGraph().enableAllInfo().whitelistPackages("net.minecraft.world.entity").scan()) {
|
||||
+ classes = scanResult.getSubclasses("net.minecraft.world.entity.Mob").loadClasses();
|
||||
+ }
|
||||
+ Assert.assertNotEquals("There are supposed to be more than 0 entity types!", Collections.emptyList(), classes);
|
||||
+
|
||||
+ boolean shouldFail = false;
|
||||
+ for (Class<?> nmsClass : classes) {
|
||||
+ Class<? extends Mob> bukkitClass = MobGoalHelper.toBukkitClass((Class<? extends net.minecraft.world.entity.Mob>) nmsClass);
|
||||
+ if (bukkitClass == null) {
|
||||
+ shouldFail = true;
|
||||
+ System.out.println("Missing bukkitMap.put(" + nmsClass.getSimpleName() + ".class, " + nmsClass.getSimpleName().replace("Entity", "") + ".class);");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (shouldFail) Assert.fail("See above");
|
||||
+ }
|
||||
+}
|
|
@ -1,127 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Mariell Hoversholm <proximyst@proximyst.com>
|
||||
Date: Wed, 22 Apr 2020 23:29:20 +0200
|
||||
Subject: [PATCH] Add villager reputation API
|
||||
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/entity/villager/ReputationConstructor.java b/src/main/java/com/destroystokyo/paper/entity/villager/ReputationConstructor.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0f10c333d88f2e1c56a6c7f22d421084adfd3789
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/entity/villager/ReputationConstructor.java
|
||||
@@ -0,0 +1,9 @@
|
||||
+package com.destroystokyo.paper.entity.villager;
|
||||
+// Must have own package due to package-level constructor.
|
||||
+
|
||||
+public final class ReputationConstructor {
|
||||
+ // Abuse the package-level constructor.
|
||||
+ public static Reputation construct(int[] values) {
|
||||
+ return new Reputation(values);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
|
||||
index 701b9c67c01f2aa4823e2aa71db388d5c3292dc4..e98951ac056910340c498d866715e174d461f723 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/gossip/GossipContainer.java
|
||||
@@ -27,7 +27,7 @@ import net.minecraft.util.VisibleForDebug;
|
||||
|
||||
public class GossipContainer {
|
||||
public static final int DISCARD_THRESHOLD = 2;
|
||||
- private final Map<UUID, GossipContainer.EntityGossips> gossips = Maps.newHashMap();
|
||||
+ private final Map<UUID, GossipContainer.EntityGossips> gossips = Maps.newHashMap(); public Map<UUID, GossipContainer.EntityGossips> getReputations() { return this.gossips; } // Paper - add getter for reputations
|
||||
|
||||
@VisibleForDebug
|
||||
public Map<UUID, Object2IntMap<GossipType>> getGossipEntries() {
|
||||
@@ -226,6 +226,28 @@ public class GossipContainer {
|
||||
public void remove(GossipType gossipType) {
|
||||
this.entries.removeInt(gossipType);
|
||||
}
|
||||
+
|
||||
+ // Paper start - Add villager reputation API
|
||||
+ private static final com.destroystokyo.paper.entity.villager.ReputationType[] REPUTATION_TYPES = com.destroystokyo.paper.entity.villager.ReputationType.values();
|
||||
+ public com.destroystokyo.paper.entity.villager.Reputation getPaperReputation() {
|
||||
+ int[] reputation = new int[REPUTATION_TYPES.length];
|
||||
+ reputation[com.destroystokyo.paper.entity.villager.ReputationType.MAJOR_NEGATIVE.ordinal()] = entries.getOrDefault(GossipType.MAJOR_NEGATIVE, 0);
|
||||
+ reputation[com.destroystokyo.paper.entity.villager.ReputationType.MAJOR_POSITIVE.ordinal()] = entries.getOrDefault(GossipType.MAJOR_POSITIVE, 0);
|
||||
+ reputation[com.destroystokyo.paper.entity.villager.ReputationType.MINOR_NEGATIVE.ordinal()] = entries.getOrDefault(GossipType.MINOR_NEGATIVE, 0);
|
||||
+ reputation[com.destroystokyo.paper.entity.villager.ReputationType.MINOR_POSITIVE.ordinal()] = entries.getOrDefault(GossipType.MINOR_POSITIVE, 0);
|
||||
+ reputation[com.destroystokyo.paper.entity.villager.ReputationType.TRADING.ordinal()] = entries.getOrDefault(GossipType.TRADING, 0);
|
||||
+ return com.destroystokyo.paper.entity.villager.ReputationConstructor.construct(reputation);
|
||||
+ }
|
||||
+
|
||||
+ public void assignFromPaperReputation(com.destroystokyo.paper.entity.villager.Reputation rep) {
|
||||
+ int val;
|
||||
+ if ((val = rep.getReputation(com.destroystokyo.paper.entity.villager.ReputationType.MAJOR_NEGATIVE)) != 0) this.entries.put(GossipType.MAJOR_NEGATIVE, val);
|
||||
+ if ((val = rep.getReputation(com.destroystokyo.paper.entity.villager.ReputationType.MAJOR_POSITIVE)) != 0) this.entries.put(GossipType.MAJOR_POSITIVE, val);
|
||||
+ if ((val = rep.getReputation(com.destroystokyo.paper.entity.villager.ReputationType.MINOR_NEGATIVE)) != 0) this.entries.put(GossipType.MINOR_NEGATIVE, val);
|
||||
+ if ((val = rep.getReputation(com.destroystokyo.paper.entity.villager.ReputationType.MINOR_POSITIVE)) != 0) this.entries.put(GossipType.MINOR_POSITIVE, val);
|
||||
+ if ((val = rep.getReputation(com.destroystokyo.paper.entity.villager.ReputationType.TRADING)) != 0) this.entries.put(GossipType.TRADING, val);
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
static class GossipEntry {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
|
||||
index 1a8a49bd269ed52879866ff3853e131d04aa8bba..f0b910df1ee471b4d72d97c6197ab14f2854976e 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
|
||||
@@ -17,6 +17,13 @@ import org.bukkit.entity.Villager;
|
||||
import org.bukkit.entity.ZombieVillager;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
|
||||
+// Paper start
|
||||
+import com.destroystokyo.paper.entity.villager.Reputation;
|
||||
+import com.google.common.collect.Maps;
|
||||
+import java.util.Map;
|
||||
+import java.util.UUID;
|
||||
+// Paper end
|
||||
+
|
||||
public class CraftVillager extends CraftAbstractVillager implements Villager {
|
||||
|
||||
public CraftVillager(CraftServer server, net.minecraft.world.entity.npc.Villager entity) {
|
||||
@@ -146,4 +153,45 @@ public class CraftVillager extends CraftAbstractVillager implements Villager {
|
||||
public static VillagerProfession bukkitToNmsProfession(Profession bukkit) {
|
||||
return Registry.VILLAGER_PROFESSION.get(CraftNamespacedKey.toMinecraft(bukkit.getKey()));
|
||||
}
|
||||
+
|
||||
+ // Paper start - Add villager reputation API
|
||||
+ @Override
|
||||
+ public Reputation getReputation(UUID uniqueId) {
|
||||
+ net.minecraft.world.entity.ai.gossip.GossipContainer.EntityGossips rep = getHandle().getGossips().getReputations().get(uniqueId);
|
||||
+ if (rep == null) {
|
||||
+ return new Reputation(Maps.newHashMap());
|
||||
+ }
|
||||
+
|
||||
+ return rep.getPaperReputation();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Map<UUID, Reputation> getReputations() {
|
||||
+ return getHandle().getGossips().getReputations().entrySet()
|
||||
+ .stream()
|
||||
+ .collect(java.util.stream.Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getPaperReputation()));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setReputation(UUID uniqueId, Reputation reputation) {
|
||||
+ net.minecraft.world.entity.ai.gossip.GossipContainer.EntityGossips nmsReputation =
|
||||
+ getHandle().getGossips().getReputations().computeIfAbsent(
|
||||
+ uniqueId,
|
||||
+ key -> new net.minecraft.world.entity.ai.gossip.GossipContainer.EntityGossips()
|
||||
+ );
|
||||
+ nmsReputation.assignFromPaperReputation(reputation);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setReputations(Map<UUID, Reputation> reputations) {
|
||||
+ for (Map.Entry<UUID, Reputation> entry : reputations.entrySet()) {
|
||||
+ setReputation(entry.getKey(), entry.getValue());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void clearReputations() {
|
||||
+ getHandle().getGossips().getReputations().clear();
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: BillyGalbreath <Blake.Galbreath@GMail.com>
|
||||
Date: Fri, 10 Nov 2017 23:03:12 -0500
|
||||
Subject: [PATCH] Option for maximum exp value when merging orbs
|
||||
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
index a8dd2a9beb48f464033c812d99c197332175fc09..ffe0ee1bd44d47abbe6b94aadab9ee7eca7c7612 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
@@ -696,4 +696,10 @@ public class PaperWorldConfig {
|
||||
phantomIgnoreCreative = getBoolean("phantoms-do-not-spawn-on-creative-players", phantomIgnoreCreative);
|
||||
phantomOnlyAttackInsomniacs = getBoolean("phantoms-only-attack-insomniacs", phantomOnlyAttackInsomniacs);
|
||||
}
|
||||
+
|
||||
+ public int expMergeMaxValue;
|
||||
+ private void expMergeMaxValue() {
|
||||
+ expMergeMaxValue = getInt("experience-merge-max-value", -1);
|
||||
+ log("Experience Merge Max Value: " + expMergeMaxValue);
|
||||
+ }
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
||||
index 1e16264b533c2205098c0569fadb8e369a032764..fb490fa7d59b1099e34a5adc7993d6dad2f10932 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
||||
@@ -640,16 +640,30 @@ public class CraftEventFactory {
|
||||
net.minecraft.world.entity.ExperienceOrb xp = (net.minecraft.world.entity.ExperienceOrb) entity;
|
||||
double radius = world.spigotConfig.expMerge;
|
||||
if (radius > 0) {
|
||||
+ // Paper start - Maximum exp value when merging - Whole section has been tweaked, see comments for specifics
|
||||
+ final int maxValue = world.paperConfig.expMergeMaxValue;
|
||||
+ final boolean mergeUnconditionally = world.paperConfig.expMergeMaxValue <= 0;
|
||||
+ if (mergeUnconditionally || xp.value < maxValue) { // Paper - Skip iteration if unnecessary
|
||||
+
|
||||
List<Entity> entities = world.getEntities(entity, entity.getBoundingBox().inflate(radius, radius, radius));
|
||||
for (Entity e : entities) {
|
||||
if (e instanceof net.minecraft.world.entity.ExperienceOrb) {
|
||||
net.minecraft.world.entity.ExperienceOrb loopItem = (net.minecraft.world.entity.ExperienceOrb) e;
|
||||
- if (!loopItem.isRemoved()) {
|
||||
+ // Paper start
|
||||
+ if (!loopItem.isRemoved() && !(maxValue > 0 && loopItem.value >= maxValue)) {
|
||||
+ long newTotal = (long)xp.value + (long)loopItem.value;
|
||||
+ if ((int) newTotal < 0) continue; // Overflow
|
||||
+ if (maxValue > 0 && newTotal > (long)maxValue) {
|
||||
+ loopItem.value = (int) (newTotal - maxValue);
|
||||
+ xp.value = maxValue;
|
||||
+ } else {
|
||||
xp.value += loopItem.value;
|
||||
loopItem.discard();
|
||||
+ } // Paper end
|
||||
}
|
||||
}
|
||||
}
|
||||
+ } // Paper end - End iteration skip check - All tweaking ends here
|
||||
}
|
||||
// Spigot end
|
||||
} else if (!(entity instanceof ServerPlayer)) {
|
|
@ -1,23 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Tue, 19 Dec 2017 22:57:26 -0500
|
||||
Subject: [PATCH] ExperienceOrbMergeEvent
|
||||
|
||||
Has to be reimplemented at one point maybe
|
||||
Fired when the server is about to merge 2 experience orbs
|
||||
Plugins can cancel this if they want to ensure experience orbs do not lose important
|
||||
metadata such as spawn reason, or conditionally move data from source to target.
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
||||
index fb490fa7d59b1099e34a5adc7993d6dad2f10932..113e4db1d66b7af5b2ba976d094b4bf548b5800e 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
|
||||
@@ -650,7 +650,7 @@ public class CraftEventFactory {
|
||||
if (e instanceof net.minecraft.world.entity.ExperienceOrb) {
|
||||
net.minecraft.world.entity.ExperienceOrb loopItem = (net.minecraft.world.entity.ExperienceOrb) e;
|
||||
// Paper start
|
||||
- if (!loopItem.isRemoved() && !(maxValue > 0 && loopItem.value >= maxValue)) {
|
||||
+ if (!loopItem.isRemoved() && !(maxValue > 0 && loopItem.value >= maxValue) && new com.destroystokyo.paper.event.entity.ExperienceOrbMergeEvent((org.bukkit.entity.ExperienceOrb) entity.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) loopItem.getBukkitEntity()).callEvent()) { // Paper - ExperienceOrbMergeEvent
|
||||
long newTotal = (long)xp.value + (long)loopItem.value;
|
||||
if ((int) newTotal < 0) continue; // Overflow
|
||||
if (maxValue > 0 && newTotal > (long)maxValue) {
|
|
@ -1,19 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: kickash32 <kickash32@gmail.com>
|
||||
Date: Fri, 8 May 2020 00:49:18 -0400
|
||||
Subject: [PATCH] Fix PotionEffect ignores icon flag
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
||||
index be1540b0a5f95f8a85f91d5fe398cd2cf8832ec4..639d376bf382409410e26385134d36fd6e3b5f0c 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
|
||||
@@ -412,7 +412,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
||||
|
||||
@Override
|
||||
public boolean addPotionEffect(PotionEffect effect, boolean force) {
|
||||
- this.getHandle().addEffect(new MobEffectInstance(MobEffect.byId(effect.getType().getId()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles()), EntityPotionEffectEvent.Cause.PLUGIN);
|
||||
+ this.getHandle().addEffect(new MobEffectInstance(MobEffect.byId(effect.getType().getId()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.hasIcon()), EntityPotionEffectEvent.Cause.PLUGIN); // Paper - Don't ignore icon
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: virustotalop <virustotalop@gmail.com>
|
||||
Date: Thu, 16 Apr 2020 20:51:32 -0700
|
||||
Subject: [PATCH] Optimize brigadier child sorting performance
|
||||
|
||||
|
||||
diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
|
||||
index da6250df1c5f3385b683cffde47754bca4606f5e..a4f97c1df86c574af9b9824a38034a3d76d6e357 100644
|
||||
--- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java
|
||||
+++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
|
||||
@@ -26,7 +26,7 @@ import java.util.function.Predicate;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
|
||||
public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
||||
- private final Map<String, CommandNode<S>> children = new LinkedHashMap<>();
|
||||
+ private Map<String, CommandNode<S>> children = com.google.common.collect.Maps.newTreeMap(); // Paper - Switch to tree map for automatic sorting
|
||||
private final Map<String, LiteralCommandNode<S>> literals = new LinkedHashMap<>();
|
||||
private final Map<String, ArgumentCommandNode<S, ?>> arguments = new LinkedHashMap<>();
|
||||
public Predicate<S> requirement;
|
||||
@@ -106,6 +106,8 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
|
||||
this.arguments.put(node.getName(), (ArgumentCommandNode<S, ?>) node);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Paper - Remove manual sorting, it is no longer needed
|
||||
}
|
||||
|
||||
public void findAmbiguities(final AmbiguityConsumer<S> consumer) {
|
|
@ -1,44 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: JRoy <joshroy126@gmail.com>
|
||||
Date: Sun, 10 May 2020 23:06:30 -0400
|
||||
Subject: [PATCH] Potential bed API
|
||||
|
||||
Adds a new method to fetch the location of a player's bed without generating any sync loads.
|
||||
|
||||
getPotentialBedLocation - Gets the last known location of a player's bed. This does not preform any check if the bed is still valid and does not load any chunks.
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
||||
index 08625a587c23394b344fed139abfcd82a3185aaf..0bad3e55ede8346531dd0eb1c139c14a13e4b72e 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
|
||||
@@ -12,6 +12,7 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundContainerClosePacket;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
@@ -125,6 +126,22 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
|
||||
return this.getHandle().sleepCounter;
|
||||
}
|
||||
|
||||
+ // Paper start - Potential bed api
|
||||
+ @Override
|
||||
+ public Location getPotentialBedLocation() {
|
||||
+ ServerPlayer handle = (ServerPlayer) getHandle();
|
||||
+ BlockPos bed = handle.getRespawnPosition();
|
||||
+ if (bed == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ ServerLevel worldServer = handle.server.getLevel(handle.getRespawnDimension());
|
||||
+ if (worldServer == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ return new Location(worldServer.getWorld(), bed.getX(), bed.getY(), bed.getZ());
|
||||
+ }
|
||||
+ // Paper end
|
||||
@Override
|
||||
public boolean sleep(Location location, boolean force) {
|
||||
Preconditions.checkArgument(location != null, "Location cannot be null");
|
|
@ -1,63 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Sun, 10 May 2020 22:16:17 -0400
|
||||
Subject: [PATCH] Wait for Async Tasks during shutdown
|
||||
|
||||
Server.reload() had this logic to give time for tasks to shutdown,
|
||||
however shutdown did not...
|
||||
|
||||
Adds a 5 second grace period for any async tasks to finish and warns
|
||||
if any are still running after that delay just as reload does.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index abb640370da876a18a3eba952b19cfa9d0f73743..12c1cee3b3055b44d0ca3196eb37e51879fce67b 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -950,6 +950,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
// CraftBukkit start
|
||||
if (this.server != null) {
|
||||
this.server.disablePlugins();
|
||||
+ this.server.waitForAsyncTasksShutdown(); // Paper
|
||||
}
|
||||
// CraftBukkit end
|
||||
if (this.getConnection() != null) {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 44c5938edda0aeab0b65f1179ef7dad6356462da..0e339c13f58419a6af39b99782160ac3155b0606 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -1006,6 +1006,35 @@ public final class CraftServer implements Server {
|
||||
org.spigotmc.WatchdogThread.hasStarted = true; // Paper - Disable watchdog early timeout on reload
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public void waitForAsyncTasksShutdown() {
|
||||
+ int pollCount = 0;
|
||||
+
|
||||
+ // Wait for at most 5 seconds for plugins to close their threads
|
||||
+ while (pollCount < 10*5 && getScheduler().getActiveWorkers().size() > 0) {
|
||||
+ try {
|
||||
+ Thread.sleep(100);
|
||||
+ } catch (InterruptedException e) {}
|
||||
+ pollCount++;
|
||||
+ }
|
||||
+
|
||||
+ List<BukkitWorker> overdueWorkers = getScheduler().getActiveWorkers();
|
||||
+ for (BukkitWorker worker : overdueWorkers) {
|
||||
+ Plugin plugin = worker.getOwner();
|
||||
+ String author = "<NoAuthorGiven>";
|
||||
+ if (plugin.getDescription().getAuthors().size() > 0) {
|
||||
+ author = plugin.getDescription().getAuthors().get(0);
|
||||
+ }
|
||||
+ getLogger().log(Level.SEVERE, String.format(
|
||||
+ "Nag author: '%s' of '%s' about the following: %s",
|
||||
+ author,
|
||||
+ plugin.getDescription().getName(),
|
||||
+ "This plugin is not properly shutting down its async tasks when it is being shut down. This task may throw errors during the final shutdown logs and might not complete before process dies."
|
||||
+ ));
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public void reloadData() {
|
||||
ReloadCommand.reload(console);
|
|
@ -1,19 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: kickash32 <kickash32@gmail.com>
|
||||
Date: Sat, 9 May 2020 02:01:48 -0400
|
||||
Subject: [PATCH] Ensure EntityRaider respects game and entity rules for
|
||||
picking up items
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/raid/Raider.java b/src/main/java/net/minecraft/world/entity/raid/Raider.java
|
||||
index 5da07a6e77bafca274700af1b3ea139fd54e77e1..c5611465b1f9228a994c462c093cec94d7d08c2f 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/raid/Raider.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/raid/Raider.java
|
||||
@@ -312,6 +312,7 @@ public abstract class Raider extends PatrollingMonster {
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
+ if (!this.mob.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING) || !this.mob.canPickUpLoot()) return false; // Paper - respect game and entity rules for picking up items
|
||||
Raid raid = this.mob.getCurrentRaid();
|
||||
|
||||
if (this.mob.hasActiveRaid() && !this.mob.getCurrentRaid().isOver() && this.mob.canBeLeader() && !ItemStack.matches(this.mob.getItemBySlot(EquipmentSlot.HEAD), Raid.getLeaderBannerInstance())) {
|
|
@ -1,189 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Wed, 13 May 2020 23:01:26 -0400
|
||||
Subject: [PATCH] Protect Bedrock and End Portal/Frames from being destroyed
|
||||
|
||||
This fixes exploits that let players destroy bedrock by Pistons, explosions
|
||||
and Mushrooom/Tree generation.
|
||||
|
||||
These blocks are designed to not be broken except by creative players/commands.
|
||||
So protect them from a multitude of methods of destroying them.
|
||||
|
||||
A config is provided if you rather let players use these exploits, and let
|
||||
them destroy the worlds End Portals and get on top of the nether easy.
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
index 52ba4797d0f27b8aaac262fd5a33dab63e28ebab..855db893080fc8784dc402a4fd7c3080dc9cdc15 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
@@ -435,4 +435,15 @@ public class PaperConfig {
|
||||
private static void loggerSettings() {
|
||||
deobfuscateStacktraces = getBoolean("settings.loggers.deobfuscate-stacktraces", deobfuscateStacktraces);
|
||||
}
|
||||
+
|
||||
+ public static boolean allowBlockPermanentBreakingExploits = false;
|
||||
+ private static void allowBlockPermanentBreakingExploits() {
|
||||
+ if (config.contains("allow-perm-block-break-exploits")) {
|
||||
+ allowBlockPermanentBreakingExploits = config.getBoolean("allow-perm-block-break-exploits", false);
|
||||
+ config.set("allow-perm-block-break-exploits", null);
|
||||
+ }
|
||||
+
|
||||
+ config.set("settings.unsupported-settings.allow-permanent-block-break-exploits-readme", "This setting controls if players should be able to break bedrock, end portals and other intended to be permanent blocks.");
|
||||
+ allowBlockPermanentBreakingExploits = getBoolean("settings.unsupported-settings.allow-permanent-block-break-exploits", allowBlockPermanentBreakingExploits);
|
||||
+ }
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java
|
||||
index fc1b86828c8a38950f7a26735d8f5d125ee3e914..0d6f33319521101bd10352d597b5e3e063ec443f 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Explosion.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Explosion.java
|
||||
@@ -174,6 +174,7 @@ public class Explosion {
|
||||
for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
|
||||
BlockPos blockposition = new BlockPos(d4, d5, d6);
|
||||
BlockState iblockdata = this.level.getBlockState(blockposition);
|
||||
+ if (!iblockdata.isDestroyable()) continue; // Paper
|
||||
FluidState fluid = iblockdata.getFluidState(); // Paper
|
||||
|
||||
if (!this.level.isInWorldBounds(blockposition)) {
|
||||
@@ -351,7 +352,7 @@ public class Explosion {
|
||||
BlockState iblockdata = this.level.getBlockState(blockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
- if (!iblockdata.isAir()) {
|
||||
+ if (!iblockdata.isAir() && iblockdata.isDestroyable()) { // Paper
|
||||
BlockPos blockposition1 = blockposition.immutable();
|
||||
|
||||
this.level.getProfiler().push("explosion_blocks");
|
||||
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
|
||||
index e4c2bd0131a54495fbd1930ad046225118e33186..4095b3ebacb8a55744fa99fea7e08d31e42d8b47 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -414,6 +414,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
|
||||
public boolean setBlock(BlockPos pos, BlockState state, int flags, int maxUpdateDepth) {
|
||||
// CraftBukkit start - tree generation
|
||||
if (this.captureTreeGeneration) {
|
||||
+ // Paper start
|
||||
+ BlockState type = getBlockState(pos);
|
||||
+ if (!type.isDestroyable()) return false;
|
||||
+ // Paper end
|
||||
CraftBlockState blockstate = this.capturedBlockStates.get(pos);
|
||||
if (blockstate == null) {
|
||||
blockstate = CapturedBlockState.getTreeBlockState(this, pos, flags);
|
||||
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 59a8d1984f3bcc94048a4ce3e530d21cc3f39d7f..17c369b49272ac72119708b507e6fa119343706e 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/Block.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/Block.java
|
||||
@@ -90,6 +90,19 @@ public class Block extends BlockBehaviour implements ItemLike {
|
||||
protected final StateDefinition<Block, BlockState> stateDefinition;
|
||||
private BlockState defaultBlockState;
|
||||
// Paper start
|
||||
+ public final boolean isDestroyable() {
|
||||
+ return com.destroystokyo.paper.PaperConfig.allowBlockPermanentBreakingExploits ||
|
||||
+ this != Blocks.BEDROCK &&
|
||||
+ this != Blocks.END_PORTAL_FRAME &&
|
||||
+ this != Blocks.END_PORTAL &&
|
||||
+ this != Blocks.END_GATEWAY &&
|
||||
+ this != Blocks.COMMAND_BLOCK &&
|
||||
+ this != Blocks.REPEATING_COMMAND_BLOCK &&
|
||||
+ this != Blocks.CHAIN_COMMAND_BLOCK &&
|
||||
+ this != Blocks.BARRIER &&
|
||||
+ this != Blocks.STRUCTURE_BLOCK &&
|
||||
+ this != Blocks.JIGSAW;
|
||||
+ }
|
||||
public co.aikar.timings.Timing timing;
|
||||
public co.aikar.timings.Timing getTiming() {
|
||||
if (timing == null) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
|
||||
index 8f4b3bf0eba548a35b59232491c68d635e21123f..edb599da2431fa32f6e656dd3de8f4a6f5e2ed4f 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
|
||||
@@ -198,6 +198,12 @@ public class PistonBaseBlock extends DirectionalBlock {
|
||||
@Override
|
||||
public boolean triggerEvent(BlockState state, Level world, BlockPos pos, int type, int data) {
|
||||
Direction enumdirection = (Direction) state.getValue(PistonBaseBlock.FACING);
|
||||
+ // Paper start - prevent retracting when we're facing the wrong way (we were replaced before retraction could occur)
|
||||
+ Direction directionQueuedAs = Direction.from3DDataValue(data & 7); // Paper - copied from below
|
||||
+ if (!com.destroystokyo.paper.PaperConfig.allowBlockPermanentBreakingExploits && enumdirection != directionQueuedAs) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ // Paper end - prevent retracting when we're facing the wrong way
|
||||
|
||||
if (!world.isClientSide) {
|
||||
boolean flag = this.getNeighborSignal(world, pos, enumdirection);
|
||||
@@ -230,7 +236,7 @@ public class PistonBaseBlock extends DirectionalBlock {
|
||||
BlockState iblockdata1 = (BlockState) ((BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(MovingPistonBlock.FACING, enumdirection)).setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT);
|
||||
|
||||
world.setBlock(pos, iblockdata1, 20);
|
||||
- world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata1, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true));
|
||||
+ world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata1, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); // Paper - diff on change
|
||||
world.blockUpdated(pos, iblockdata1.getBlock());
|
||||
iblockdata1.updateNeighbourShapes(world, pos, 2);
|
||||
if (this.isSticky) {
|
||||
@@ -259,7 +265,14 @@ public class PistonBaseBlock extends DirectionalBlock {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
- world.removeBlock(pos.relative(enumdirection), false);
|
||||
+ // Paper start - fix headless pistons breaking blocks
|
||||
+ BlockPos headPos = pos.relative(enumdirection);
|
||||
+ if (com.destroystokyo.paper.PaperConfig.allowBlockPermanentBreakingExploits || world.getBlockState(headPos) == Blocks.PISTON_HEAD.defaultBlockState().setValue(FACING, enumdirection)) { // double check to make sure we're not a headless piston.
|
||||
+ world.removeBlock(headPos, false);
|
||||
+ } else {
|
||||
+ ((ServerLevel)world).getChunkSource().blockChanged(headPos); // ... fix client desync
|
||||
+ }
|
||||
+ // Paper end - fix headless pistons breaking blocks
|
||||
}
|
||||
|
||||
world.playSound((Player) null, pos, SoundEvents.PISTON_CONTRACT, SoundSource.BLOCKS, 0.5F, world.random.nextFloat() * 0.15F + 0.6F);
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
||||
index a5b7fbd940bf180b76017442ee7224761741fe52..fdb9a60ce9e1f8e3caabe1733e32e34146d4ddf3 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/state/BlockBehaviour.java
|
||||
@@ -225,7 +225,7 @@ public abstract class BlockBehaviour {
|
||||
/** @deprecated */
|
||||
@Deprecated
|
||||
public boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
|
||||
- return this.material.isReplaceable() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem()));
|
||||
+ return this.material.isReplaceable() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem())) && (state.isDestroyable() || (context.getPlayer() != null && context.getPlayer().getAbilities().instabuild)); // Paper
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
@@ -707,7 +707,11 @@ public abstract class BlockBehaviour {
|
||||
public Block getBlock() {
|
||||
return (Block) this.owner;
|
||||
}
|
||||
-
|
||||
+ // Paper start
|
||||
+ public final boolean isDestroyable() {
|
||||
+ return getBlock().isDestroyable();
|
||||
+ }
|
||||
+ // Paper end
|
||||
public Material getMaterial() {
|
||||
return this.material;
|
||||
}
|
||||
@@ -805,7 +809,7 @@ public abstract class BlockBehaviour {
|
||||
}
|
||||
|
||||
public PushReaction getPistonPushReaction() {
|
||||
- return this.getBlock().getPistonPushReaction(this.asState());
|
||||
+ return !isDestroyable() ? PushReaction.BLOCK : this.getBlock().getPistonPushReaction(this.asState()); // Paper
|
||||
}
|
||||
|
||||
public boolean isSolidRender(BlockGetter world, BlockPos pos) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
|
||||
index 05150fbade1d5a9b3b6de8ad1f5e825f34d1037e..ed79058696eb26a89b9d4116821840dbad9ea449 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
|
||||
@@ -210,6 +210,13 @@ public class PortalForcer {
|
||||
for (int j = -1; j < 3; ++j) {
|
||||
for (int k = -1; k < 4; ++k) {
|
||||
temp.setWithOffset(pos, portalDirection.getStepX() * j + enumdirection1.getStepX() * distanceOrthogonalToPortal, k, portalDirection.getStepZ() * j + enumdirection1.getStepZ() * distanceOrthogonalToPortal);
|
||||
+ // Paper start - prevent destroying unbreakable blocks
|
||||
+ if (!com.destroystokyo.paper.PaperConfig.allowBlockPermanentBreakingExploits) {
|
||||
+ if (!this.level.getBlockState(temp).isDestroyable()) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - prevent destroying unbreakable blocks
|
||||
if (k < 0 && !this.level.getBlockState(temp).getMaterial().isSolid()) {
|
||||
return false;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <spottedleaf@spottedleaf.dev>
|
||||
Date: Mon, 27 Apr 2020 02:48:06 -0700
|
||||
Subject: [PATCH] Reduce MutableInt allocations from light engine
|
||||
|
||||
We can abuse the fact light is single threaded and share an instance
|
||||
per light engine instance
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java b/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java
|
||||
index 729c4b1763a24bac3c0764bea505555a32e54f57..37d7165dfd17da03428f8dbbbf95aa8005be289c 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/lighting/BlockLightEngine.java
|
||||
@@ -15,6 +15,7 @@ import org.apache.commons.lang3.mutable.MutableInt;
|
||||
public final class BlockLightEngine extends LayerLightEngine<BlockLightSectionStorage.BlockDataLayerStorageMap, BlockLightSectionStorage> {
|
||||
private static final Direction[] DIRECTIONS = Direction.values();
|
||||
private final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
|
||||
+ private final MutableInt mutableInt = new MutableInt(); // Paper
|
||||
|
||||
public BlockLightEngine(LightChunkGetter chunkProvider) {
|
||||
super(chunkProvider, LightLayer.BLOCK, new BlockLightSectionStorage(chunkProvider));
|
||||
@@ -44,7 +45,7 @@ public final class BlockLightEngine extends LayerLightEngine<BlockLightSectionSt
|
||||
if (direction == null) {
|
||||
return 15;
|
||||
} else {
|
||||
- MutableInt mutableInt = new MutableInt();
|
||||
+ //MutableInt mutableint = new MutableInt(); // Paper - share mutableint, single threaded
|
||||
BlockState blockState = this.getStateAndOpacity(targetId, mutableInt);
|
||||
if (mutableInt.getValue() >= 15) {
|
||||
return 15;
|
||||
diff --git a/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java b/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java
|
||||
index 4252247acd5c71e46d90f454663a9737e22e2a61..d122475c1a9d340046c478087d3ff5bf1ff8932c 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/lighting/SkyLightEngine.java
|
||||
@@ -14,6 +14,7 @@ import org.apache.commons.lang3.mutable.MutableInt;
|
||||
public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorage.SkyDataLayerStorageMap, SkyLightSectionStorage> {
|
||||
private static final Direction[] DIRECTIONS = Direction.values();
|
||||
private static final Direction[] HORIZONTALS = new Direction[]{Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST};
|
||||
+ private final MutableInt mutableInt = new MutableInt(); // Paper
|
||||
|
||||
public SkyLightEngine(LightChunkGetter chunkProvider) {
|
||||
super(chunkProvider, LightLayer.SKY, new SkyLightSectionStorage(chunkProvider));
|
||||
@@ -25,7 +26,7 @@ public final class SkyLightEngine extends LayerLightEngine<SkyLightSectionStorag
|
||||
if (level >= 15) {
|
||||
return level;
|
||||
} else {
|
||||
- MutableInt mutableInt = new MutableInt();
|
||||
+ //MutableInt mutableint = new MutableInt(); // Paper - share mutableint, single threaded
|
||||
BlockState blockState = this.getStateAndOpacity(targetId, mutableInt);
|
||||
if (mutableInt.getValue() >= 15) {
|
||||
return 15;
|
|
@ -1,60 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <spottedleaf@spottedleaf.dev>
|
||||
Date: Mon, 27 Apr 2020 00:04:16 -0700
|
||||
Subject: [PATCH] Reduce allocation of Vec3D by entity tracker
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index f4b14d77d5c256852677bd9bc6dbda2bbe5367e3..38efde791c99dd22de71183676d0301a1d5a14e0 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -1886,9 +1886,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
public void updatePlayer(ServerPlayer player) {
|
||||
org.spigotmc.AsyncCatcher.catchOp("player tracker update"); // Spigot
|
||||
if (player != this.entity) {
|
||||
- Vec3 vec3d = player.position().subtract(this.entity.position()); // MC-155077, SPIGOT-5113
|
||||
+ // Paper start - remove allocation of Vec3D here
|
||||
+ //Vec3 vec3d = player.position().subtract(this.entity.position()); // MC-155077, SPIGOT-5113
|
||||
+ double vec3d_dx = player.getX() - this.entity.getX();
|
||||
+ double vec3d_dz = player.getZ() - this.entity.getZ();
|
||||
+ // Paper end - remove allocation of Vec3D here
|
||||
double d0 = (double) Math.min(this.getEffectiveRange(), (ChunkMap.this.viewDistance - 1) * 16);
|
||||
- double d1 = vec3d.x * vec3d.x + vec3d.z * vec3d.z;
|
||||
+ double d1 = vec3d_dx * vec3d_dx + vec3d_dz * vec3d_dz; // Paper
|
||||
double d2 = d0 * d0;
|
||||
boolean flag = d1 <= d2 && this.entity.broadcastToPlayer(player);
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
index 5e3f489964489e0facc93a823a1cb84b4c8abce7..1a4350b40d15edba998e3dee5f16cde4e9f50759 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
|
||||
@@ -143,8 +143,12 @@ public class ServerEntity {
|
||||
++this.teleportDelay;
|
||||
i = Mth.floor(this.entity.getYRot() * 256.0F / 360.0F);
|
||||
j = Mth.floor(this.entity.getXRot() * 256.0F / 360.0F);
|
||||
- Vec3 vec3d = this.entity.position().subtract(ClientboundMoveEntityPacket.packetToEntity(this.xp, this.yp, this.zp));
|
||||
- boolean flag1 = vec3d.lengthSqr() >= 7.62939453125E-6D;
|
||||
+ // Paper start - reduce allocation of Vec3D here
|
||||
+ double vec3d_dx = this.entity.getX() - 2.44140625E-4D*(this.xp);
|
||||
+ double vec3d_dy = this.entity.getY() - 2.44140625E-4D*(this.yp);
|
||||
+ double vec3d_dz = this.entity.getZ() - 2.44140625E-4D*(this.zp);
|
||||
+ boolean flag1 = (vec3d_dx * vec3d_dx + vec3d_dy * vec3d_dy + vec3d_dz * vec3d_dz) >= 7.62939453125E-6D;
|
||||
+ // Paper end - reduce allocation of Vec3D here
|
||||
Packet<?> packet1 = null;
|
||||
boolean flag2 = flag1 || this.tickCount % 60 == 0;
|
||||
boolean flag3 = Math.abs(i - this.yRotp) >= 1 || Math.abs(j - this.xRotp) >= 1;
|
||||
@@ -161,9 +165,11 @@ public class ServerEntity {
|
||||
// CraftBukkit end
|
||||
|
||||
if (this.tickCount > 0 || this.entity instanceof AbstractArrow) {
|
||||
- long k = ClientboundMoveEntityPacket.entityToPacket(vec3d.x);
|
||||
- long l = ClientboundMoveEntityPacket.entityToPacket(vec3d.y);
|
||||
- long i1 = ClientboundMoveEntityPacket.entityToPacket(vec3d.z);
|
||||
+ // Paper start - remove allocation of Vec3D here
|
||||
+ long k = ClientboundMoveEntityPacket.entityToPacket(vec3d_dx);
|
||||
+ long l = ClientboundMoveEntityPacket.entityToPacket(vec3d_dy);
|
||||
+ long i1 = ClientboundMoveEntityPacket.entityToPacket(vec3d_dz);
|
||||
+ // Paper end - remove allocation of Vec3D here
|
||||
boolean flag4 = k < -32768L || k > 32767L || l < -32768L || l > 32767L || i1 < -32768L || i1 > 32767L;
|
||||
|
||||
if (!flag4 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.isOnGround()) {
|
|
@ -1,26 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: kickash32 <kickash32@gmail.com>
|
||||
Date: Fri, 15 May 2020 01:10:03 -0400
|
||||
Subject: [PATCH] Ensure safe gateway teleport
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java
|
||||
index 4e47b641a2aae8e82890cda1bdb209803472b1d4..4e435a16a16c703a2978f4dc82606b8710742670 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java
|
||||
@@ -105,7 +105,14 @@ public class TheEndGatewayBlockEntity extends TheEndPortalBlockEntity {
|
||||
List<Entity> list = world.getEntitiesOfClass(Entity.class, new AABB(pos), TheEndGatewayBlockEntity::canEntityTeleport);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
- TheEndGatewayBlockEntity.teleportEntity(world, pos, state, (Entity) list.get(world.random.nextInt(list.size())), blockEntity);
|
||||
+ // Paper start
|
||||
+ for (Entity entity : list) {
|
||||
+ if (entity.canChangeDimensions()) {
|
||||
+ TheEndGatewayBlockEntity.teleportEntity(world, pos, state, entity, blockEntity);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
if (blockEntity.age % 2400L == 0L) {
|
|
@ -1,61 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Mariell Hoversholm <proximyst@proximyst.com>
|
||||
Date: Sat, 16 May 2020 10:12:15 +0200
|
||||
Subject: [PATCH] Add option for console having all permissions
|
||||
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
index 855db893080fc8784dc402a4fd7c3080dc9cdc15..62a0f6772bacfb8ad90c15493e0d48e29b70fac1 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
@@ -446,4 +446,9 @@ public class PaperConfig {
|
||||
config.set("settings.unsupported-settings.allow-permanent-block-break-exploits-readme", "This setting controls if players should be able to break bedrock, end portals and other intended to be permanent blocks.");
|
||||
allowBlockPermanentBreakingExploits = getBoolean("settings.unsupported-settings.allow-permanent-block-break-exploits", allowBlockPermanentBreakingExploits);
|
||||
}
|
||||
+
|
||||
+ public static boolean consoleHasAllPermissions = false;
|
||||
+ private static void consoleHasAllPermissions() {
|
||||
+ consoleHasAllPermissions = getBoolean("settings.console-has-all-permissions", consoleHasAllPermissions);
|
||||
+ }
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
|
||||
index 324e6d1a4fadd3e557e4ba05f04e6a5891cc54df..db77cdee224d14ec91e8d530dd65da51f3661a9c 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
|
||||
@@ -93,5 +93,15 @@ public class CraftConsoleCommandSender extends ServerCommandSender implements Co
|
||||
public void sendMessage(final net.kyori.adventure.identity.Identity identity, final net.kyori.adventure.text.Component message, final net.kyori.adventure.audience.MessageType type) {
|
||||
this.sendRawMessage(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(message));
|
||||
}
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean hasPermission(String name) {
|
||||
+ return com.destroystokyo.paper.PaperConfig.consoleHasAllPermissions || super.hasPermission(name);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean hasPermission(org.bukkit.permissions.Permission perm) {
|
||||
+ return com.destroystokyo.paper.PaperConfig.consoleHasAllPermissions || super.hasPermission(perm);
|
||||
+ }
|
||||
// Paper end
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java
|
||||
index 07f7effaa46afdd8766e3e6bfd8cb923e55f68cf..54e358ffb9c89469a7cd0df6493caf6162a37a21 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java
|
||||
@@ -46,4 +46,16 @@ public class CraftRemoteConsoleCommandSender extends ServerCommandSender impleme
|
||||
public void setOp(boolean value) {
|
||||
throw new UnsupportedOperationException("Cannot change operator status of remote controller.");
|
||||
}
|
||||
+
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public boolean hasPermission(String name) {
|
||||
+ return com.destroystokyo.paper.PaperConfig.consoleHasAllPermissions || super.hasPermission(name);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean hasPermission(org.bukkit.permissions.Permission perm) {
|
||||
+ return com.destroystokyo.paper.PaperConfig.consoleHasAllPermissions || super.hasPermission(perm);
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
|
@ -1,370 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Tue, 5 May 2020 20:40:53 -0700
|
||||
Subject: [PATCH] Optimize anyPlayerCloseEnoughForSpawning to use distance maps
|
||||
|
||||
Use a distance map to find the players in range quickly
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
index 8356a0e816324ce67df14151bdc186e60d8a223c..1324d48ff7540c4fe9148b3c6d4952ebbfb3fc22 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
@@ -76,6 +76,23 @@ public class ChunkHolder {
|
||||
boolean isUpdateQueued = false; // Paper
|
||||
private final ChunkMap chunkMap; // Paper
|
||||
|
||||
+ // Paper start - optimise anyPlayerCloseEnoughForSpawning
|
||||
+ // cached here to avoid a map lookup
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInMobSpawnRange;
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInChunkTickRange;
|
||||
+
|
||||
+ void onChunkAdd() {
|
||||
+ long key = net.minecraft.server.MCUtil.getCoordinateKey(this.pos);
|
||||
+ this.playersInMobSpawnRange = this.chunkMap.playerMobSpawnMap.getObjectsInRange(key);
|
||||
+ this.playersInChunkTickRange = this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(key);
|
||||
+ }
|
||||
+
|
||||
+ void onChunkRemove() {
|
||||
+ this.playersInMobSpawnRange = null;
|
||||
+ this.playersInChunkTickRange = null;
|
||||
+ }
|
||||
+ // Paper end - optimise anyPlayerCloseEnoughForSpawning
|
||||
+
|
||||
public ChunkHolder(ChunkPos pos, int level, LevelHeightAccessor world, LevelLightEngine lightingProvider, ChunkHolder.LevelChangeListener levelUpdateListener, ChunkHolder.PlayerProvider playersWatchingChunkProvider) {
|
||||
this.futures = new AtomicReferenceArray(ChunkHolder.CHUNK_STATUSES.size());
|
||||
this.fullChunkFuture = ChunkHolder.UNLOADED_LEVEL_CHUNK_FUTURE;
|
||||
@@ -97,6 +114,7 @@ public class ChunkHolder {
|
||||
this.setTicketLevel(level);
|
||||
this.changedBlocksPerSection = new ShortSet[world.getSectionsCount()];
|
||||
this.chunkMap = (ChunkMap)playersWatchingChunkProvider; // Paper
|
||||
+ this.onChunkAdd(); // Paper - optimise anyPlayerCloseEnoughForSpawning
|
||||
}
|
||||
|
||||
// Paper start
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 38efde791c99dd22de71183676d0301a1d5a14e0..63e264c862053874585b0f6aad2f89389c2dadcb 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -181,11 +181,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
final CallbackExecutor chunkLoadConversionCallbackExecutor = new CallbackExecutor(); // Paper
|
||||
// Paper start - distance maps
|
||||
private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
|
||||
+ // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
+ // A note about the naming used here:
|
||||
+ // Previously, mojang used a "spawn range" of 8 for controlling both ticking and
|
||||
+ // mob spawn range. However, spigot makes the spawn range configurable by
|
||||
+ // checking if the chunk is in the tick range (8) and the spawn range
|
||||
+ // obviously this means a spawn range > 8 cannot be implemented
|
||||
+
|
||||
+ // these maps are named after spigot's uses
|
||||
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap; // this map is absent from updateMaps since it's controlled at the start of the chunkproviderserver tick
|
||||
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerChunkTickRangeMap;
|
||||
+ // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
|
||||
void addPlayerToDistanceMaps(ServerPlayer player) {
|
||||
int chunkX = MCUtil.getChunkCoordinate(player.getX());
|
||||
int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
|
||||
// Note: players need to be explicitly added to distance maps before they can be updated
|
||||
+ this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
// Paper start - per player mob spawning
|
||||
if (this.playerMobDistanceMap != null) {
|
||||
this.playerMobDistanceMap.add(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
|
||||
@@ -195,6 +207,10 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
|
||||
void removePlayerFromDistanceMaps(ServerPlayer player) {
|
||||
|
||||
+ // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
+ this.playerMobSpawnMap.remove(player);
|
||||
+ this.playerChunkTickRangeMap.remove(player);
|
||||
+ // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
// Paper start - per player mob spawning
|
||||
if (this.playerMobDistanceMap != null) {
|
||||
this.playerMobDistanceMap.remove(player);
|
||||
@@ -206,6 +222,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
int chunkX = MCUtil.getChunkCoordinate(player.getX());
|
||||
int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
|
||||
// Note: players need to be explicitly added to distance maps before they can be updated
|
||||
+ this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
// Paper start - per player mob spawning
|
||||
if (this.playerMobDistanceMap != null) {
|
||||
this.playerMobDistanceMap.update(player, chunkX, chunkZ, this.distanceManager.getSimulationDistance());
|
||||
@@ -286,6 +303,38 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
this.regionManagers.add(this.dataRegionManager);
|
||||
// Paper end
|
||||
this.playerMobDistanceMap = this.level.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets) : null; // Paper
|
||||
+ // Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
+ this.playerChunkTickRangeMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
||||
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
||||
+ if (playerChunk != null) {
|
||||
+ playerChunk.playersInChunkTickRange = newState;
|
||||
+ }
|
||||
+ },
|
||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
||||
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
||||
+ if (playerChunk != null) {
|
||||
+ playerChunk.playersInChunkTickRange = newState;
|
||||
+ }
|
||||
+ });
|
||||
+ this.playerMobSpawnMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
||||
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
||||
+ if (playerChunk != null) {
|
||||
+ playerChunk.playersInMobSpawnRange = newState;
|
||||
+ }
|
||||
+ },
|
||||
+ (ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newState) -> {
|
||||
+ ChunkHolder playerChunk = ChunkMap.this.getUpdatingChunkIfPresent(MCUtil.getCoordinateKey(rangeX, rangeZ));
|
||||
+ if (playerChunk != null) {
|
||||
+ playerChunk.playersInMobSpawnRange = newState;
|
||||
+ }
|
||||
+ });
|
||||
+ // Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
}
|
||||
|
||||
protected ChunkGenerator generator() {
|
||||
@@ -537,6 +586,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
holder = (ChunkHolder) this.pendingUnloads.remove(pos);
|
||||
if (holder != null) {
|
||||
holder.setTicketLevel(level);
|
||||
+ holder.onChunkAdd(); // Paper - optimise anyPlayerCloseEnoughForSpawning - PUT HERE AFTER RE-ADDING ONLY
|
||||
} else {
|
||||
holder = new ChunkHolder(new ChunkPos(pos), level, this.level, this.lightEngine, this.queueSorter, this);
|
||||
// Paper start
|
||||
@@ -632,6 +682,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
ChunkHolder playerchunk = (ChunkHolder) this.updatingChunkMap.remove(j);
|
||||
|
||||
if (playerchunk != null) {
|
||||
+ playerchunk.onChunkRemove(); // Paper
|
||||
this.pendingUnloads.put(j, playerchunk);
|
||||
this.modified = true;
|
||||
++i;
|
||||
@@ -1370,43 +1421,48 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
return this.anyPlayerCloseEnoughForSpawning(pos, false);
|
||||
}
|
||||
|
||||
- boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkcoordintpair, boolean reducedRange) {
|
||||
- int chunkRange = level.spigotConfig.mobSpawnRange;
|
||||
- chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
|
||||
- chunkRange = (chunkRange > 8) ? 8 : chunkRange;
|
||||
-
|
||||
- final int finalChunkRange = chunkRange; // Paper for lambda below
|
||||
- //double blockRange = (reducedRange) ? Math.pow(chunkRange << 4, 2) : 16384.0D; // Paper - use from event
|
||||
- double blockRange = 16384.0D; // Paper
|
||||
- // Spigot end
|
||||
- long i = chunkcoordintpair.toLong();
|
||||
+ // Paper start - optimise anyPlayerCloseEnoughForSpawning
|
||||
+ final boolean anyPlayerCloseEnoughForSpawning(ChunkPos chunkcoordintpair, boolean reducedRange) {
|
||||
+ return this.anyPlayerCloseEnoughForSpawning(this.getUpdatingChunkIfPresent(chunkcoordintpair.toLong()), chunkcoordintpair, reducedRange);
|
||||
+ }
|
||||
|
||||
- if (!this.distanceManager.hasPlayersNearby(i)) {
|
||||
+ final boolean anyPlayerCloseEnoughForSpawning(ChunkHolder playerchunk, ChunkPos chunkcoordintpair, boolean reducedRange) {
|
||||
+ // this function is so hot that removing the map lookup call can have an order of magnitude impact on its performance
|
||||
+ // tested and confirmed via System.nanoTime()
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> playersInRange = reducedRange ? playerchunk.playersInMobSpawnRange : playerchunk.playersInChunkTickRange;
|
||||
+ if (playersInRange == null) {
|
||||
return false;
|
||||
- } else {
|
||||
- Iterator iterator = this.playerMap.getPlayers(i).iterator();
|
||||
-
|
||||
- ServerPlayer entityplayer;
|
||||
+ }
|
||||
+ Object[] backingSet = playersInRange.getBackingSet();
|
||||
|
||||
- do {
|
||||
- if (!iterator.hasNext()) {
|
||||
- return false;
|
||||
+ if (reducedRange) {
|
||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
||||
+ Object raw = backingSet[i];
|
||||
+ if (!(raw instanceof ServerPlayer player)) {
|
||||
+ continue;
|
||||
}
|
||||
-
|
||||
- entityplayer = (ServerPlayer) iterator.next();
|
||||
- // Paper start - add PlayerNaturallySpawnCreaturesEvent
|
||||
- com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event;
|
||||
- blockRange = 16384.0D;
|
||||
- if (reducedRange) {
|
||||
- event = entityplayer.playerNaturallySpawnedEvent;
|
||||
- if (event == null || event.isCancelled()) return false;
|
||||
- blockRange = (double) ((event.getSpawnRadius() << 4) * (event.getSpawnRadius() << 4));
|
||||
+ // don't check spectator and whatnot, already handled by mob spawn map update
|
||||
+ if (euclideanDistanceSquared(chunkcoordintpair, player) < player.lastEntitySpawnRadiusSquared) {
|
||||
+ return true; // in range
|
||||
}
|
||||
- // Paper end
|
||||
- } while (!this.playerIsCloseEnoughForSpawning(entityplayer, chunkcoordintpair, blockRange)); // Spigot
|
||||
-
|
||||
- return true;
|
||||
+ }
|
||||
+ } else {
|
||||
+ final double range = (DistanceManager.MOB_SPAWN_RANGE * 16) * (DistanceManager.MOB_SPAWN_RANGE * 16);
|
||||
+ // before spigot, mob spawn range was actually mob spawn range + tick range, but it was split
|
||||
+ for (int i = 0, len = backingSet.length; i < len; ++i) {
|
||||
+ Object raw = backingSet[i];
|
||||
+ if (!(raw instanceof ServerPlayer player)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ // don't check spectator and whatnot, already handled by mob spawn map update
|
||||
+ if (euclideanDistanceSquared(chunkcoordintpair, player) < range) {
|
||||
+ return true; // in range
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ // no players in range
|
||||
+ return false;
|
||||
+ // Paper end - optimise anyPlayerCloseEnoughForSpawning
|
||||
}
|
||||
|
||||
public List<ServerPlayer> getPlayersCloseForSpawning(ChunkPos pos) {
|
||||
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
index 4a9d0fca55b71f817defcb4286154c0a47bede03..62118348d6fb00f063507debb488e1ff431d139c 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
@@ -49,7 +49,7 @@ public abstract class DistanceManager {
|
||||
final Long2ObjectMap<ObjectSet<ServerPlayer>> playersPerChunk = new Long2ObjectOpenHashMap();
|
||||
public final Long2ObjectOpenHashMap<SortedArraySet<Ticket<?>>> tickets = new Long2ObjectOpenHashMap();
|
||||
private final DistanceManager.ChunkTicketTracker ticketTracker = new DistanceManager.ChunkTicketTracker();
|
||||
- private final DistanceManager.FixedPlayerDistanceChunkTracker naturalSpawnChunkCounter = new DistanceManager.FixedPlayerDistanceChunkTracker(8);
|
||||
+ public static final int MOB_SPAWN_RANGE = 8; // private final ChunkMapDistance.b f = new ChunkMapDistance.b(8); // Paper - no longer used
|
||||
private final TickingTracker tickingTicketsTracker = new TickingTracker();
|
||||
private final DistanceManager.PlayerTicketTracker playerTicketManager = new DistanceManager.PlayerTicketTracker(33);
|
||||
// Paper start use a queue, but still keep unique requirement
|
||||
@@ -140,7 +140,7 @@ public abstract class DistanceManager {
|
||||
protected abstract ChunkHolder updateChunkScheduling(long pos, int level, @Nullable ChunkHolder holder, int k);
|
||||
|
||||
public boolean runAllUpdates(ChunkMap chunkStorage) {
|
||||
- this.naturalSpawnChunkCounter.runAllUpdates();
|
||||
+ //this.f.a(); // Paper - no longer used
|
||||
this.tickingTicketsTracker.runAllUpdates();
|
||||
this.playerTicketManager.runAllUpdates();
|
||||
int i = Integer.MAX_VALUE - this.ticketTracker.runDistanceUpdates(Integer.MAX_VALUE);
|
||||
@@ -289,7 +289,7 @@ public abstract class DistanceManager {
|
||||
((ObjectSet) this.playersPerChunk.computeIfAbsent(i, (j) -> {
|
||||
return new ObjectOpenHashSet();
|
||||
})).add(player);
|
||||
- this.naturalSpawnChunkCounter.update(i, 0, true);
|
||||
+ //this.f.update(i, 0, true); // Paper - no longer used
|
||||
this.playerTicketManager.update(i, 0, true);
|
||||
this.tickingTicketsTracker.addTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair);
|
||||
}
|
||||
@@ -303,7 +303,7 @@ public abstract class DistanceManager {
|
||||
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.f.update(i, Integer.MAX_VALUE, false); // Paper - no longer used
|
||||
this.playerTicketManager.update(i, Integer.MAX_VALUE, false);
|
||||
this.tickingTicketsTracker.removeTicket(TicketType.PLAYER, chunkcoordintpair, this.getPlayerTicketLevel(), chunkcoordintpair);
|
||||
}
|
||||
@@ -347,13 +347,17 @@ public abstract class DistanceManager {
|
||||
// Paper end
|
||||
|
||||
public int getNaturalSpawnChunkCount() {
|
||||
- this.naturalSpawnChunkCounter.runAllUpdates();
|
||||
- return this.naturalSpawnChunkCounter.chunks.size();
|
||||
+ // Paper start - use distance map to implement
|
||||
+ // note: this is the spawn chunk count
|
||||
+ return this.chunkMap.playerChunkTickRangeMap.size();
|
||||
+ // Paper end - use distance map to implement
|
||||
}
|
||||
|
||||
public boolean hasPlayersNearby(long chunkPos) {
|
||||
- this.naturalSpawnChunkCounter.runAllUpdates();
|
||||
- return this.naturalSpawnChunkCounter.chunks.containsKey(chunkPos);
|
||||
+ // Paper start - use distance map to implement
|
||||
+ // note: this is the is spawn chunk method
|
||||
+ return this.chunkMap.playerChunkTickRangeMap.getObjectsInRange(chunkPos) != null;
|
||||
+ // Paper end - use distance map to implement
|
||||
}
|
||||
|
||||
public String getDebugStatus() {
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
index cf5a5d98420c3849621bfdfae7bda7d5cb4b2dc9..846e6f41fdb6413b10f908c6b3f5c199a9f5ef38 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
@@ -872,6 +872,37 @@ public class ServerChunkCache extends ChunkSource {
|
||||
if (flag) {
|
||||
this.chunkMap.tick();
|
||||
} else {
|
||||
+ // Paper start - optimize isOutisdeRange
|
||||
+ ChunkMap playerChunkMap = this.chunkMap;
|
||||
+ for (ServerPlayer player : this.level.players) {
|
||||
+ if (!player.affectsSpawning || player.isSpectator()) {
|
||||
+ playerChunkMap.playerMobSpawnMap.remove(player);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ int viewDistance = this.chunkMap.getEffectiveViewDistance();
|
||||
+
|
||||
+ // copied and modified from isOutisdeRange
|
||||
+ int chunkRange = level.spigotConfig.mobSpawnRange;
|
||||
+ chunkRange = (chunkRange > viewDistance) ? (byte)viewDistance : chunkRange;
|
||||
+ chunkRange = (chunkRange > DistanceManager.MOB_SPAWN_RANGE) ? DistanceManager.MOB_SPAWN_RANGE : chunkRange;
|
||||
+
|
||||
+ com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(player.getBukkitEntity(), (byte)chunkRange);
|
||||
+ event.callEvent();
|
||||
+ if (event.isCancelled() || event.getSpawnRadius() < 0 || playerChunkMap.playerChunkTickRangeMap.getLastViewDistance(player) == -1) {
|
||||
+ playerChunkMap.playerMobSpawnMap.remove(player);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ int range = Math.min(event.getSpawnRadius(), 32); // limit to max view distance
|
||||
+ int chunkX = net.minecraft.server.MCUtil.getChunkCoordinate(player.getX());
|
||||
+ int chunkZ = net.minecraft.server.MCUtil.getChunkCoordinate(player.getZ());
|
||||
+
|
||||
+ playerChunkMap.playerMobSpawnMap.addOrUpdate(player, chunkX, chunkZ, range);
|
||||
+ player.lastEntitySpawnRadiusSquared = (double)((range << 4) * (range << 4)); // used in anyPlayerCloseEnoughForSpawning
|
||||
+ player.playerNaturallySpawnedEvent = event;
|
||||
+ }
|
||||
+ // Paper end - optimize isOutisdeRange
|
||||
LevelData worlddata = this.level.getLevelData();
|
||||
ProfilerFiller gameprofilerfiller = this.level.getProfiler();
|
||||
|
||||
@@ -915,15 +946,7 @@ public class ServerChunkCache extends ChunkSource {
|
||||
boolean flag2 = this.level.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING) && !this.level.players().isEmpty(); // CraftBukkit
|
||||
|
||||
Collections.shuffle(list);
|
||||
- // Paper start - call player naturally spawn event
|
||||
- int chunkRange = level.spigotConfig.mobSpawnRange;
|
||||
- chunkRange = (chunkRange > level.spigotConfig.viewDistance) ? (byte) level.spigotConfig.viewDistance : chunkRange;
|
||||
- chunkRange = Math.min(chunkRange, 8);
|
||||
- for (ServerPlayer entityPlayer : this.level.players()) {
|
||||
- entityPlayer.playerNaturallySpawnedEvent = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(entityPlayer.getBukkitEntity(), (byte) chunkRange);
|
||||
- entityPlayer.playerNaturallySpawnedEvent.callEvent();
|
||||
- };
|
||||
- // Paper end
|
||||
+ // Paper - moved natural spawn event up
|
||||
Iterator iterator1 = list.iterator();
|
||||
|
||||
while (iterator1.hasNext()) {
|
||||
@@ -931,9 +954,9 @@ public class ServerChunkCache extends ChunkSource {
|
||||
LevelChunk chunk1 = chunkproviderserver_a.chunk;
|
||||
ChunkPos chunkcoordintpair = chunk1.getPos();
|
||||
|
||||
- if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair)) {
|
||||
+ if (this.level.isNaturalSpawningAllowed(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, false)) { // Paper - optimise anyPlayerCloseEnoughForSpawning
|
||||
chunk1.incrementInhabitedTime(j);
|
||||
- if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkcoordintpair, true)) { // Spigot
|
||||
+ if (flag2 && (this.spawnEnemies || this.spawnFriendlies) && this.level.getWorldBorder().isWithinBounds(chunkcoordintpair) && this.chunkMap.anyPlayerCloseEnoughForSpawning(chunkproviderserver_a.holder, chunkcoordintpair, true)) { // Spigot // Paper - optimise anyPlayerCloseEnoughForSpawning
|
||||
NaturalSpawner.spawnForChunk(this.level, chunk1, spawnercreature_d, this.spawnFriendlies, this.spawnEnemies, flag1);
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
index f8913f10e3e24c4da7c251bc4bc0ab0820807f57..90a844229e4a3ef5193cffc2c742b4c5a305a91c 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -253,6 +253,7 @@ public class ServerPlayer extends Player {
|
||||
// CraftBukkit end
|
||||
public PlayerNaturallySpawnCreaturesEvent playerNaturallySpawnedEvent; // Paper
|
||||
|
||||
+ public double lastEntitySpawnRadiusSquared; // Paper - optimise isOutsideRange, this field is in blocks
|
||||
public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> cachedSingleHashSet; // Paper
|
||||
|
||||
public ServerPlayer(MinecraftServer server, ServerLevel world, GameProfile profile) {
|
|
@ -1,395 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Tue, 5 May 2020 20:18:05 -0700
|
||||
Subject: [PATCH] Use distance map to optimise entity tracker
|
||||
|
||||
Use the distance map to find candidate players for tracking.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 63e264c862053874585b0f6aad2f89389c2dadcb..2c9cd3619b0759905637eff746a761ebe89a38d8 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -70,6 +70,7 @@ import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket;
|
||||
import net.minecraft.network.protocol.game.DebugPackets;
|
||||
import net.minecraft.server.MCUtil;
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListener;
|
||||
import net.minecraft.server.network.ServerPlayerConnection;
|
||||
import net.minecraft.util.CsvOutput;
|
||||
@@ -192,10 +193,35 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerMobSpawnMap; // this map is absent from updateMaps since it's controlled at the start of the chunkproviderserver tick
|
||||
public final com.destroystokyo.paper.util.misc.PlayerAreaMap playerChunkTickRangeMap;
|
||||
// Paper end - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
+ // Paper start - use distance map to optimise tracker
|
||||
+ public static boolean isLegacyTrackingEntity(Entity entity) {
|
||||
+ return entity.isLegacyTrackingEntity;
|
||||
+ }
|
||||
+
|
||||
+ // inlined EnumMap, TrackingRange.TrackingRangeType
|
||||
+ static final org.spigotmc.TrackingRange.TrackingRangeType[] TRACKING_RANGE_TYPES = org.spigotmc.TrackingRange.TrackingRangeType.values();
|
||||
+ public final com.destroystokyo.paper.util.misc.PlayerAreaMap[] playerEntityTrackerTrackMaps;
|
||||
+ final int[] entityTrackerTrackRanges;
|
||||
+ public final int getEntityTrackerRange(final int ordinal) {
|
||||
+ return this.entityTrackerTrackRanges[ordinal];
|
||||
+ }
|
||||
+
|
||||
+ private int convertSpigotRangeToVanilla(final int vanilla) {
|
||||
+ return MinecraftServer.getServer().getScaledTrackingDistance(vanilla);
|
||||
+ }
|
||||
+ // Paper end - use distance map to optimise tracker
|
||||
|
||||
void addPlayerToDistanceMaps(ServerPlayer player) {
|
||||
int chunkX = MCUtil.getChunkCoordinate(player.getX());
|
||||
int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
|
||||
+ // Paper start - use distance map to optimise entity tracker
|
||||
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
|
||||
+ com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i];
|
||||
+ int trackRange = this.entityTrackerTrackRanges[i];
|
||||
+
|
||||
+ trackMap.add(player, chunkX, chunkZ, Math.min(trackRange, this.getEffectiveViewDistance()));
|
||||
+ }
|
||||
+ // Paper end - use distance map to optimise entity tracker
|
||||
// Note: players need to be explicitly added to distance maps before they can be updated
|
||||
this.playerChunkTickRangeMap.add(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
// Paper start - per player mob spawning
|
||||
@@ -207,6 +233,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
|
||||
void removePlayerFromDistanceMaps(ServerPlayer player) {
|
||||
|
||||
+ // Paper start - use distance map to optimise tracker
|
||||
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
|
||||
+ this.playerEntityTrackerTrackMaps[i].remove(player);
|
||||
+ }
|
||||
+ // Paper end - use distance map to optimise tracker
|
||||
// Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
this.playerMobSpawnMap.remove(player);
|
||||
this.playerChunkTickRangeMap.remove(player);
|
||||
@@ -222,6 +253,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
int chunkX = MCUtil.getChunkCoordinate(player.getX());
|
||||
int chunkZ = MCUtil.getChunkCoordinate(player.getZ());
|
||||
// Note: players need to be explicitly added to distance maps before they can be updated
|
||||
+ // Paper start - use distance map to optimise entity tracker
|
||||
+ for (int i = 0, len = TRACKING_RANGE_TYPES.length; i < len; ++i) {
|
||||
+ com.destroystokyo.paper.util.misc.PlayerAreaMap trackMap = this.playerEntityTrackerTrackMaps[i];
|
||||
+ int trackRange = this.entityTrackerTrackRanges[i];
|
||||
+
|
||||
+ trackMap.update(player, chunkX, chunkZ, Math.min(trackRange, this.getEffectiveViewDistance()));
|
||||
+ }
|
||||
+ // Paper end - use distance map to optimise entity tracker
|
||||
this.playerChunkTickRangeMap.update(player, chunkX, chunkZ, DistanceManager.MOB_SPAWN_RANGE); // Paper - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
// Paper start - per player mob spawning
|
||||
if (this.playerMobDistanceMap != null) {
|
||||
@@ -303,6 +342,45 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
this.regionManagers.add(this.dataRegionManager);
|
||||
// Paper end
|
||||
this.playerMobDistanceMap = this.level.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets) : null; // Paper
|
||||
+ // Paper start - use distance map to optimise entity tracker
|
||||
+ this.playerEntityTrackerTrackMaps = new com.destroystokyo.paper.util.misc.PlayerAreaMap[TRACKING_RANGE_TYPES.length];
|
||||
+ this.entityTrackerTrackRanges = new int[TRACKING_RANGE_TYPES.length];
|
||||
+
|
||||
+ org.spigotmc.SpigotWorldConfig spigotWorldConfig = this.level.spigotConfig;
|
||||
+
|
||||
+ for (int ordinal = 0, len = TRACKING_RANGE_TYPES.length; ordinal < len; ++ordinal) {
|
||||
+ org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = TRACKING_RANGE_TYPES[ordinal];
|
||||
+ int configuredSpigotValue;
|
||||
+ switch (trackingRangeType) {
|
||||
+ case PLAYER:
|
||||
+ configuredSpigotValue = spigotWorldConfig.playerTrackingRange;
|
||||
+ break;
|
||||
+ case ANIMAL:
|
||||
+ configuredSpigotValue = spigotWorldConfig.animalTrackingRange;
|
||||
+ break;
|
||||
+ case MONSTER:
|
||||
+ configuredSpigotValue = spigotWorldConfig.monsterTrackingRange;
|
||||
+ break;
|
||||
+ case MISC:
|
||||
+ configuredSpigotValue = spigotWorldConfig.miscTrackingRange;
|
||||
+ break;
|
||||
+ case OTHER:
|
||||
+ configuredSpigotValue = spigotWorldConfig.otherTrackingRange;
|
||||
+ break;
|
||||
+ case ENDERDRAGON:
|
||||
+ configuredSpigotValue = EntityType.ENDER_DRAGON.clientTrackingRange() * 16;
|
||||
+ break;
|
||||
+ default:
|
||||
+ throw new IllegalStateException("Missing case for enum " + trackingRangeType);
|
||||
+ }
|
||||
+ configuredSpigotValue = convertSpigotRangeToVanilla(configuredSpigotValue);
|
||||
+
|
||||
+ int trackRange = (configuredSpigotValue >>> 4) + ((configuredSpigotValue & 15) != 0 ? 1 : 0);
|
||||
+ this.entityTrackerTrackRanges[ordinal] = trackRange;
|
||||
+
|
||||
+ this.playerEntityTrackerTrackMaps[ordinal] = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets);
|
||||
+ }
|
||||
+ // Paper end - use distance map to optimise entity tracker
|
||||
// Paper start - optimise ChunkMap#anyPlayerCloseEnoughForSpawning
|
||||
this.playerChunkTickRangeMap = new com.destroystokyo.paper.util.misc.PlayerAreaMap(this.pooledLinkedPlayerHashSets,
|
||||
(ServerPlayer player, int rangeX, int rangeZ, int currPosX, int currPosZ, int prevPosX, int prevPosZ,
|
||||
@@ -1544,17 +1622,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
}
|
||||
|
||||
public void move(ServerPlayer player) {
|
||||
- ObjectIterator objectiterator = this.entityMap.values().iterator();
|
||||
-
|
||||
- while (objectiterator.hasNext()) {
|
||||
- ChunkMap.TrackedEntity playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next();
|
||||
-
|
||||
- if (playerchunkmap_entitytracker.entity == player) {
|
||||
- playerchunkmap_entitytracker.updatePlayers(this.level.players());
|
||||
- } else {
|
||||
- playerchunkmap_entitytracker.updatePlayer(player);
|
||||
- }
|
||||
- }
|
||||
+ // Paper - delay this logic for the entity tracker tick, no need to duplicate it
|
||||
|
||||
int i = SectionPos.blockToSectionCoord(player.getBlockX());
|
||||
int j = SectionPos.blockToSectionCoord(player.getBlockZ());
|
||||
@@ -1681,7 +1749,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
|
||||
entity.tracker = playerchunkmap_entitytracker; // Paper - Fast access to tracker
|
||||
this.entityMap.put(entity.getId(), playerchunkmap_entitytracker);
|
||||
- playerchunkmap_entitytracker.updatePlayers(this.level.players());
|
||||
+ playerchunkmap_entitytracker.updatePlayers(entity.getPlayersInTrackRange()); // Paper - don't search all players
|
||||
if (entity instanceof ServerPlayer) {
|
||||
ServerPlayer entityplayer = (ServerPlayer) entity;
|
||||
|
||||
@@ -1725,7 +1793,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
entity.tracker = null; // Paper - We're no longer tracked
|
||||
}
|
||||
|
||||
+ // Paper start - optimised tracker
|
||||
+ private final void processTrackQueue() {
|
||||
+ this.level.timings.tracker1.startTiming();
|
||||
+ try {
|
||||
+ for (TrackedEntity tracker : this.entityMap.values()) {
|
||||
+ // update tracker entry
|
||||
+ tracker.updatePlayers(tracker.entity.getPlayersInTrackRange());
|
||||
+ }
|
||||
+ } finally {
|
||||
+ this.level.timings.tracker1.stopTiming();
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ this.level.timings.tracker2.startTiming();
|
||||
+ try {
|
||||
+ for (TrackedEntity tracker : this.entityMap.values()) {
|
||||
+ tracker.serverEntity.sendChanges();
|
||||
+ }
|
||||
+ } finally {
|
||||
+ this.level.timings.tracker2.stopTiming();
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - optimised tracker
|
||||
+
|
||||
protected void tick() {
|
||||
+ // Paper start - optimized tracker
|
||||
+ if (true) {
|
||||
+ this.processTrackQueue();
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - optimized tracker
|
||||
List<ServerPlayer> list = Lists.newArrayList();
|
||||
List<ServerPlayer> list1 = this.level.players();
|
||||
ObjectIterator objectiterator = this.entityMap.values().iterator();
|
||||
@@ -1801,23 +1899,31 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
DebugPackets.sendPoiPacketsForChunk(this.level, chunk.getPos());
|
||||
List<Entity> list = Lists.newArrayList();
|
||||
List<Entity> list1 = Lists.newArrayList();
|
||||
- ObjectIterator objectiterator = this.entityMap.values().iterator();
|
||||
+ // Paper start - optimise entity tracker
|
||||
+ // use the chunk entity list, not the whole trackedEntities map...
|
||||
+ Entity[] entities = chunk.entities.getRawData();
|
||||
+ for (int i = 0, size = chunk.entities.size(); i < size; ++i) {
|
||||
+ Entity entity = entities[i];
|
||||
+ if (entity == player) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ ChunkMap.TrackedEntity tracker = this.entityMap.get(entity.getId());
|
||||
+ if (tracker != null) { // dumb plugins... move on...
|
||||
+ tracker.updatePlayer(player);
|
||||
+ }
|
||||
|
||||
- while (objectiterator.hasNext()) {
|
||||
- ChunkMap.TrackedEntity playerchunkmap_entitytracker = (ChunkMap.TrackedEntity) objectiterator.next();
|
||||
- Entity entity = playerchunkmap_entitytracker.entity;
|
||||
+ // keep the vanilla logic here - this is REQUIRED or else passengers and their vehicles disappear!
|
||||
+ // (and god knows what the leash thing is)
|
||||
|
||||
- if (entity != player && entity.chunkPosition().equals(chunk.getPos())) {
|
||||
- playerchunkmap_entitytracker.updatePlayer(player);
|
||||
- if (entity instanceof Mob && ((Mob) entity).getLeashHolder() != null) {
|
||||
- list.add(entity);
|
||||
- }
|
||||
+ if (entity instanceof Mob && ((Mob)entity).getLeashHolder() != null) {
|
||||
+ list.add(entity);
|
||||
+ }
|
||||
|
||||
- if (!entity.getPassengers().isEmpty()) {
|
||||
- list1.add(entity);
|
||||
- }
|
||||
+ if (!entity.getPassengers().isEmpty()) {
|
||||
+ list1.add(entity);
|
||||
}
|
||||
}
|
||||
+ // Paper end - optimise entity tracker
|
||||
|
||||
Iterator iterator;
|
||||
Entity entity1;
|
||||
@@ -1893,6 +1999,42 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
this.lastSectionPos = SectionPos.of(entity);
|
||||
}
|
||||
|
||||
+ // Paper start - use distance map to optimise tracker
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> lastTrackerCandidates;
|
||||
+
|
||||
+ final void updatePlayers(com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> newTrackerCandidates) {
|
||||
+ com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> oldTrackerCandidates = this.lastTrackerCandidates;
|
||||
+ this.lastTrackerCandidates = newTrackerCandidates;
|
||||
+
|
||||
+ if (newTrackerCandidates != null) {
|
||||
+ Object[] rawData = newTrackerCandidates.getBackingSet();
|
||||
+ for (int i = 0, len = rawData.length; i < len; ++i) {
|
||||
+ Object raw = rawData[i];
|
||||
+ if (!(raw instanceof ServerPlayer)) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ ServerPlayer player = (ServerPlayer)raw;
|
||||
+ this.updatePlayer(player);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (oldTrackerCandidates == newTrackerCandidates) {
|
||||
+ // this is likely the case.
|
||||
+ // means there has been no range changes, so we can just use the above for tracking.
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ // stuff could have been removed, so we need to check the trackedPlayers set
|
||||
+ // for players that were removed
|
||||
+
|
||||
+ for (ServerPlayerConnection conn : this.seenBy.toArray(new ServerPlayerConnection[0])) { // avoid CME
|
||||
+ if (newTrackerCandidates == null || !newTrackerCandidates.contains(conn.getPlayer())) {
|
||||
+ this.updatePlayer(conn.getPlayer());
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end - use distance map to optimise tracker
|
||||
+
|
||||
public boolean equals(Object object) {
|
||||
return object instanceof ChunkMap.TrackedEntity ? ((ChunkMap.TrackedEntity) object).entity.getId() == this.entity.getId() : false;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index f69bc9d81750feb108119347f984de6bca24ae44..14b3f14d2489bdbeb89886a115db2ba78cb97ad7 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -54,6 +54,7 @@ import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
+import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -362,6 +363,39 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
}
|
||||
// Paper end
|
||||
|
||||
+ // Paper start - optimise entity tracking
|
||||
+ final org.spigotmc.TrackingRange.TrackingRangeType trackingRangeType = org.spigotmc.TrackingRange.getTrackingRangeType(this);
|
||||
+
|
||||
+ public boolean isLegacyTrackingEntity = false;
|
||||
+
|
||||
+ public final void setLegacyTrackingEntity(final boolean isLegacyTrackingEntity) {
|
||||
+ this.isLegacyTrackingEntity = isLegacyTrackingEntity;
|
||||
+ }
|
||||
+
|
||||
+ public final com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<ServerPlayer> getPlayersInTrackRange() {
|
||||
+ // determine highest range of passengers
|
||||
+ if (this.passengers.isEmpty()) {
|
||||
+ return ((ServerLevel)this.level).getChunkSource().chunkMap.playerEntityTrackerTrackMaps[this.trackingRangeType.ordinal()]
|
||||
+ .getObjectsInRange(MCUtil.getCoordinateKey(this));
|
||||
+ }
|
||||
+ Iterable<Entity> passengers = this.getIndirectPassengers();
|
||||
+ net.minecraft.server.level.ChunkMap chunkMap = ((ServerLevel)this.level).getChunkSource().chunkMap;
|
||||
+ org.spigotmc.TrackingRange.TrackingRangeType type = this.trackingRangeType;
|
||||
+ int range = chunkMap.getEntityTrackerRange(type.ordinal());
|
||||
+
|
||||
+ for (Entity passenger : passengers) {
|
||||
+ org.spigotmc.TrackingRange.TrackingRangeType passengerType = passenger.trackingRangeType;
|
||||
+ int passengerRange = chunkMap.getEntityTrackerRange(passengerType.ordinal());
|
||||
+ if (passengerRange > range) {
|
||||
+ type = passengerType;
|
||||
+ range = passengerRange;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return chunkMap.playerEntityTrackerTrackMaps[type.ordinal()].getObjectsInRange(MCUtil.getCoordinateKey(this));
|
||||
+ }
|
||||
+ // Paper end - optimise entity tracking
|
||||
+
|
||||
public Entity(EntityType<?> type, Level world) {
|
||||
this.id = Entity.ENTITY_COUNTER.incrementAndGet();
|
||||
this.passengers = ImmutableList.of();
|
||||
diff --git a/src/main/java/org/spigotmc/TrackingRange.java b/src/main/java/org/spigotmc/TrackingRange.java
|
||||
index 55ce69b5fe097841d00ef5c241459dce9bb0d4db..e5bcbfe175a697e04886d04543e1278b7e83a184 100644
|
||||
--- a/src/main/java/org/spigotmc/TrackingRange.java
|
||||
+++ b/src/main/java/org/spigotmc/TrackingRange.java
|
||||
@@ -24,6 +24,7 @@ public class TrackingRange
|
||||
{
|
||||
return defaultRange;
|
||||
}
|
||||
+ if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon) return defaultRange; // Paper - enderdragon is exempt
|
||||
SpigotWorldConfig config = entity.level.spigotConfig;
|
||||
if ( entity instanceof ServerPlayer )
|
||||
{
|
||||
@@ -47,8 +48,48 @@ public class TrackingRange
|
||||
return config.miscTrackingRange;
|
||||
} else
|
||||
{
|
||||
- if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon) return ((net.minecraft.server.level.ServerLevel)(entity.getCommandSenderWorld())).getChunkSource().chunkMap.getEffectiveViewDistance(); // Paper - enderdragon is exempt
|
||||
return config.otherTrackingRange;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Paper start - optimise entity tracking
|
||||
+ // copied from above, TODO check on update
|
||||
+ public static TrackingRangeType getTrackingRangeType(Entity entity)
|
||||
+ {
|
||||
+ if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon) return TrackingRangeType.ENDERDRAGON; // Paper - enderdragon is exempt
|
||||
+ if ( entity instanceof ServerPlayer )
|
||||
+ {
|
||||
+ return TrackingRangeType.PLAYER;
|
||||
+ // Paper start - Simplify and set water mobs to animal tracking range
|
||||
+ }
|
||||
+ switch (entity.activationType) {
|
||||
+ case RAIDER:
|
||||
+ case MONSTER:
|
||||
+ case FLYING_MONSTER:
|
||||
+ return TrackingRangeType.MONSTER;
|
||||
+ case WATER:
|
||||
+ case VILLAGER:
|
||||
+ case ANIMAL:
|
||||
+ return TrackingRangeType.ANIMAL;
|
||||
+ case MISC:
|
||||
+ }
|
||||
+ if ( entity instanceof ItemFrame || entity instanceof Painting || entity instanceof ItemEntity || entity instanceof ExperienceOrb )
|
||||
+ // Paper end
|
||||
+ {
|
||||
+ return TrackingRangeType.MISC;
|
||||
+ } else
|
||||
+ {
|
||||
+ return TrackingRangeType.OTHER;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static enum TrackingRangeType {
|
||||
+ PLAYER,
|
||||
+ ANIMAL,
|
||||
+ MONSTER,
|
||||
+ MISC,
|
||||
+ OTHER,
|
||||
+ ENDERDRAGON;
|
||||
+ }
|
||||
+ // Paper end - optimise entity tracking
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 16 Apr 2020 16:13:59 -0700
|
||||
Subject: [PATCH] Optimize ServerLevels chunk level checking methods
|
||||
|
||||
These can be hot functions (i.e entity ticking and block ticking),
|
||||
so inline where possible, and avoid the abstraction of the
|
||||
Either class.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
index 5009ad1a758e192eaf6ca59baab26d2ba58a6c66..894186d25863a437e49ef0c10b2f2dab34af7ed0 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -2169,19 +2169,22 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
}
|
||||
|
||||
private boolean isPositionTickingWithEntitiesLoaded(long chunkPos) {
|
||||
- return this.areEntitiesLoaded(chunkPos) && this.chunkSource.isPositionTicking(chunkPos);
|
||||
+ // Paper start - optimize is ticking ready type functions
|
||||
+ ChunkHolder chunkHolder = this.chunkSource.chunkMap.getVisibleChunkIfPresent(chunkPos);
|
||||
+ return chunkHolder != null && this.chunkSource.isPositionTicking(chunkPos) && chunkHolder.isTickingReady() && this.areEntitiesLoaded(chunkPos);
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
public boolean isPositionEntityTicking(BlockPos pos) {
|
||||
- return this.entityManager.canPositionTick(pos) && this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(ChunkPos.asLong(pos));
|
||||
+ return this.entityManager.canPositionTick(ChunkPos.asLong(pos)) && this.chunkSource.chunkMap.getDistanceManager().inEntityTickingRange(ChunkPos.asLong(pos)); // Paper
|
||||
}
|
||||
|
||||
public boolean isNaturalSpawningAllowed(BlockPos pos) {
|
||||
- return this.entityManager.canPositionTick(pos);
|
||||
+ return this.entityManager.canPositionTick(ChunkPos.asLong(pos)); // Paper
|
||||
}
|
||||
|
||||
public boolean isNaturalSpawningAllowed(ChunkPos pos) {
|
||||
- return this.entityManager.canPositionTick(pos);
|
||||
+ return this.entityManager.canPositionTick(pos.toLong()); // Paper
|
||||
}
|
||||
|
||||
private final class EntityCallbacks implements LevelCallback<Entity> {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/ChunkPos.java b/src/main/java/net/minecraft/world/level/ChunkPos.java
|
||||
index 4335132883e959f0c9649d5640b703a291d8a7aa..22d95316eac3731c66dd7dde960e4d71a6b565e2 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/ChunkPos.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/ChunkPos.java
|
||||
@@ -50,7 +50,7 @@ public class ChunkPos {
|
||||
}
|
||||
|
||||
public static long asLong(BlockPos pos) {
|
||||
- return asLong(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()));
|
||||
+ return (((long)pos.getX() >> 4) & 4294967295L) | ((((long)pos.getZ() >> 4) & 4294967295L) << 32); // Paper - inline
|
||||
}
|
||||
|
||||
public static int getX(long pos) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
||||
index 1407f30d467fa78bc207a91da0e6395c0a9ba83d..2110cb437807f99994838b57653caefe2f01a9c5 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
|
||||
@@ -383,6 +383,11 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
|
||||
public LevelEntityGetter<T> getEntityGetter() {
|
||||
return this.entityGetter;
|
||||
}
|
||||
+ // Paper start
|
||||
+ public final boolean canPositionTick(long position) {
|
||||
+ return this.chunkVisibility.get(position).isTicking();
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
public boolean canPositionTick(BlockPos pos) {
|
||||
return ((Visibility) this.chunkVisibility.get(ChunkPos.asLong(pos))).isTicking();
|
|
@ -1,107 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Sat, 18 Jun 2016 23:22:12 -0400
|
||||
Subject: [PATCH] Delay Chunk Unloads based on Player Movement
|
||||
|
||||
When players are moving in the world, doing things such as building or exploring,
|
||||
they will commonly go back and forth in a small area. This causes a ton of chunk load
|
||||
and unload activity on the edge chunks of their view distance.
|
||||
|
||||
A simple back and forth movement in 6 blocks could spam a chunk to thrash a
|
||||
loading and unload cycle over and over again.
|
||||
|
||||
This is very wasteful. This system introduces a delay of inactivity on a chunk
|
||||
before it actually unloads, which will be handled by the ticket expiry process.
|
||||
|
||||
This allows servers with smaller worlds who do less long distance exploring to stop
|
||||
wasting cpu cycles on saving/unloading/reloading chunks repeatedly.
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
index ffe0ee1bd44d47abbe6b94aadab9ee7eca7c7612..7839ee18955725463fa420a19927019f9814ee24 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java
|
||||
@@ -702,4 +702,13 @@ public class PaperWorldConfig {
|
||||
expMergeMaxValue = getInt("experience-merge-max-value", -1);
|
||||
log("Experience Merge Max Value: " + expMergeMaxValue);
|
||||
}
|
||||
+
|
||||
+ public long delayChunkUnloadsBy;
|
||||
+ private void delayChunkUnloadsBy() {
|
||||
+ delayChunkUnloadsBy = PaperConfig.getSeconds(getString("delay-chunk-unloads-by", "10s"));
|
||||
+ if (delayChunkUnloadsBy > 0) {
|
||||
+ log("Delaying chunk unloads by " + delayChunkUnloadsBy + " seconds");
|
||||
+ delayChunkUnloadsBy *= 20;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
index 62118348d6fb00f063507debb488e1ff431d139c..211566dada0f820af331695b4c62035b89f25a53 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
|
||||
@@ -212,6 +212,27 @@ public abstract class DistanceManager {
|
||||
boolean removed = false; // CraftBukkit
|
||||
if (arraysetsorted.remove(ticket)) {
|
||||
removed = true; // CraftBukkit
|
||||
+ // Paper start - delay chunk unloads for player tickets
|
||||
+ long delayChunkUnloadsBy = chunkMap.level.paperConfig.delayChunkUnloadsBy;
|
||||
+ if (ticket.getType() == TicketType.PLAYER && delayChunkUnloadsBy > 0) {
|
||||
+ boolean hasPlayer = false;
|
||||
+ for (Ticket<?> ticket1 : arraysetsorted) {
|
||||
+ if (ticket1.getType() == TicketType.PLAYER) {
|
||||
+ hasPlayer = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ ChunkHolder playerChunk = chunkMap.getUpdatingChunkIfPresent(i);
|
||||
+ if (!hasPlayer && playerChunk != null && playerChunk.isFullChunkReady()) {
|
||||
+ Ticket<Long> delayUnload = new Ticket<Long>(TicketType.DELAY_UNLOAD, 33, i);
|
||||
+ delayUnload.delayUnloadBy = delayChunkUnloadsBy;
|
||||
+ delayUnload.setCreatedTick(this.ticketTickCounter);
|
||||
+ arraysetsorted.remove(delayUnload);
|
||||
+ // refresh ticket
|
||||
+ arraysetsorted.add(delayUnload);
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
if (arraysetsorted.isEmpty()) {
|
||||
diff --git a/src/main/java/net/minecraft/server/level/Ticket.java b/src/main/java/net/minecraft/server/level/Ticket.java
|
||||
index ffc43e5d3d0563c9e9c171064511b2c65ddf67e1..f1128f0d4a9a0241ac6c9bc18dd13b431c616bb1 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/Ticket.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/Ticket.java
|
||||
@@ -7,11 +7,13 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
|
||||
private final int ticketLevel;
|
||||
public final T key;
|
||||
public long createdTick;
|
||||
+ public long delayUnloadBy; // Paper
|
||||
|
||||
protected Ticket(TicketType<T> type, int level, T argument) {
|
||||
this.type = type;
|
||||
this.ticketLevel = level;
|
||||
this.key = argument;
|
||||
+ this.delayUnloadBy = type.timeout; // Paper
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,7 +62,7 @@ public final class Ticket<T> implements Comparable<Ticket<?>> {
|
||||
}
|
||||
|
||||
protected boolean timedOut(long currentTick) {
|
||||
- long l = this.type.timeout();
|
||||
+ long l = delayUnloadBy; // Paper
|
||||
return l != 0L && currentTick - this.createdTick > l;
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/level/TicketType.java b/src/main/java/net/minecraft/server/level/TicketType.java
|
||||
index 78fbb4c3e52e900956ae0811aaf934c81ee5ea48..8770fe0db46b01e8b608637df4f1a669a3f4cdde 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/TicketType.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/TicketType.java
|
||||
@@ -28,6 +28,7 @@ public class TicketType<T> {
|
||||
public static final TicketType<ChunkPos> UNKNOWN = TicketType.create("unknown", Comparator.comparingLong(ChunkPos::toLong), 1);
|
||||
public static final TicketType<Unit> PLUGIN = TicketType.create("plugin", (a, b) -> 0); // CraftBukkit
|
||||
public static final TicketType<org.bukkit.plugin.Plugin> PLUGIN_TICKET = TicketType.create("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // CraftBukkit
|
||||
+ public static final TicketType<Long> DELAY_UNLOAD = create("delay_unload", Long::compareTo, 300); // Paper
|
||||
|
||||
public static <T> TicketType<T> create(String name, Comparator<T> argumentComparator) {
|
||||
return new TicketType<>(name, argumentComparator, 0L);
|
|
@ -1,20 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: chickeneer <emcchickeneer@gmail.com>
|
||||
Date: Fri, 5 Jun 2020 20:02:04 -0500
|
||||
Subject: [PATCH] Fix villager trading demand - MC-163962
|
||||
|
||||
Prevent demand from going negative and tending to negative infinity
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java b/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java
|
||||
index 510730fecc8a88dd0588689512863bd30ac248c7..f3cf16ce1e1d6c8f76ca5823f532925253ae64aa 100644
|
||||
--- a/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java
|
||||
+++ b/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java
|
||||
@@ -113,7 +113,7 @@ public class MerchantOffer {
|
||||
}
|
||||
|
||||
public void updateDemand() {
|
||||
- this.demand = this.demand + this.uses - (this.maxUses - this.uses);
|
||||
+ this.demand = Math.max(0, this.demand + this.uses - (this.maxUses - this.uses)); // Paper
|
||||
}
|
||||
|
||||
public ItemStack assemble() {
|
|
@ -1,32 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Phoenix616 <mail@moep.tv>
|
||||
Date: Sun, 7 Jun 2020 21:43:42 +0100
|
||||
Subject: [PATCH] Maps shouldn't load chunks
|
||||
|
||||
Previously maps would load all chunks in a certain radius depending on
|
||||
their scale when trying to update their content. This would result in
|
||||
main thread chunk loads when they weren't really necessary, especially
|
||||
on low view distances or "slow" async chunk loads after teleports or
|
||||
other prioritisation.
|
||||
|
||||
This changes it to only try to render already loaded chunks based on
|
||||
the assumption that the chunks around the player will get loaded
|
||||
eventually anyways and that maps will get checked for update every
|
||||
five ticks that movement occur in anyways.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/item/MapItem.java b/src/main/java/net/minecraft/world/item/MapItem.java
|
||||
index f3c0d48f1f6bc6a1a20d4b25d943b01c8736bb36..d15d87208d21173912cf489b55b1c5d226ea998b 100644
|
||||
--- a/src/main/java/net/minecraft/world/item/MapItem.java
|
||||
+++ b/src/main/java/net/minecraft/world/item/MapItem.java
|
||||
@@ -130,9 +130,9 @@ public class MapItem extends ComplexItem {
|
||||
int k2 = (j / i + k1 - 64) * i;
|
||||
int l2 = (k / i + l1 - 64) * i;
|
||||
Multiset<MaterialColor> multiset = LinkedHashMultiset.create();
|
||||
- LevelChunk chunk = world.getChunkAt(new BlockPos(k2, 0, l2));
|
||||
+ LevelChunk chunk = world.getChunkIfLoaded(new BlockPos(k2, 0, l2)); // Paper - Maps shouldn't load chunks
|
||||
|
||||
- if (!chunk.isEmpty()) {
|
||||
+ if (chunk != null && !chunk.isEmpty()) { // Paper - Maps shouldn't load chunks
|
||||
ChunkPos chunkcoordintpair = chunk.getPos();
|
||||
int i3 = k2 & 15;
|
||||
int j3 = l2 & 15;
|
|
@ -1,27 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Sun, 7 Jun 2020 19:25:13 -0400
|
||||
Subject: [PATCH] Use seed based lookup for Treasure Maps - Fixes lag from
|
||||
carto/sunken maps
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/item/MapItem.java b/src/main/java/net/minecraft/world/item/MapItem.java
|
||||
index d15d87208d21173912cf489b55b1c5d226ea998b..6f86f00d33cd50a2b67409c2f0f5400c869b802a 100644
|
||||
--- a/src/main/java/net/minecraft/world/item/MapItem.java
|
||||
+++ b/src/main/java/net/minecraft/world/item/MapItem.java
|
||||
@@ -251,14 +251,13 @@ public class MapItem extends ComplexItem {
|
||||
boolean[] aboolean = new boolean[16384];
|
||||
int l = j / i - 64;
|
||||
int i1 = k / i - 64;
|
||||
- BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
|
||||
|
||||
int j1;
|
||||
int k1;
|
||||
|
||||
for (j1 = 0; j1 < 128; ++j1) {
|
||||
for (k1 = 0; k1 < 128; ++k1) {
|
||||
- Biome.BiomeCategory biomebase_geography = Biome.getBiomeCategory(world.getBiome(blockposition_mutableblockposition.set((l + k1) * i, 0, (i1 + j1) * i)));
|
||||
+ Biome.BiomeCategory biomebase_geography = Biome.getBiomeCategory(world.getUncachedNoiseBiome((l + k1) * i, 0, (i1 + j1) * i)); // Paper
|
||||
|
||||
aboolean[j1 * 128 + k1] = biomebase_geography == Biome.BiomeCategory.OCEAN || biomebase_geography == Biome.BiomeCategory.RIVER || biomebase_geography == Biome.BiomeCategory.SWAMP;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: David Slovikosky <davidslovikosky@gmail.com>
|
||||
Date: Tue, 9 Jun 2020 00:10:03 -0700
|
||||
Subject: [PATCH] Fix missing chunks due to integer overflow
|
||||
|
||||
This patch fixes a bug in the WorldChunkManagerTheEnd class where the distance
|
||||
from 0,0 squared overflows the maximum size of an integer. The overflow leads
|
||||
to hard chunk borders around 370,000 blocks from 0,0. After this cutoff there
|
||||
is a few hundred thousand block gap before end land resuming to generate at
|
||||
530,000 blocks from spawn. This is due to the integer flipping back and forth.
|
||||
|
||||
The fix for the issue is quite simple, casting chunk coordinates to longs
|
||||
allows the distance calculation to avoid overflow and work as intended.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/biome/TheEndBiomeSource.java b/src/main/java/net/minecraft/world/level/biome/TheEndBiomeSource.java
|
||||
index bbb43461ad899aae2212c32a9e839c766b3b8e37..3f9127f93c9720c89b578d36a1bcdb89fc01222a 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/biome/TheEndBiomeSource.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/biome/TheEndBiomeSource.java
|
||||
@@ -84,7 +84,7 @@ public class TheEndBiomeSource extends BiomeSource {
|
||||
int l = j / 2;
|
||||
int m = i % 2;
|
||||
int n = j % 2;
|
||||
- float f = 100.0F - Mth.sqrt((float)(i * i + j * j)) * 8.0F;
|
||||
+ float f = 100.0F - Mth.sqrt((long) i * (long) i + (long) j * (long) j) * 8.0F; // Paper - cast ints to long to avoid integer overflow
|
||||
f = Mth.clamp(f, -100.0F, 80.0F);
|
||||
|
||||
for(int o = -12; o <= 12; ++o) {
|
|
@ -1,21 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: ossi <oser0002@gmail.com>
|
||||
Date: Fri, 12 Jun 2020 01:38:06 +0300
|
||||
Subject: [PATCH] Fix CraftScheduler#runTaskTimerAsynchronously(Plugin,
|
||||
Consumer<BukkitTask>, long, long) scheduling a non-repeating task instead of
|
||||
a repeating one.
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
|
||||
index ea7ebbc2674df727cf44856f172731ee083b8800..a423970cf7c927ea8a1bf842aaa236d3cf2d54c2 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
|
||||
@@ -199,7 +199,7 @@ public class CraftScheduler implements BukkitScheduler {
|
||||
|
||||
@Override
|
||||
public void runTaskTimerAsynchronously(Plugin plugin, Consumer<BukkitTask> task, long delay, long period) throws IllegalArgumentException {
|
||||
- this.runTaskTimerAsynchronously(plugin, (Object) task, delay, CraftTask.NO_REPEATING);
|
||||
+ this.runTaskTimerAsynchronously(plugin, (Object) task, delay, period);
|
||||
}
|
||||
|
||||
@Override
|
|
@ -1,97 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Thu, 11 Jun 2020 17:29:42 -0700
|
||||
Subject: [PATCH] Fix piston physics inconsistency - MC-188840
|
||||
|
||||
Pistons invoke physics when they move blocks. The physics can cause
|
||||
tnt blocks to ignite. However, pistons (when storing the blocks they "moved")
|
||||
don't actually go back to the world state sometimes to check if something
|
||||
like that happened. As a result they end up moving the tnt like it was
|
||||
never ignited. This resulted in the ability to create machines
|
||||
that can duplicate tnt, called "world eaters".
|
||||
This patch makes the piston logic retrieve the block state from the world
|
||||
prevent this from occuring.
|
||||
|
||||
This patch also sets the moved pos to air immediately after creating
|
||||
the moving piston TE. This prevents the block from being updated from
|
||||
other physics calls by the piston.
|
||||
|
||||
Tested against the following tnt duper design:
|
||||
https://www.youtube.com/watch?v=mS7xxNGhjxs
|
||||
|
||||
This patch also affects every type of machine that utilises
|
||||
this mechanic. For example, dead coral is removed by a physics
|
||||
update when being moved while it is attached to slimeblocks.
|
||||
|
||||
Standard piston machines that don't destroy or modify the
|
||||
blocks they move by physics updates should be entirely
|
||||
unaffected.
|
||||
|
||||
This patch fixes https://bugs.mojang.com/browse/MC-188840
|
||||
|
||||
This patch also fixes rail duping and carpet duping.
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
index 62a0f6772bacfb8ad90c15493e0d48e29b70fac1..c639557108ad9c59ccdd0b543b5507fbab1e0fa4 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
|
||||
@@ -451,4 +451,12 @@ public class PaperConfig {
|
||||
private static void consoleHasAllPermissions() {
|
||||
consoleHasAllPermissions = getBoolean("settings.console-has-all-permissions", consoleHasAllPermissions);
|
||||
}
|
||||
+
|
||||
+ public static boolean allowPistonDuplication;
|
||||
+ private static void allowPistonDuplication() {
|
||||
+ config.set("settings.unsupported-settings.allow-piston-duplication-readme", "This setting controls if player should be able to use TNT duplication, but this also allows duplicating carpet, rails and potentially other items");
|
||||
+ allowPistonDuplication = getBoolean("settings.unsupported-settings.allow-piston-duplication", config.getBoolean("settings.unsupported-settings.allow-tnt-duplication", false));
|
||||
+ set("settings.unsupported-settings.allow-tnt-duplication", null);
|
||||
+ }
|
||||
+
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
|
||||
index edb599da2431fa32f6e656dd3de8f4a6f5e2ed4f..f4f49cc4109d4ae72c0a50f7acbd181d05bf415a 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
|
||||
@@ -409,14 +409,26 @@ public class PistonBaseBlock extends DirectionalBlock {
|
||||
}
|
||||
|
||||
for (k = list.size() - 1; k >= 0; --k) {
|
||||
- blockposition3 = (BlockPos) list.get(k);
|
||||
- iblockdata1 = world.getBlockState(blockposition3);
|
||||
+ // Paper start - fix a variety of piston desync dupes
|
||||
+ boolean allowDesync = com.destroystokyo.paper.PaperConfig.allowPistonDuplication;
|
||||
+ BlockPos oldPos = blockposition3 = (BlockPos) list.get(k);
|
||||
+ iblockdata1 = allowDesync ? world.getBlockState(oldPos) : null;
|
||||
+ // Paper end - fix a variety of piston desync dupes
|
||||
blockposition3 = blockposition3.relative(enumdirection1);
|
||||
map.remove(blockposition3);
|
||||
BlockState iblockdata2 = (BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(PistonBaseBlock.FACING, dir);
|
||||
|
||||
world.setBlock(blockposition3, iblockdata2, 68);
|
||||
- world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockposition3, iblockdata2, (BlockState) list1.get(k), dir, retract, false));
|
||||
+ // Paper start - fix a variety of piston desync dupes
|
||||
+ if (!allowDesync) {
|
||||
+ iblockdata1 = world.getBlockState(oldPos);
|
||||
+ map.replace(oldPos, iblockdata1);
|
||||
+ }
|
||||
+ world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(blockposition3, iblockdata2, allowDesync ? list1.get(k) : iblockdata1, dir, retract, false));
|
||||
+ if (!allowDesync) {
|
||||
+ world.setBlock(oldPos, Blocks.AIR.defaultBlockState(), 2 | 4 | 16 | 1024); // set air to prevent later physics updates from seeing this block
|
||||
+ }
|
||||
+ // Paper end - fix a variety of piston desync dupes
|
||||
aiblockdata[j++] = iblockdata1;
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
index dda0b32a4989bbead35a2219a969a30ba0e975b0..94bd47212b05f66ebc81966eec3bc0abda76252c 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
|
||||
@@ -285,7 +285,7 @@ public class PistonMovingBlockEntity extends BlockEntity {
|
||||
if (world.getBlockState(pos).is(Blocks.MOVING_PISTON)) {
|
||||
BlockState blockState = Block.updateFromNeighbourShapes(blockEntity.movedState, world, pos);
|
||||
if (blockState.isAir()) {
|
||||
- world.setBlock(pos, blockEntity.movedState, 84);
|
||||
+ world.setBlock(pos, blockEntity.movedState, com.destroystokyo.paper.PaperConfig.allowPistonDuplication ? 84 : (84 | 2)); // Paper - force notify (flag 2), it's possible the set type by the piston block (which doesn't notify) set this block to air
|
||||
Block.updateOrDestroy(blockEntity.movedState, blockState, world, pos, 3);
|
||||
} else {
|
||||
if (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED)) {
|
|
@ -1,37 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Fri, 12 Jun 2020 13:33:19 -0700
|
||||
Subject: [PATCH] Fix sand duping
|
||||
|
||||
If the falling block dies during teleportation (entity#move), then we need
|
||||
to detect that by placing a check after the move.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
index 08defb76e73a5f9121bf405c86a3c4c750ab1933..46102c28d10b11ecbafd3dda2de66982eaed8a00 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
|
||||
@@ -126,6 +126,11 @@ public class FallingBlockEntity extends Entity {
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
+ // Paper start - fix sand duping
|
||||
+ if (this.isRemoved()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - fix sand duping
|
||||
if (this.blockState.isAir()) {
|
||||
this.discard();
|
||||
} else {
|
||||
@@ -138,6 +143,12 @@ public class FallingBlockEntity extends Entity {
|
||||
|
||||
this.move(MoverType.SELF, this.getDeltaMovement());
|
||||
|
||||
+ // Paper start - fix sand duping
|
||||
+ if (this.isRemoved()) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Paper end - fix sand duping
|
||||
+
|
||||
// Paper start - Configurable EntityFallingBlock height nerf
|
||||
if (this.level.paperConfig.fallingBlockHeightNerf != 0 && this.getY() > this.level.paperConfig.fallingBlockHeightNerf) {
|
||||
if (this.dropItem && this.level.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
|
|
@ -1,31 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Fri, 12 Jun 2020 16:51:39 -0700
|
||||
Subject: [PATCH] Prevent position desync in playerconnection causing tp
|
||||
exploit
|
||||
|
||||
Caused the server to revert to the player's overworld coordinates
|
||||
after teleporting into the end.
|
||||
|
||||
Sidenote: The underlying issue is that the move call can teleport
|
||||
entities and do other things like kill the entity. In the future,
|
||||
to fix all exploits derieved from this usually unexpected
|
||||
behaviour, we need to move all of this dangerous logic outside
|
||||
of the move call and into an appropriate place in the tick method.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 9f80408a7cf39f1f8b58c688b91de4731208e93d..61d5b5001979751da986fd8a634a58094c172946 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -1342,6 +1342,11 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
|
||||
this.player.move(MoverType.PLAYER, new Vec3(d7, d8, d9));
|
||||
this.player.onGround = packet.isOnGround(); // CraftBukkit - SPIGOT-5810, SPIGOT-5835, SPIGOT-6828: reset by this.player.move
|
||||
+ // Paper start - prevent position desync
|
||||
+ if (this.awaitingPositionFromClient != null) {
|
||||
+ return; // ... thanks Mojang for letting move calls teleport across dimensions.
|
||||
+ }
|
||||
+ // Paper end - prevent position desync
|
||||
double d12 = d8;
|
||||
|
||||
d7 = d0 - this.player.getX();
|
|
@ -1,42 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Phoenix616 <mail@moep.tv>
|
||||
Date: Wed, 10 Jun 2020 23:55:15 +0100
|
||||
Subject: [PATCH] Inventory getHolder method without block snapshot
|
||||
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
||||
index 3796f8b122ad981b6faacd2afcaf3696314aad6b..b17dab9e5c06d8789553b104602d7da35d926dd1 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
|
||||
@@ -532,6 +532,13 @@ public class CraftInventory implements Inventory {
|
||||
return this.inventory.getOwner();
|
||||
}
|
||||
|
||||
+ // Paper start - getHolder without snapshot
|
||||
+ @Override
|
||||
+ public InventoryHolder getHolder(boolean useSnapshot) {
|
||||
+ return inventory instanceof net.minecraft.world.level.block.entity.BlockEntity ? ((net.minecraft.world.level.block.entity.BlockEntity) inventory).getOwner(useSnapshot) : getHolder();
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public int getMaxStackSize() {
|
||||
return this.inventory.getMaxStackSize();
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java
|
||||
index 01d425d359f2d6d87b6c01b435a9cfcfe11caa20..4707a651dc80086efa852bcfba38a534e7f1f3d0 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java
|
||||
@@ -64,6 +64,13 @@ public class CraftInventoryDoubleChest extends CraftInventory implements DoubleC
|
||||
return new DoubleChest(this);
|
||||
}
|
||||
|
||||
+ // Paper start - getHolder without snapshot
|
||||
+ @Override
|
||||
+ public DoubleChest getHolder(boolean useSnapshot) {
|
||||
+ return getHolder();
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public Location getLocation() {
|
||||
return this.getLeftSide().getLocation().add(this.getRightSide().getLocation()).multiply(0.5);
|
|
@ -1,38 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nesaak <52047222+Nesaak@users.noreply.github.com>
|
||||
Date: Sat, 23 May 2020 10:31:11 -0400
|
||||
Subject: [PATCH] Improve Arrow API
|
||||
|
||||
Add method to get the arrow's itemstack and a method
|
||||
to set the arrow's "noclip" status
|
||||
|
||||
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
|
||||
index 376885c8148da619a3b203145d315ebaf44994fb..15abd085eeb0a31a925c1a8d6de903c9d4625a29 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
|
||||
@@ -102,6 +102,23 @@ public class CraftArrow extends AbstractProjectile implements AbstractArrow {
|
||||
this.getHandle().pickup = net.minecraft.world.entity.projectile.AbstractArrow.Pickup.byOrdinal(status.ordinal());
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ @Override
|
||||
+ public org.bukkit.craftbukkit.inventory.CraftItemStack getItemStack() {
|
||||
+ return org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(getHandle().getPickupItem());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setNoPhysics(boolean noPhysics) {
|
||||
+ this.getHandle().setNoPhysics(noPhysics);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean hasNoPhysics() {
|
||||
+ return this.getHandle().isNoPhysics();
|
||||
+ }
|
||||
+ // Paper end
|
||||
+
|
||||
@Override
|
||||
public void setTicksLived(int value) {
|
||||
super.setTicksLived(value);
|
|
@ -1,28 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: JRoy <joshroy126@gmail.com>
|
||||
Date: Fri, 5 Jun 2020 18:24:06 -0400
|
||||
Subject: [PATCH] Add and implement PlayerRecipeBookClickEvent
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 61d5b5001979751da986fd8a634a58094c172946..5069017665f98a8b3900220b01ca4b99bc8d2f93 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -2805,9 +2805,14 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
|
||||
this.player.resetLastActionTime();
|
||||
if (!this.player.isSpectator() && this.player.containerMenu.containerId == packet.getContainerId() && this.player.containerMenu instanceof RecipeBookMenu) {
|
||||
- this.server.getRecipeManager().byKey(packet.getRecipe()).ifPresent((irecipe) -> {
|
||||
- ((RecipeBookMenu) this.player.containerMenu).handlePlacement(packet.isShiftDown(), irecipe, this.player);
|
||||
- });
|
||||
+ // Paper start - fire event for clicking recipes in the recipe book
|
||||
+ com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent event = new com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent(
|
||||
+ player.getBukkitEntity(), org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(packet.getRecipe()), packet.isShiftDown());
|
||||
+ if (event.callEvent() && this.player.containerMenu instanceof RecipeBookMenu<?> recipeBookMenu) { // check if inventory changed during event handling
|
||||
+ this.server.getRecipeManager().byKey(org.bukkit.craftbukkit.util.CraftNamespacedKey.toMinecraft(event.getRecipe())).ifPresent((irecipe) -> {
|
||||
+ recipeBookMenu.handlePlacement(event.isMakeAll(), irecipe, this.player);
|
||||
+ });
|
||||
+ } // Paper end
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
|
||||
Date: Fri, 26 Jun 2020 22:35:08 -0700
|
||||
Subject: [PATCH] Hide sync chunk writes behind flag
|
||||
|
||||
Syncing writes on each write call has terrible performance
|
||||
on harddrives.
|
||||
|
||||
-DPaper.enable-sync-chunk-writes=true to enable
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
|
||||
index a7938420f6840b9d3880fb895aaf709ebd844312..ebfaa8d5de5b905bd2dd7778728b8c9939d01252 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
|
||||
@@ -111,7 +111,7 @@ public class DedicatedServerProperties extends Settings<DedicatedServerPropertie
|
||||
this.maxWorldSize = this.get("max-world-size", (integer) -> {
|
||||
return Mth.clamp(integer, (int) 1, 29999984);
|
||||
}, 29999984);
|
||||
- this.syncChunkWrites = this.get("sync-chunk-writes", true);
|
||||
+ this.syncChunkWrites = this.get("sync-chunk-writes", true) && Boolean.getBoolean("Paper.enable-sync-chunk-writes"); // Paper - hide behind flag
|
||||
this.enableJmxMonitoring = this.get("enable-jmx-monitoring", false);
|
||||
this.enableStatus = this.get("enable-status", true);
|
||||
this.hideOnlinePlayers = this.get("hide-online-players", false);
|
|
@ -1,79 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Mariell Hoversholm <proximyst@proximyst.com>
|
||||
Date: Sat, 16 May 2020 10:05:30 +0200
|
||||
Subject: [PATCH] Add permission for command blocks
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
||||
index d4f57fde2f02071dfde08cb2a5c8359984056aef..176e5bbac94cf39805dcacfcae3a3daa98b793b7 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
|
||||
@@ -403,7 +403,7 @@ public class ServerPlayerGameMode {
|
||||
BlockEntity tileentity = this.level.getBlockEntity(pos);
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
- if (block instanceof GameMasterBlock && !this.player.canUseGameMasterBlocks()) {
|
||||
+ if (block instanceof GameMasterBlock && !this.player.canUseGameMasterBlocks() && !(block instanceof net.minecraft.world.level.block.CommandBlock && (this.player.isCreative() && this.player.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission
|
||||
this.level.sendBlockUpdated(pos, iblockdata, iblockdata, 3);
|
||||
return false;
|
||||
} else if (this.player.blockActionRestricted(this.level, pos, this.gameModeForPlayer)) {
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 5069017665f98a8b3900220b01ca4b99bc8d2f93..b03b8bb8e3f19de772d41bc5a9bda3192273df07 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -796,7 +796,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
|
||||
if (!this.server.isCommandBlockEnabled()) {
|
||||
this.player.sendMessage(new TranslatableComponent("advMode.notEnabled"), Util.NIL_UUID);
|
||||
- } else if (!this.player.canUseGameMasterBlocks()) {
|
||||
+ } else if (!this.player.canUseGameMasterBlocks() && (!this.player.isCreative() || !this.player.getBukkitEntity().hasPermission("minecraft.commandblock"))) { // Paper - command block permission
|
||||
this.player.sendMessage(new TranslatableComponent("advMode.notAllowed"), Util.NIL_UUID);
|
||||
} else {
|
||||
BaseCommandBlock commandblocklistenerabstract = null;
|
||||
@@ -863,7 +863,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
|
||||
if (!this.server.isCommandBlockEnabled()) {
|
||||
this.player.sendMessage(new TranslatableComponent("advMode.notEnabled"), Util.NIL_UUID);
|
||||
- } else if (!this.player.canUseGameMasterBlocks()) {
|
||||
+ } else if (!this.player.canUseGameMasterBlocks() && (!this.player.isCreative() || !this.player.getBukkitEntity().hasPermission("minecraft.commandblock"))) { // Paper - command block permission
|
||||
this.player.sendMessage(new TranslatableComponent("advMode.notAllowed"), Util.NIL_UUID);
|
||||
} else {
|
||||
BaseCommandBlock commandblocklistenerabstract = packet.getCommandBlock(this.player.level);
|
||||
diff --git a/src/main/java/net/minecraft/world/level/BaseCommandBlock.java b/src/main/java/net/minecraft/world/level/BaseCommandBlock.java
|
||||
index 5b125aa77e769983a0aee7c5f6eb6a8a146fead0..fca57e5d1e25db6367c53ad04e49508ebf2223e7 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/BaseCommandBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/BaseCommandBlock.java
|
||||
@@ -199,7 +199,7 @@ public abstract class BaseCommandBlock implements CommandSource {
|
||||
}
|
||||
|
||||
public InteractionResult usedBy(Player player) {
|
||||
- if (!player.canUseGameMasterBlocks()) {
|
||||
+ if (!player.canUseGameMasterBlocks() && (!player.isCreative() || !player.getBukkitEntity().hasPermission("minecraft.commandblock"))) { // Paper - command block permission
|
||||
return InteractionResult.PASS;
|
||||
} else {
|
||||
if (player.getCommandSenderWorld().isClientSide) {
|
||||
diff --git a/src/main/java/net/minecraft/world/level/block/CommandBlock.java b/src/main/java/net/minecraft/world/level/block/CommandBlock.java
|
||||
index 25a297968388f85108d7b9de96fdc7d368034e4d..1a48c5ea1f2f755abfa85469e26f37ea5f02d6da 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/block/CommandBlock.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/block/CommandBlock.java
|
||||
@@ -130,7 +130,7 @@ public class CommandBlock extends BaseEntityBlock implements GameMasterBlock {
|
||||
public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
|
||||
BlockEntity tileentity = world.getBlockEntity(pos);
|
||||
|
||||
- if (tileentity instanceof CommandBlockEntity && player.canUseGameMasterBlocks()) {
|
||||
+ if (tileentity instanceof CommandBlockEntity && (player.canUseGameMasterBlocks() || (player.isCreative() && player.getBukkitEntity().hasPermission("minecraft.commandblock")))) { // Paper - command block permission
|
||||
player.openCommandBlock((CommandBlockEntity) tileentity);
|
||||
return InteractionResult.sidedSuccess(world.isClientSide);
|
||||
} else {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java
|
||||
index 70d3949616c63038ad3e9bd1069db5ea2fb3f3b8..8e06bc11fb28baee3407bbfe9d7b3689d6f85ff2 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/util/permissions/CraftDefaultPermissions.java
|
||||
@@ -16,6 +16,7 @@ public final class CraftDefaultPermissions {
|
||||
DefaultPermissions.registerPermission(CraftDefaultPermissions.ROOT + ".nbt.copy", "Gives the user the ability to copy NBT in creative", org.bukkit.permissions.PermissionDefault.TRUE, parent);
|
||||
DefaultPermissions.registerPermission(CraftDefaultPermissions.ROOT + ".debugstick", "Gives the user the ability to use the debug stick in creative", org.bukkit.permissions.PermissionDefault.OP, parent);
|
||||
DefaultPermissions.registerPermission(CraftDefaultPermissions.ROOT + ".debugstick.always", "Gives the user the ability to use the debug stick in all game modes", org.bukkit.permissions.PermissionDefault.FALSE/* , parent */); // Paper - should not have this parent, as it's not a "vanilla" utility
|
||||
+ DefaultPermissions.registerPermission(CraftDefaultPermissions.ROOT + ".commandblock", "Gives the user the ability to use command blocks.", org.bukkit.permissions.PermissionDefault.OP, parent); // Paper
|
||||
// Spigot end
|
||||
parent.recalculatePermissibles();
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Sun, 10 May 2020 22:12:46 -0400
|
||||
Subject: [PATCH] Ensure Entity AABB's are never invalid
|
||||
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
index 14b3f14d2489bdbeb89886a115db2ba78cb97ad7..5d51296954dc9e1c3c9576d7afb5287c76ab3afe 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
|
||||
@@ -599,8 +599,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
}
|
||||
|
||||
public void setPos(double x, double y, double z) {
|
||||
- this.setPosRaw(x, y, z);
|
||||
- this.setBoundingBox(this.makeBoundingBox());
|
||||
+ this.setPosRaw(x, y, z, true); // Paper - force bounding box update
|
||||
+ // this.setBoundingBox(this.makeBoundingBox()); // Paper - move into setPositionRaw
|
||||
}
|
||||
|
||||
protected AABB makeBoundingBox() {
|
||||
@@ -3783,6 +3783,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
}
|
||||
|
||||
public final void setPosRaw(double x, double y, double z) {
|
||||
+ // Paper start
|
||||
+ this.setPosRaw(x, y, z, false);
|
||||
+ }
|
||||
+ public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) {
|
||||
+ // Paper end
|
||||
if (this.position.x != x || this.position.y != y || this.position.z != z) {
|
||||
this.position = new Vec3(x, y, z);
|
||||
int i = Mth.floor(x);
|
||||
@@ -3805,6 +3810,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Paper start - never allow AABB to become desynced from position
|
||||
+ // hanging has its own special logic
|
||||
+ if (!(this instanceof net.minecraft.world.entity.decoration.HangingEntity) && (forceBoundingBoxUpdate || this.position.x != x || this.position.y != y || this.position.z != z)) {
|
||||
+ this.setBoundingBox(this.makeBoundingBox());
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
public void checkDespawn() {}
|
|
@ -1,118 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Sun, 28 Jun 2020 03:59:10 -0400
|
||||
Subject: [PATCH] Fix Per World Difficulty / Remembering Difficulty
|
||||
|
||||
Fixes per world difficulty with /difficulty command and also
|
||||
makes it so that the server keeps the last difficulty used instead
|
||||
of restoring the server.properties every single load.
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index 12c1cee3b3055b44d0ca3196eb37e51879fce67b..f85ee370c89b59ad58f182da2ff6d0d11c6e3a8b 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -820,7 +820,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
chunkproviderserver.getLightEngine().setTaskPerBatch(worldserver.paperConfig.lightQueueSize); // Paper - increase light queue size
|
||||
// CraftBukkit start
|
||||
// this.updateMobSpawningFlags();
|
||||
- worldserver.setSpawnSettings(this.isSpawningMonsters(), this.isSpawningAnimals());
|
||||
+ worldserver.setSpawnSettings(worldserver.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && ((DedicatedServer) this).settings.getProperties().spawnMonsters, this.isSpawningAnimals()); // Paper - per level difficulty (from setDifficulty(ServerLevel, Difficulty, boolean))
|
||||
|
||||
this.forceTicks = false;
|
||||
// CraftBukkit end
|
||||
@@ -1733,11 +1733,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
}
|
||||
}
|
||||
|
||||
- public void setDifficulty(Difficulty difficulty, boolean forceUpdate) {
|
||||
- if (forceUpdate || !this.worldData.isDifficultyLocked()) {
|
||||
- this.worldData.setDifficulty(this.worldData.isHardcore() ? Difficulty.HARD : difficulty);
|
||||
- this.updateMobSpawningFlags();
|
||||
- this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate);
|
||||
+ // Paper start - remember per level difficulty
|
||||
+ public void setDifficulty(ServerLevel level, Difficulty difficulty, boolean forceUpdate) {
|
||||
+ PrimaryLevelData worldData = level.serverLevelData;
|
||||
+ if (forceUpdate || !worldData.isDifficultyLocked()) {
|
||||
+ worldData.setDifficulty(worldData.isHardcore() ? Difficulty.HARD : difficulty);
|
||||
+ level.setSpawnSettings(worldData.getDifficulty() != Difficulty.PEACEFUL && ((DedicatedServer) this).settings.getProperties().spawnMonsters, this.isSpawningAnimals());
|
||||
+ // this.getPlayerList().getPlayers().forEach(this::sendDifficultyUpdate);
|
||||
+ // Paper end
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1751,7 +1754,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
while (iterator.hasNext()) {
|
||||
ServerLevel worldserver = (ServerLevel) iterator.next();
|
||||
|
||||
- worldserver.setSpawnSettings(this.isSpawningMonsters(), this.isSpawningAnimals());
|
||||
+ worldserver.setSpawnSettings(worldserver.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && ((DedicatedServer) this).settings.getProperties().spawnMonsters, this.isSpawningAnimals()); // Paper - per level difficulty (from setDifficulty(ServerLevel, Difficulty, boolean))
|
||||
}
|
||||
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/commands/DifficultyCommand.java b/src/main/java/net/minecraft/server/commands/DifficultyCommand.java
|
||||
index e23dca1a711955417ef3f590960d4e7a388b9147..36dd28498eb75152a79dae4353ef657a0485403d 100644
|
||||
--- a/src/main/java/net/minecraft/server/commands/DifficultyCommand.java
|
||||
+++ b/src/main/java/net/minecraft/server/commands/DifficultyCommand.java
|
||||
@@ -47,7 +47,7 @@ public class DifficultyCommand {
|
||||
if (worldServer.getDifficulty() == difficulty) { // CraftBukkit
|
||||
throw DifficultyCommand.ERROR_ALREADY_DIFFICULT.create(difficulty.getKey());
|
||||
} else {
|
||||
- worldServer.serverLevelData.setDifficulty(difficulty); // CraftBukkit
|
||||
+ minecraftserver.setDifficulty(worldServer, difficulty, true); // Paper - don't skip other difficulty-changing logic (fix upstream's fix)
|
||||
source.sendSuccess(new TranslatableComponent("commands.difficulty.success", new Object[]{difficulty.getDisplayName()}), true);
|
||||
return 0;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index cec0082718c2a729044d6f19d74b8e4425816725..8eca8825008713467a20f84d71ed0f32d5a40e47 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -369,7 +369,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
|
||||
@Override
|
||||
public void forceDifficulty() {
|
||||
- this.setDifficulty(this.getProperties().difficulty, true);
|
||||
+ //this.a(this.getDedicatedServerProperties().difficulty, true); // Paper - Don't overwrite level.dat's difficulty, keep current
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
index 90a844229e4a3ef5193cffc2c742b4c5a305a91c..d60d3ea1ec9c14dafda9f0fffd16ed2ba947789b 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -1120,7 +1120,7 @@ public class ServerPlayer extends Player {
|
||||
this.isChangingDimension = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds
|
||||
|
||||
this.connection.send(new ClientboundRespawnPacket(worldserver.dimensionTypeRegistration(), worldserver.dimension(), BiomeManager.obfuscateSeed(worldserver.getSeed()), this.gameMode.getGameModeForPlayer(), this.gameMode.getPreviousGameModeForPlayer(), worldserver.isDebug(), worldserver.isFlat(), true));
|
||||
- this.connection.send(new ClientboundChangeDifficultyPacket(this.level.getDifficulty(), this.level.getLevelData().isDifficultyLocked()));
|
||||
+ this.connection.send(new ClientboundChangeDifficultyPacket(worldserver.getDifficulty(), this.level.getLevelData().isDifficultyLocked())); // Paper - fix difficulty sync issue
|
||||
PlayerList playerlist = this.server.getPlayerList();
|
||||
|
||||
playerlist.sendPlayerPermissionLevel(this);
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index b03b8bb8e3f19de772d41bc5a9bda3192273df07..6acc4ee50ba32a208930bda89d37f155e3f4835a 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -3063,7 +3063,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Ser
|
||||
public void handleChangeDifficulty(ServerboundChangeDifficultyPacket packet) {
|
||||
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.getLevel());
|
||||
if (this.player.hasPermissions(2) || this.isSingleplayerOwner()) {
|
||||
- this.server.setDifficulty(packet.getDifficulty(), false);
|
||||
+ //this.minecraftServer.a(packetplayindifficultychange.b(), false); // Paper - don't allow clients to change this
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 0e339c13f58419a6af39b99782160ac3155b0606..0c736a05aaaaaaba65730ca9c731045ac1c9338c 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -945,8 +945,8 @@ public final class CraftServer implements Server {
|
||||
org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot
|
||||
com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings")); // Paper
|
||||
for (ServerLevel world : this.console.getAllLevels()) {
|
||||
- world.serverLevelData.setDifficulty(config.difficulty);
|
||||
- world.setSpawnSettings(config.spawnMonsters, config.spawnAnimals);
|
||||
+ // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty
|
||||
+ world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean))
|
||||
|
||||
for (SpawnCategory spawnCategory : SpawnCategory.values()) {
|
||||
if (CraftSpawnCategory.isValidForLimits(spawnCategory)) {
|
|
@ -1,72 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Sun, 28 Jun 2020 19:27:20 -0400
|
||||
Subject: [PATCH] Paper dumpitem command
|
||||
|
||||
Let's you quickly view the item in your hands NBT data
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
index 64c8ba212855dbdd7d2025126474644bd68c3ec0..fa0b824747c5b15cfb29e22b2d30b9c6932c1320 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -20,6 +20,7 @@ import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
+import net.minecraft.network.protocol.game.ClientboundLightUpdatePacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MCUtil;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
@@ -32,7 +33,9 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.entity.Player;
|
||||
+import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -63,7 +66,7 @@ import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||
|
||||
public class PaperCommand extends Command {
|
||||
private static final String BASE_PERM = "bukkit.command.paper.";
|
||||
- private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight", "syncloadinfo").build();
|
||||
+ private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight", "syncloadinfo", "dumpitem").build();
|
||||
|
||||
public PaperCommand(String name) {
|
||||
super(name);
|
||||
@@ -177,6 +180,9 @@ public class PaperCommand extends Command {
|
||||
case "reload":
|
||||
doReload(sender);
|
||||
break;
|
||||
+ case "dumpitem":
|
||||
+ doDumpItem(sender);
|
||||
+ break;
|
||||
case "debug":
|
||||
doDebug(sender, args);
|
||||
break;
|
||||
@@ -493,6 +499,23 @@ public class PaperCommand extends Command {
|
||||
|
||||
Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN));
|
||||
}
|
||||
+ private void doDumpItem(CommandSender sender) {
|
||||
+ if (!(sender instanceof Player)) {
|
||||
+ sender.sendMessage("Only players can use this command");
|
||||
+ return;
|
||||
+ }
|
||||
+ ItemStack itemInHand = ((CraftPlayer) sender).getItemInHand();
|
||||
+ net.minecraft.world.item.ItemStack itemStack = CraftItemStack.asNMSCopy(itemInHand);
|
||||
+ net.minecraft.nbt.CompoundTag tag = itemStack.getTag();
|
||||
+ if (tag != null) {
|
||||
+ net.kyori.adventure.text.Component nbtComponent = io.papermc.paper.adventure.PaperAdventure.asAdventure(net.minecraft.nbt.NbtUtils.toPrettyComponent(tag));
|
||||
+ Bukkit.getConsoleSender().sendMessage(nbtComponent);
|
||||
+ sender.sendMessage(nbtComponent);
|
||||
+ } else {
|
||||
+ sender.sendMessage("Item does not have NBT");
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
private void doFixLight(CommandSender sender, String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage("Only players can use this command");
|
Loading…
Add table
Add a link
Reference in a new issue