some more patch

This commit is contained in:
Jason Penilla 2023-12-05 15:55:31 -07:00
parent 7a59fd38d4
commit 6e9a238a8c
No known key found for this signature in database
GPG key ID: 0E75A301420E48F8
83 changed files with 300 additions and 300 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,34 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
Date: Sun, 26 Jan 2020 16:30:19 -0600
Subject: [PATCH] Bees get gravity in void. Fixes MC-167279
diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java
index 98b0b91a8033ae297fee3fe655ee641e9ab08154..27e3bed06cfb5c36125b55c4862fca94ade26a09 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Bee.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java
@@ -147,7 +147,22 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
public Bee(EntityType<? extends Bee> type, Level world) {
super(type, world);
this.remainingCooldownBeforeLocatingNewFlower = Mth.nextInt(this.random, 20, 60);
- this.moveControl = new FlyingMoveControl(this, 20, true);
+ // Paper start - apply gravity to bees when they get stuck in the void, fixes MC-167279
+ class BeeFlyingMoveControl extends FlyingMoveControl {
+ public BeeFlyingMoveControl(final Mob entity, final int maxPitchChange, final boolean noGravity) {
+ super(entity, maxPitchChange, noGravity);
+ }
+
+ @Override
+ public void tick() {
+ if (this.mob.getY() <= Bee.this.level().getMinBuildHeight()) {
+ this.mob.setNoGravity(false);
+ }
+ super.tick();
+ }
+ }
+ this.moveControl = new BeeFlyingMoveControl(this, 20, true);
+ // Paper end
this.lookControl = new Bee.BeeLookControl(this);
this.setPathfindingMalus(BlockPathTypes.DANGER_FIRE, -1.0F);
this.setPathfindingMalus(BlockPathTypes.WATER, -1.0F);

View file

@ -1,95 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 2 Jan 2020 12:25:07 -0600
Subject: [PATCH] Improve Block#breakNaturally API
Adds bool parameter to play world effect on block break
Adds bool parameter to drop xp from blocks
Fixes fluid-logged blocks not leaving fluid behind if
broken
Handles special cases for ice and turtle eggs
== AT ==
public net.minecraft.world.level.block.TurtleEggBlock decreaseEggs(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)V
Co-authored-by: William Blake Galbreath <Blake.Galbreath@GMail.com>
diff --git a/src/main/java/net/minecraft/world/level/block/IceBlock.java b/src/main/java/net/minecraft/world/level/block/IceBlock.java
index 6f9cb55491da718cd6564425748ab3852fda9b68..5fbdc96f29e29dfc092b9e84a988032db0fa36ab 100644
--- a/src/main/java/net/minecraft/world/level/block/IceBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/IceBlock.java
@@ -27,6 +27,11 @@ public class IceBlock extends HalfTransparentBlock {
@Override
public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, ItemStack tool) {
super.playerDestroy(world, player, pos, state, blockEntity, tool);
+ // Paper start
+ this.afterDestroy(world, pos, tool);
+ }
+ public void afterDestroy(Level world, BlockPos pos, ItemStack tool) {
+ // Paper end
if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) == 0) {
if (world.dimensionType().ultraWarm()) {
world.removeBlock(pos, false);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
index b25ebc5f1c54ea3ecc38e96b79e5cca88aafb816..35e264172688be6cf6e82d948f591893d97a43aa 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
@@ -451,6 +451,18 @@ public class CraftBlock implements Block {
@Override
public boolean breakNaturally(ItemStack item) {
+ // Paper start
+ return this.breakNaturally(item, false);
+ }
+
+ @Override
+ public boolean breakNaturally(boolean triggerEffect, boolean dropExperience) {
+ return this.breakNaturally(null, triggerEffect, dropExperience);
+ }
+
+ @Override
+ public boolean breakNaturally(ItemStack item, boolean triggerEffect, boolean dropExperience) {
+ // Paper end
// Order matters here, need to drop before setting to air so skulls can get their data
net.minecraft.world.level.block.state.BlockState iblockdata = this.getNMS();
net.minecraft.world.level.block.Block block = iblockdata.getBlock();
@@ -460,11 +472,35 @@ public class CraftBlock implements Block {
// Modelled off EntityHuman#hasBlock
if (block != Blocks.AIR && (item == null || !iblockdata.requiresCorrectToolForDrops() || nmsItem.isCorrectToolForDrops(iblockdata))) {
net.minecraft.world.level.block.Block.dropResources(iblockdata, this.world.getMinecraftWorld(), this.position, this.world.getBlockEntity(this.position), null, nmsItem);
+ // Paper start - improve Block#breanNaturally
+ if (triggerEffect) {
+ if (iblockdata.getBlock() instanceof net.minecraft.world.level.block.BaseFireBlock) {
+ this.world.levelEvent(net.minecraft.world.level.block.LevelEvent.SOUND_EXTINGUISH_FIRE, this.position, 0);
+ } else {
+ this.world.levelEvent(net.minecraft.world.level.block.LevelEvent.PARTICLES_DESTROY_BLOCK, this.position, net.minecraft.world.level.block.Block.getId(iblockdata));
+ }
+ }
+ if (dropExperience) block.popExperience(this.world.getMinecraftWorld(), this.position, block.getExpDrop(iblockdata, this.world.getMinecraftWorld(), this.position, nmsItem, true));
+ // Paper end
result = true;
}
// SPIGOT-6778: Directly call setBlock instead of setTypeAndData, so that the tile entiy is not removed and custom remove logic is run.
- return this.world.setBlock(this.position, Blocks.AIR.defaultBlockState(), 3) && result;
+ // Paper start - improve breakNaturally
+ boolean destroyed = this.world.removeBlock(this.position, false);
+ if (destroyed) {
+ block.destroy(this.world, this.position, iblockdata);
+ }
+ if (result) {
+ // special cases
+ if (block instanceof net.minecraft.world.level.block.IceBlock iceBlock) {
+ iceBlock.afterDestroy(this.world.getMinecraftWorld(), this.position, nmsItem);
+ } else if (block instanceof net.minecraft.world.level.block.TurtleEggBlock turtleEggBlock) {
+ turtleEggBlock.decreaseEggs(this.world.getMinecraftWorld(), this.position, iblockdata);
+ }
+ }
+ return destroyed && result;
+ // Paper end
}
@Override

View file

@ -1,66 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 25 Jan 2020 17:04:35 -0800
Subject: [PATCH] Optimise getChunkAt calls for loaded chunks
bypass the need to get a player chunk, then get the either,
then unwrap it...
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index ce8afee7d1db99de43c9ec47edcbc929e025b159..8f5d30ad2cf6274bc6a1721be9ccde8dac0be333 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -270,6 +270,12 @@ public class ServerChunkCache extends ChunkSource {
return this.getChunk(x, z, leastStatus, create);
}, this.mainThreadProcessor).join();
} else {
+ // Paper start - optimise for loaded chunks
+ LevelChunk ifLoaded = this.getChunkAtIfLoadedMainThread(x, z);
+ if (ifLoaded != null) {
+ return ifLoaded;
+ }
+ // Paper end
ProfilerFiller gameprofilerfiller = this.level.getProfiler();
gameprofilerfiller.incrementCounter("getChunk");
@@ -313,39 +319,7 @@ public class ServerChunkCache extends ChunkSource {
if (!io.papermc.paper.util.TickThread.isTickThread()) { // Paper - rewrite chunk system
return null;
} else {
- this.level.getProfiler().incrementCounter("getChunkNow");
- long k = ChunkPos.asLong(chunkX, chunkZ);
-
- for (int l = 0; l < 4; ++l) {
- if (k == this.lastChunkPos[l] && this.lastChunkStatus[l] == ChunkStatus.FULL) {
- ChunkAccess ichunkaccess = this.lastChunk[l];
-
- return ichunkaccess instanceof LevelChunk ? (LevelChunk) ichunkaccess : null;
- }
- }
-
- ChunkHolder playerchunk = this.getVisibleChunkIfPresent(k);
-
- if (playerchunk == null) {
- return null;
- } else {
- Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = (Either) playerchunk.getFutureIfPresent(ChunkStatus.FULL).getNow(null); // CraftBukkit - decompile error
-
- if (either == null) {
- return null;
- } else {
- ChunkAccess ichunkaccess1 = (ChunkAccess) either.left().orElse(null); // CraftBukkit - decompile error
-
- if (ichunkaccess1 != null) {
- this.storeInCache(k, ichunkaccess1, ChunkStatus.FULL);
- if (ichunkaccess1 instanceof LevelChunk) {
- return (LevelChunk) ichunkaccess1;
- }
- }
-
- return null;
- }
- }
+ return this.getChunkAtIfLoadedMainThread(chunkX, chunkZ); // Paper - optimise for loaded chunks
}
}

View file

@ -1,331 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Fri, 19 Jul 2019 03:29:14 -0700
Subject: [PATCH] Add debug for sync chunk loads
This patch adds a tool to find calls to getChunkAt which would load
chunks, however it must be enabled by setting the startup flag
-Dpaper.debug-sync-loads=true
- To get a debug log for sync loads, the command is
/paper syncloadinfo
- To clear clear the currently stored sync load info, use
/paper syncloadinfo clear
diff --git a/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
new file mode 100644
index 0000000000000000000000000000000000000000..0bb4aaa546939b67a5d22865190f30478a9337c1
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
@@ -0,0 +1,175 @@
+package com.destroystokyo.paper.io;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.mojang.datafixers.util.Pair;
+import it.unimi.dsi.fastutil.longs.Long2IntMap;
+import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import net.minecraft.world.level.Level;
+
+public class SyncLoadFinder {
+
+ public static final boolean ENABLED = Boolean.getBoolean("paper.debug-sync-loads");
+
+ private static final WeakHashMap<Level, Object2ObjectOpenHashMap<ThrowableWithEquals, SyncLoadInformation>> SYNC_LOADS = new WeakHashMap<>();
+
+ private static final class SyncLoadInformation {
+
+ public int times;
+
+ public final Long2IntOpenHashMap coordinateTimes = new Long2IntOpenHashMap();
+ }
+
+ public static void clear() {
+ SYNC_LOADS.clear();
+ }
+
+ public static void logSyncLoad(final Level world, final int chunkX, final int chunkZ) {
+ if (!ENABLED) {
+ return;
+ }
+
+ final ThrowableWithEquals stacktrace = new ThrowableWithEquals(Thread.currentThread().getStackTrace());
+
+ SYNC_LOADS.compute(world, (final Level keyInMap, Object2ObjectOpenHashMap<ThrowableWithEquals, SyncLoadInformation> map) -> {
+ if (map == null) {
+ map = new Object2ObjectOpenHashMap<>();
+ }
+
+ map.compute(stacktrace, (ThrowableWithEquals keyInMap0, SyncLoadInformation valueInMap) -> {
+ if (valueInMap == null) {
+ valueInMap = new SyncLoadInformation();
+ }
+
+ ++valueInMap.times;
+
+ valueInMap.coordinateTimes.compute(IOUtil.getCoordinateKey(chunkX, chunkZ), (Long keyInMap1, Integer valueInMap1) -> {
+ return valueInMap1 == null ? Integer.valueOf(1) : Integer.valueOf(valueInMap1.intValue() + 1);
+ });
+
+ return valueInMap;
+ });
+
+ return map;
+ });
+ }
+
+ public static JsonObject serialize() {
+ final JsonObject ret = new JsonObject();
+
+ final JsonArray worldsData = new JsonArray();
+
+ for (final Map.Entry<Level, Object2ObjectOpenHashMap<ThrowableWithEquals, SyncLoadInformation>> entry : SYNC_LOADS.entrySet()) {
+ final Level world = entry.getKey();
+
+ final JsonObject worldData = new JsonObject();
+
+ worldData.addProperty("name", world.getWorld().getName());
+
+ final List<Pair<ThrowableWithEquals, SyncLoadInformation>> data = new ArrayList<>();
+
+ entry.getValue().forEach((ThrowableWithEquals stacktrace, SyncLoadInformation times) -> {
+ data.add(new Pair<>(stacktrace, times));
+ });
+
+ data.sort((Pair<ThrowableWithEquals, SyncLoadInformation> pair1, Pair<ThrowableWithEquals, SyncLoadInformation> pair2) -> {
+ return Integer.compare(pair2.getSecond().times, pair1.getSecond().times); // reverse order
+ });
+
+ final JsonArray stacktraces = new JsonArray();
+
+ for (Pair<ThrowableWithEquals, SyncLoadInformation> pair : data) {
+ final JsonObject stacktrace = new JsonObject();
+
+ stacktrace.addProperty("times", pair.getSecond().times);
+
+ final JsonArray traces = new JsonArray();
+
+ for (StackTraceElement element : pair.getFirst().stacktrace) {
+ traces.add(String.valueOf(element));
+ }
+
+ stacktrace.add("stacktrace", traces);
+
+ final JsonArray coordinates = new JsonArray();
+
+ for (Long2IntMap.Entry coordinate : pair.getSecond().coordinateTimes.long2IntEntrySet()) {
+ final long key = coordinate.getLongKey();
+ final int times = coordinate.getIntValue();
+ coordinates.add("(" + IOUtil.getCoordinateX(key) + "," + IOUtil.getCoordinateZ(key) + "): " + times);
+ }
+
+ stacktrace.add("coordinates", coordinates);
+
+ stacktraces.add(stacktrace);
+ }
+
+
+ worldData.add("stacktraces", stacktraces);
+ worldsData.add(worldData);
+ }
+
+ ret.add("worlds", worldsData);
+
+ return ret;
+ }
+
+ static final class ThrowableWithEquals {
+
+ private final StackTraceElement[] stacktrace;
+ private final int hash;
+
+ public ThrowableWithEquals(final StackTraceElement[] stacktrace) {
+ this.stacktrace = stacktrace;
+ this.hash = ThrowableWithEquals.hash(stacktrace);
+ }
+
+ public static int hash(final StackTraceElement[] stacktrace) {
+ int hash = 0;
+
+ for (int i = 0; i < stacktrace.length; ++i) {
+ hash *= 31;
+ hash += stacktrace[i].hashCode();
+ }
+
+ return hash;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.hash;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null || obj.getClass() != this.getClass()) {
+ return false;
+ }
+
+ final ThrowableWithEquals other = (ThrowableWithEquals)obj;
+ final StackTraceElement[] otherStackTrace = other.stacktrace;
+
+ if (this.stacktrace.length != otherStackTrace.length || this.hash != other.hash) {
+ return false;
+ }
+
+ if (this == obj) {
+ return true;
+ }
+
+ for (int i = 0; i < this.stacktrace.length; ++i) {
+ if (!this.stacktrace[i].equals(otherStackTrace[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
index ae51993e0de706cb62c96795ca9de7663893a5bf..5bfa245a621a0bf7ef60cd01f4c04576b770384e 100644
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
@@ -40,6 +40,7 @@ public final class PaperCommand extends Command {
commands.put(Set.of("dumpplugins"), new DumpPluginsCommand());
commands.put(Set.of("fixlight"), new FixLightCommand());
commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand());
+ commands.put(Set.of("syncloadinfo"), new SyncLoadInfoCommand());
return commands.entrySet().stream()
.flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue())))
diff --git a/src/main/java/io/papermc/paper/command/subcommands/SyncLoadInfoCommand.java b/src/main/java/io/papermc/paper/command/subcommands/SyncLoadInfoCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..95d6022c9cfb2e36ec5a71be6e34354027c2ec08
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/subcommands/SyncLoadInfoCommand.java
@@ -0,0 +1,88 @@
+package io.papermc.paper.command.subcommands;
+
+import com.destroystokyo.paper.io.SyncLoadFinder;
+import com.google.gson.JsonObject;
+import com.google.gson.internal.Streams;
+import com.google.gson.stream.JsonWriter;
+import io.papermc.paper.command.CommandUtil;
+import io.papermc.paper.command.PaperSubcommand;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+import net.kyori.adventure.text.event.ClickEvent;
+import net.kyori.adventure.text.event.HoverEvent;
+import net.minecraft.server.MinecraftServer;
+import org.bukkit.command.CommandSender;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
+import static net.kyori.adventure.text.format.NamedTextColor.WHITE;
+
+@DefaultQualifier(NonNull.class)
+public final class SyncLoadInfoCommand implements PaperSubcommand {
+ @Override
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
+ this.doSyncLoadInfo(sender, args);
+ return true;
+ }
+
+ @Override
+ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
+ return CommandUtil.getListMatchingLast(sender, args, "clear");
+ }
+
+ private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss");
+
+ private void doSyncLoadInfo(final CommandSender sender, final String[] args) {
+ if (!SyncLoadFinder.ENABLED) {
+ String systemFlag = "-Dpaper.debug-sync-loads=true";
+ sender.sendMessage(text().color(RED).append(text("This command requires the server startup flag '")).append(
+ text(systemFlag, WHITE).clickEvent(ClickEvent.copyToClipboard(systemFlag))
+ .hoverEvent(HoverEvent.showText(text("Click to copy the system flag")))).append(
+ text("' to be set.")));
+ return;
+ }
+
+ if (args.length > 0 && args[0].equals("clear")) {
+ SyncLoadFinder.clear();
+ sender.sendMessage(text("Sync load data cleared.", GRAY));
+ return;
+ }
+
+ File file = new File(new File(new File("."), "debug"),
+ "sync-load-info-" + FORMATTER.format(LocalDateTime.now()) + ".txt");
+ file.getParentFile().mkdirs();
+ sender.sendMessage(text("Writing sync load info to " + file, GREEN));
+
+
+ try {
+ final JsonObject data = SyncLoadFinder.serialize();
+
+ StringWriter stringWriter = new StringWriter();
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
+ jsonWriter.setIndent(" ");
+ jsonWriter.setLenient(false);
+ Streams.write(data, jsonWriter);
+
+ try (
+ PrintStream out = new PrintStream(new FileOutputStream(file), false, StandardCharsets.UTF_8)
+ ) {
+ out.print(stringWriter);
+ }
+ sender.sendMessage(text("Successfully written sync load information!", GREEN));
+ } catch (Throwable thr) {
+ sender.sendMessage(text("Failed to write sync load information! See the console for more info.", RED));
+ MinecraftServer.LOGGER.warn("Error occurred while dumping sync chunk load info", thr);
+ }
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
index 8f5d30ad2cf6274bc6a1721be9ccde8dac0be333..e27067e72a49e127aa4abb17f5ed1df09f943f3c 100644
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
+++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
@@ -294,6 +294,7 @@ public class ServerChunkCache extends ChunkSource {
// Paper start - async chunk io/loading
io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.pushChunkWait(this.level, x1, z1); // Paper - rewrite chunk system
// Paper end
+ com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.level, x1, z1); // Paper - sync load info
this.level.timings.syncChunkLoad.startTiming(); // Paper
chunkproviderserver_b.managedBlock(completablefuture::isDone);
io.papermc.paper.chunk.system.scheduling.ChunkTaskScheduler.popChunkWait(); // Paper - async chunk debug // Paper - rewrite chunk system
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 90c80bc7fc7d6f9b91d9f9953d19eef021435780..6c8fa0ed1e445e77593b6d26c09eeaac1437787d 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -645,6 +645,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
this.entityLookup = new io.papermc.paper.chunk.system.entity.EntityLookup(this, new EntityCallbacks()); // Paper - rewrite chunk system
}
+ // Paper start
+ @Override
+ public boolean hasChunk(int chunkX, int chunkZ) {
+ return this.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ) != null;
+ }
+ // Paper end
+
/** @deprecated */
@Deprecated
@VisibleForTesting

View file

@ -1,47 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Wed, 16 Mar 2022 13:58:16 +0100
Subject: [PATCH] Improve java version check
Co-Authored-By: MiniDigger <admin@benndorf.dev>
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
index ec442f1a6437f449d55739b990a4b69fcd48e8e0..a288b405052716ba13adefe43ef2006c8bf6242d 100644
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -201,23 +201,27 @@ public class Main {
return;
}
+ // Paper start - better java version checks
+ boolean skip = Boolean.getBoolean("Paper.IgnoreJavaVersion");
float javaVersion = Float.parseFloat(System.getProperty("java.class.version"));
- if (javaVersion < 61.0) {
- System.err.println("Unsupported Java detected (" + javaVersion + "). This version of Minecraft requires at least Java 17. Check your Java version with the command 'java -version'.");
- return;
- }
- if (javaVersion > 65.0) {
- System.err.println("Unsupported Java detected (" + javaVersion + "). Only up to Java 21 is supported.");
+ boolean isOldVersion = javaVersion < 61.0;
+ if (!skip && isOldVersion) {
+ System.err.println("Unsupported Java detected (" + javaVersion + "). This version of Minecraft requires at least Java 17. Check your Java version with the command 'java -version'. For more info see https://docs.papermc.io/misc/java-install");
return;
}
String javaVersionName = System.getProperty("java.version");
// J2SE SDK/JRE Version String Naming Convention
boolean isPreRelease = javaVersionName.contains("-");
- if (isPreRelease && javaVersion == 61.0) {
- System.err.println("Unsupported Java detected (" + javaVersionName + "). You are running an outdated, pre-release version. Only general availability versions of Java are supported. Please update your Java version.");
+ if (!skip && isPreRelease) {
+ System.err.println("Unsupported Java detected (" + javaVersionName + "). You are running an unsupported, non official, version. Only general availability versions of Java are supported. Please update your Java version. See https://docs.papermc.io/paper/faq#unsupported-java-detected-what-do-i-do for more information.");
return;
}
+ if (skip && (isOldVersion || isPreRelease)) {
+ System.err.println("Unsupported Java detected ("+ javaVersionName + "), but the check was skipped. Proceed with caution! ");
+ }
+ // Paper end - better java version checks
+
try {
// Paper start - Handled by TerminalConsoleAppender
/*

View file

@ -1,26 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Sun, 9 Feb 2020 00:19:05 -0600
Subject: [PATCH] Add ThrownEggHatchEvent
Adds a new event similar to PlayerEggThrowEvent, but without the Player requirement
(dispensers can throw eggs to hatch them, too).
diff --git a/src/main/java/net/minecraft/world/entity/projectile/ThrownEgg.java b/src/main/java/net/minecraft/world/entity/projectile/ThrownEgg.java
index 88e8ce770ddc61702fc87cd8dcee498183dc1032..588e5ac6fc9b2d12be3bb80bc3fe50d81470c441 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/ThrownEgg.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/ThrownEgg.java
@@ -82,6 +82,13 @@ public class ThrownEgg extends ThrowableItemProjectile {
}
}
// CraftBukkit end
+ // Paper start
+ com.destroystokyo.paper.event.entity.ThrownEggHatchEvent event = new com.destroystokyo.paper.event.entity.ThrownEggHatchEvent((org.bukkit.entity.Egg) getBukkitEntity(), hatching, b0, hatchingType);
+ event.callEvent();
+ hatching = event.isHatching();
+ b0 = hatching ? event.getNumHatches() : 0; // If hatching is set to false, ensure child count is 0
+ hatchingType = event.getHatchingType();
+ // Paper end
for (int i = 0; i < b0; ++i) {
Entity entitychicken = this.level().getWorld().createEntity(new org.bukkit.Location(this.level().getWorld(), this.getX(), this.getY(), this.getZ(), this.getYRot(), 0.0F), hatchingType.getEntityClass()); // CraftBukkit

View file

@ -1,73 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <Blake.Galbreath@GMail.com>
Date: Sat, 8 Feb 2020 23:26:11 -0600
Subject: [PATCH] Entity Jump API
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index c95c7619820d1dbabc52ae8ccef88e03a2f1d9a3..63e00b3aa1b31c14fc4630fb4b2d3355349bdf24 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -3281,8 +3281,10 @@ public abstract class LivingEntity extends Entity implements Attackable {
} else if (this.isInLava() && (!this.onGround() || d3 > d4)) {
this.jumpInLiquid(FluidTags.LAVA);
} else if ((this.onGround() || flag && d3 <= d4) && this.noJumpDelay == 0) {
+ if (new com.destroystokyo.paper.event.entity.EntityJumpEvent(getBukkitLivingEntity()).callEvent()) { // Paper
this.jumpFromGround();
this.noJumpDelay = 10;
+ } else { this.setJumping(false); } // Paper - setJumping(false) stops a potential loop
}
} else {
this.noJumpDelay = 0;
diff --git a/src/main/java/net/minecraft/world/entity/animal/Panda.java b/src/main/java/net/minecraft/world/entity/animal/Panda.java
index 3d947293d33407e5d24f8fba738ab872e1d890b6..27e1ed14f2ae4f7922c7c3bc1ac21156bafcb0ba 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Panda.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Panda.java
@@ -527,7 +527,9 @@ public class Panda extends Animal {
Panda entitypanda = (Panda) iterator.next();
if (!entitypanda.isBaby() && entitypanda.onGround() && !entitypanda.isInWater() && entitypanda.canPerformAction()) {
+ if (new com.destroystokyo.paper.event.entity.EntityJumpEvent(getBukkitLivingEntity()).callEvent()) { // Paper
entitypanda.jumpFromGround();
+ } else { this.setJumping(false); } // Paper - setJumping(false) stops a potential loop
}
}
diff --git a/src/main/java/net/minecraft/world/entity/monster/Ravager.java b/src/main/java/net/minecraft/world/entity/monster/Ravager.java
index 344e933311f5cdccb66069d486b111a003639dfe..add3cd866452df727107e94fb2039bddebe909be 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Ravager.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Ravager.java
@@ -167,7 +167,9 @@ public class Ravager extends Raider {
}
if (!flag && this.onGround()) {
+ if (new com.destroystokyo.paper.event.entity.EntityJumpEvent(getBukkitLivingEntity()).callEvent()) { // Paper
this.jumpFromGround();
+ } else { this.setJumping(false); } // Paper - setJumping(false) stops a potential loop
}
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
index 9e15c5e2adfb46ffa3548b3a2eda225de4c44fb5..904f34f52659e864d148204cb0eebb9b381cc24a 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@@ -906,5 +906,19 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
public org.bukkit.inventory.EquipmentSlot getHandRaised() {
return getHandle().getUsedItemHand() == net.minecraft.world.InteractionHand.MAIN_HAND ? org.bukkit.inventory.EquipmentSlot.HAND : org.bukkit.inventory.EquipmentSlot.OFF_HAND;
}
+
+ @Override
+ public boolean isJumping() {
+ return getHandle().jumping;
+ }
+
+ @Override
+ public void setJumping(boolean jumping) {
+ getHandle().setJumping(jumping);
+ if (jumping && getHandle() instanceof Mob) {
+ // this is needed to actually make a mob jump
+ ((Mob) getHandle()).getJumpControl().jump();
+ }
+ }
// Paper end
}

View file

@ -1,49 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
Date: Fri, 7 Feb 2020 14:36:56 -0600
Subject: [PATCH] Add option to nerf pigmen from nether portals
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 7ba9b07c4f3335fabc3aeb28cdcd331b0c5ada86..15b807c0a49461ae5daecd6aef43f5c2d464b224 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -392,6 +392,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
// Paper start
public long activatedImmunityTick = Integer.MIN_VALUE; // Paper
public boolean isTemporarilyActive = false; // Paper
+ public boolean fromNetherPortal; // Paper
protected int numCollisions = 0; // Paper
public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one
@javax.annotation.Nullable
@@ -2208,6 +2209,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
if (spawnedViaMobSpawner) {
nbt.putBoolean("Paper.FromMobSpawner", true);
}
+ if (fromNetherPortal) {
+ nbt.putBoolean("Paper.FromNetherPortal", true);
+ }
// Paper end
return nbt;
} catch (Throwable throwable) {
@@ -2350,6 +2354,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
spawnedViaMobSpawner = nbt.getBoolean("Paper.FromMobSpawner"); // Restore entity's from mob spawner status
+ fromNetherPortal = nbt.getBoolean("Paper.FromNetherPortal");
if (nbt.contains("Paper.SpawnReason")) {
String spawnReasonName = nbt.getString("Paper.SpawnReason");
try {
diff --git a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java
index 47c99d86be833b7c6f9f76c76897fb89d6fca712..69f34c566bf825259253abbefd7d7ba2e847231b 100644
--- a/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/NetherPortalBlock.java
@@ -63,6 +63,8 @@ public class NetherPortalBlock extends Block {
if (entity != null) {
entity.setPortalCooldown();
+ entity.fromNetherPortal = true; // Paper
+ if (world.paperConfig().entities.behavior.nerfPigmenFromNetherPortals) ((net.minecraft.world.entity.Mob) entity).aware = false; // Paper
}
}
}

View file

@ -1,398 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
Date: Sun, 2 Feb 2020 04:00:40 -0600
Subject: [PATCH] Make the GUI graph fancier
diff --git a/src/main/java/com/destroystokyo/paper/gui/GraphColor.java b/src/main/java/com/destroystokyo/paper/gui/GraphColor.java
new file mode 100644
index 0000000000000000000000000000000000000000..a4e641fdcccd3efcd1a2865dc6dc28d50671b995
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/gui/GraphColor.java
@@ -0,0 +1,44 @@
+package com.destroystokyo.paper.gui;
+
+import java.awt.Color;
+
+public class GraphColor {
+ private static final Color[] colorLine = new Color[101];
+ private static final Color[] colorFill = new Color[101];
+
+ static {
+ for (int i = 0; i < 101; i++) {
+ Color color = createColor(i);
+ colorLine[i] = new Color(color.getRed() / 2, color.getGreen() / 2, color.getBlue() / 2, 255);
+ colorFill[i] = new Color(colorLine[i].getRed(), colorLine[i].getGreen(), colorLine[i].getBlue(), 125);
+ }
+ }
+
+ public static Color getLineColor(int percent) {
+ return colorLine[percent];
+ }
+
+ public static Color getFillColor(int percent) {
+ return colorFill[percent];
+ }
+
+ private static Color createColor(int percent) {
+ if (percent <= 50) {
+ return new Color(0X00FF00);
+ }
+
+ int value = 510 - (int) (Math.min(Math.max(0, ((percent - 50) / 50F)), 1) * 510);
+
+ int red, green;
+ if (value < 255) {
+ red = 255;
+ green = (int) (Math.sqrt(value) * 16);
+ } else {
+ green = 255;
+ value = value - 255;
+ red = 255 - (value * value / 255);
+ }
+
+ return new Color(red, green, 0);
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/gui/GraphData.java b/src/main/java/com/destroystokyo/paper/gui/GraphData.java
new file mode 100644
index 0000000000000000000000000000000000000000..186fc722965e403f76b1480e1c2381fc34e29049
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/gui/GraphData.java
@@ -0,0 +1,47 @@
+package com.destroystokyo.paper.gui;
+
+import java.awt.Color;
+
+public class GraphData {
+ private long total;
+ private long free;
+ private long max;
+ private long usedMem;
+ private int usedPercent;
+
+ public GraphData(long total, long free, long max) {
+ this.total = total;
+ this.free = free;
+ this.max = max;
+ this.usedMem = total - free;
+ this.usedPercent = usedMem == 0 ? 0 : (int) (usedMem * 100L / max);
+ }
+
+ public long getTotal() {
+ return total;
+ }
+
+ public long getFree() {
+ return free;
+ }
+
+ public long getMax() {
+ return max;
+ }
+
+ public long getUsedMem() {
+ return usedMem;
+ }
+
+ public int getUsedPercent() {
+ return usedPercent;
+ }
+
+ public Color getFillColor() {
+ return GraphColor.getFillColor(usedPercent);
+ }
+
+ public Color getLineColor() {
+ return GraphColor.getLineColor(usedPercent);
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/gui/GuiStatsComponent.java b/src/main/java/com/destroystokyo/paper/gui/GuiStatsComponent.java
new file mode 100644
index 0000000000000000000000000000000000000000..537bc6213545e8ff1b7b51bc4b27fd5b2a740883
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/gui/GuiStatsComponent.java
@@ -0,0 +1,41 @@
+package com.destroystokyo.paper.gui;
+
+import net.minecraft.server.MinecraftServer;
+
+import javax.swing.JPanel;
+import javax.swing.Timer;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+
+public class GuiStatsComponent extends JPanel {
+ private final Timer timer;
+ private final RAMGraph ramGraph;
+
+ public GuiStatsComponent(MinecraftServer server) {
+ super(new BorderLayout());
+
+ setOpaque(false);
+
+ ramGraph = new RAMGraph();
+ RAMDetails ramDetails = new RAMDetails(server);
+
+ add(ramGraph, "North");
+ add(ramDetails, "Center");
+
+ timer = new Timer(500, (event) -> {
+ ramGraph.update();
+ ramDetails.update();
+ });
+ timer.start();
+ }
+
+ @Override
+ public Dimension getPreferredSize() {
+ return new Dimension(350, 200);
+ }
+
+ public void close() {
+ timer.stop();
+ ramGraph.stop();
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java
new file mode 100644
index 0000000000000000000000000000000000000000..23239679d6584f1088b2b94c46eb9a5c1f9ad91d
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/gui/RAMDetails.java
@@ -0,0 +1,73 @@
+package com.destroystokyo.paper.gui;
+
+import net.minecraft.Util;
+import net.minecraft.server.MinecraftServer;
+
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.JList;
+import javax.swing.border.EmptyBorder;
+import java.awt.Dimension;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
+import java.util.Vector;
+
+public class RAMDetails extends JList<String> {
+ public static final DecimalFormat DECIMAL_FORMAT = Util.make(new DecimalFormat("########0.000"), (format)
+ -> format.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(Locale.ROOT)));
+
+ private final MinecraftServer server;
+
+ public RAMDetails(MinecraftServer server) {
+ this.server = server;
+
+ setBorder(new EmptyBorder(0, 10, 0, 0));
+ setFixedCellHeight(20);
+ setOpaque(false);
+
+ DefaultListCellRenderer renderer = new DefaultListCellRenderer();
+ renderer.setOpaque(false);
+ setCellRenderer(renderer);
+
+ setSelectionModel(new DefaultListSelectionModel() {
+ @Override
+ public void setAnchorSelectionIndex(final int anchorIndex) {
+ }
+
+ @Override
+ public void setLeadAnchorNotificationEnabled(final boolean flag) {
+ }
+
+ @Override
+ public void setLeadSelectionIndex(final int leadIndex) {
+ }
+
+ @Override
+ public void setSelectionInterval(final int index0, final int index1) {
+ }
+ });
+ }
+
+ @Override
+ public Dimension getPreferredSize() {
+ return new Dimension(350, 100);
+ }
+
+ public void update() {
+ GraphData data = RAMGraph.DATA.peekLast();
+ Vector<String> vector = new Vector<>();
+ vector.add("Memory use: " + (data.getUsedMem() / 1024L / 1024L) + " mb (" + (data.getFree() * 100L / data.getMax()) + "% free)");
+ vector.add("Heap: " + (data.getTotal() / 1024L / 1024L) + " / " + (data.getMax() / 1024L / 1024L) + " mb");
+ vector.add("Avg tick: " + DECIMAL_FORMAT.format(getAverage(server.tickTimes)) + " ms");
+ setListData(vector);
+ }
+
+ public double getAverage(long[] tickTimes) {
+ long total = 0L;
+ for (long value : tickTimes) {
+ total += value;
+ }
+ return ((double) total / (double) tickTimes.length) * 1.0E-6D;
+ }
+}
diff --git a/src/main/java/com/destroystokyo/paper/gui/RAMGraph.java b/src/main/java/com/destroystokyo/paper/gui/RAMGraph.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3e54da4ab6440811aab2f9dd1e218802ac13285
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/gui/RAMGraph.java
@@ -0,0 +1,144 @@
+package com.destroystokyo.paper.gui;
+
+import javax.swing.JComponent;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.ToolTipManager;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.MouseInfo;
+import java.awt.Point;
+import java.awt.PointerInfo;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.concurrent.TimeUnit;
+
+public class RAMGraph extends JComponent {
+ public static final LinkedList<GraphData> DATA = new LinkedList<GraphData>() {
+ @Override
+ public boolean add(GraphData data) {
+ if (size() >= 348) {
+ remove();
+ }
+ return super.add(data);
+ }
+ };
+
+ static {
+ GraphData empty = new GraphData(0, 0, 0);
+ for (int i = 0; i < 350; i++) {
+ DATA.add(empty);
+ }
+ }
+
+ private final Timer timer;
+ private final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:ss");
+
+ private int currentTick;
+
+ public RAMGraph() {
+ ToolTipManager.sharedInstance().setInitialDelay(0);
+
+ addMouseListener(new MouseAdapter() {
+ final int defaultDismissTimeout = ToolTipManager.sharedInstance().getDismissDelay();
+ final int dismissDelayMinutes = (int) TimeUnit.MINUTES.toMillis(10);
+
+ @Override
+ public void mouseEntered(MouseEvent me) {
+ ToolTipManager.sharedInstance().setDismissDelay(dismissDelayMinutes);
+ }
+
+ @Override
+ public void mouseExited(MouseEvent me) {
+ ToolTipManager.sharedInstance().setDismissDelay(defaultDismissTimeout);
+ }
+ });
+
+ timer = new Timer(50, (event) -> repaint());
+ timer.start();
+ }
+
+ @Override
+ public Dimension getPreferredSize() {
+ return new Dimension(350, 110);
+ }
+
+ public void update() {
+ Runtime jvm = Runtime.getRuntime();
+ DATA.add(new GraphData(jvm.totalMemory(), jvm.freeMemory(), jvm.maxMemory()));
+
+ PointerInfo pointerInfo = MouseInfo.getPointerInfo();
+ if (pointerInfo != null) {
+ Point point = pointerInfo.getLocation();
+ if (point != null) {
+ Point loc = new Point(point);
+ SwingUtilities.convertPointFromScreen(loc, this);
+ if (this.contains(loc)) {
+ ToolTipManager.sharedInstance().mouseMoved(
+ new MouseEvent(this, -1, System.currentTimeMillis(), 0, loc.x, loc.y,
+ point.x, point.y, 0, false, 0));
+ }
+ }
+ }
+
+ currentTick++;
+ }
+
+ @Override
+ public void paint(Graphics graphics) {
+ graphics.setColor(new Color(0xFFFFFFFF));
+ graphics.fillRect(0, 0, 350, 100);
+
+ graphics.setColor(new Color(0x888888));
+ graphics.drawLine(1, 25, 348, 25);
+ graphics.drawLine(1, 50, 348, 50);
+ graphics.drawLine(1, 75, 348, 75);
+
+ int i = 0;
+ for (GraphData data : DATA) {
+ i++;
+ if ((i + currentTick) % 120 == 0) {
+ graphics.setColor(new Color(0x888888));
+ graphics.drawLine(i, 1, i, 99);
+ }
+ int used = data.getUsedPercent();
+ if (used > 0) {
+ Color color = data.getLineColor();
+ graphics.setColor(data.getFillColor());
+ graphics.fillRect(i, 100 - used, 1, used);
+ graphics.setColor(color);
+ graphics.fillRect(i, 100 - used, 1, 1);
+ }
+ }
+
+ graphics.setColor(new Color(0xFF000000));
+ graphics.drawRect(0, 0, 348, 100);
+
+ Point m = getMousePosition();
+ if (m != null && m.x > 0 && m.x < 348 && m.y > 0 && m.y < 100) {
+ GraphData data = DATA.get(m.x);
+ int used = data.getUsedPercent();
+ graphics.setColor(new Color(0x000000));
+ graphics.drawLine(m.x, 1, m.x, 99);
+ graphics.drawOval(m.x - 2, 100 - used - 2, 5, 5);
+ graphics.setColor(data.getLineColor());
+ graphics.fillOval(m.x - 2, 100 - used - 2, 5, 5);
+ setToolTipText(String.format("<html><body>Used: %s mb (%s%%)<br/>%s</body></html>",
+ Math.round(data.getUsedMem() / 1024F / 1024F),
+ used, getTime(m.x)));
+ }
+ }
+
+ public String getTime(int halfSeconds) {
+ int millis = (348 - halfSeconds) / 2 * 1000;
+ return TIME_FORMAT.format(new Date((System.currentTimeMillis() - millis)));
+ }
+
+ public void stop() {
+ timer.stop();
+ }
+}
diff --git a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
index d292fdb165436f0b9b46b32110f5e09ad0e517a1..2e93eec8733c5b548a15269a322fe4dd1f189b7d 100644
--- a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
+++ b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
@@ -95,7 +95,7 @@ public class MinecraftServerGui extends JComponent {
private JComponent buildInfoPanel() {
JPanel jpanel = new JPanel(new BorderLayout());
- StatsComponent guistatscomponent = new StatsComponent(this.server);
+ com.destroystokyo.paper.gui.GuiStatsComponent guistatscomponent = new com.destroystokyo.paper.gui.GuiStatsComponent(this.server); // Paper
Collection<Runnable> collection = this.finalizers; // CraftBukkit - decompile error
Objects.requireNonNull(guistatscomponent);

View file

@ -1,30 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Trigary <trigary0@gmail.com>
Date: Sun, 1 Mar 2020 22:43:24 +0100
Subject: [PATCH] add hand to BlockMultiPlaceEvent
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index ee1be98a7844e983e46cf4ef6f0da1aa1b8c1c1d..96e33c433605ae2e0473b36f03894e030e79cd0d 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -403,13 +403,18 @@ public class CraftEventFactory {
}
org.bukkit.inventory.ItemStack item;
+ // Paper start - add hand to BlockMultiPlaceEvent
+ EquipmentSlot equipmentSlot;
if (hand == InteractionHand.MAIN_HAND) {
item = player.getInventory().getItemInMainHand();
+ equipmentSlot = EquipmentSlot.HAND;
} else {
item = player.getInventory().getItemInOffHand();
+ equipmentSlot = EquipmentSlot.OFF_HAND;
}
- BlockMultiPlaceEvent event = new BlockMultiPlaceEvent(blockStates, blockClicked, item, player, canBuild);
+ BlockMultiPlaceEvent event = new BlockMultiPlaceEvent(blockStates, blockClicked, item, player, canBuild, equipmentSlot);
+ // Paper end
craftServer.getPluginManager().callEvent(event);
return event;

View file

@ -1,18 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Sat, 7 Mar 2020 00:07:51 +0000
Subject: [PATCH] Validate tripwire hook placement before update
diff --git a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
index a5f8c7d9d9998eebce7f15e01c157651b9831516..4a516828e5c6abd63511ee7c93fcff11203cf8d0 100644
--- a/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/TripWireHookBlock.java
@@ -175,6 +175,7 @@ public class TripWireHookBlock extends Block {
this.emitState(world, pos, flag4, flag5, flag2, flag3);
if (!beingRemoved) {
+ if (world.getBlockState(pos).getBlock() == Blocks.TRIPWIRE_HOOK) // Paper - validate
world.setBlock(pos, (BlockState) iblockdata3.setValue(TripWireHookBlock.FACING, enumdirection), 3);
if (flag1) {
this.notifyNeighbors(world, pos, enumdirection);

View file

@ -1,19 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Sat, 13 Apr 2019 16:50:58 -0500
Subject: [PATCH] Add option to allow iron golems to spawn in air
diff --git a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
index ce83fe0b68fcb229dd37fe07e5f21b52a60c32de..f383928fc5b331ddf128bdcb6a23010d8fe088d3 100644
--- a/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
+++ b/src/main/java/net/minecraft/world/entity/animal/IronGolem.java
@@ -325,7 +325,7 @@ public class IronGolem extends AbstractGolem implements NeutralMob {
BlockPos blockposition1 = blockposition.below();
BlockState iblockdata = world.getBlockState(blockposition1);
- if (!iblockdata.entityCanStandOn(world, blockposition1, this)) {
+ if (!iblockdata.entityCanStandOn(world, blockposition1, this) && !this.level().paperConfig().entities.spawning.ironGolemsCanSpawnInAir) { // Paper
return false;
} else {
for (int i = 1; i < 3; ++i) {

View file

@ -1,28 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zero <zero@cock.li>
Date: Sat, 22 Feb 2020 16:10:31 -0500
Subject: [PATCH] Configurable chance of villager zombie infection
This allows you to solve an issue in vanilla behavior where:
* On easy difficulty your villagers will NEVER get infected, meaning they will always die.
* On normal difficulty they will have a 50% of getting infected or dying.
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
index 39a508b2834e554e94d635abd0d4a549f317dee5..93ca1016806103fe688379c99afc57cb02635f65 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
@@ -452,12 +452,8 @@ public class Zombie extends Monster {
public boolean killedEntity(ServerLevel world, LivingEntity other) {
boolean flag = super.killedEntity(world, other);
- if ((world.getDifficulty() == Difficulty.NORMAL || world.getDifficulty() == Difficulty.HARD) && other instanceof Villager) {
- Villager entityvillager = (Villager) other;
-
- if (world.getDifficulty() != Difficulty.HARD && this.random.nextBoolean()) {
- return flag;
- }
+ final double fallbackChance = world.getDifficulty() == Difficulty.HARD ? 100d : world.getDifficulty() == Difficulty.NORMAL ? 50d : 0d; // Paper
+ if (this.random.nextDouble() * 100 < world.paperConfig().entities.behavior.zombieVillagerInfectionChance.or(fallbackChance) && other instanceof Villager entityvillager) { // Paper
// CraftBukkit start
flag = Zombie.zombifyVillager(world, entityvillager, this.blockPosition(), this.isSilent(), CreatureSpawnEvent.SpawnReason.INFECTION) == null;
}

View file

@ -1,61 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Tue, 14 Jan 2020 14:59:08 -0800
Subject: [PATCH] Optimise Chunk#getFluid
Removing the try catch and generally reducing ops should make it
faster on its own, however removing the try catch makes it
easier to inline due to code size
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
index 4a5491a79281b7cddf5ad5f060567f174b89d14b..12834995b2bab9e82a40feb01f038532961296cf 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -377,18 +377,20 @@ public class LevelChunk extends ChunkAccess {
}
public FluidState getFluidState(int x, int y, int z) {
- try {
- int l = this.getSectionIndex(y);
-
- if (l >= 0 && l < this.sections.length) {
- LevelChunkSection chunksection = this.sections[l];
+ // try { // Paper - remove try catch
+ // Paper start - reduce the number of ops in this call
+ int index = this.getSectionIndex(y);
+ if (index >= 0 && index < this.sections.length) {
+ LevelChunkSection chunksection = this.sections[index];
if (!chunksection.hasOnlyAir()) {
- return chunksection.getFluidState(x & 15, y & 15, z & 15);
+ return chunksection.states.get((y & 15) << 8 | (z & 15) << 4 | x & 15).getFluidState();
+ // Paper end
}
}
return Fluids.EMPTY.defaultFluidState();
+ /* // Paper - remove try catch
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.forThrowable(throwable, "Getting fluid state");
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Block being got");
@@ -398,6 +400,7 @@ public class LevelChunk extends ChunkAccess {
});
throw new ReportedException(crashreport);
}
+ */ // Paper - remove try catch
}
// CraftBukkit start
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
index 4eeb719b40ff1c18a7cdda7ecc6b135dbedd626e..b8fee4f8a0cfe32b9ef7f3f3cf818cbaec0d3fca 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunkSection.java
@@ -46,7 +46,7 @@ public class LevelChunkSection {
}
public FluidState getFluidState(int x, int y, int z) {
- return ((BlockState) this.states.get(x, y, z)).getFluidState();
+ return this.states.get(x, y, z).getFluidState(); // Paper - diff on change - we expect this to be effectively just getType(x, y, z).getFluid(). If this changes we need to check other patches that use IBlockData#getFluid.
}
public void acquire() {

View file

@ -1,19 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Wed, 2 Dec 2020 20:17:54 -0800
Subject: [PATCH] Set spigots verbose world setting to false by def
diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
index 50e57486d49694396816122504028e410ebb602d..11ac344ef113732fa717b67c51f76692b9b247e7 100644
--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
@@ -20,7 +20,7 @@ public class SpigotWorldConfig
public void init()
{
- this.verbose = this.getBoolean( "verbose", true );
+ this.verbose = this.getBoolean( "verbose", false ); // Paper
this.log( "-------- World Settings For [" + this.worldName + "] --------" );
SpigotConfig.readConfig( SpigotWorldConfig.class, this );

View file

@ -1,206 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
Date: Sun, 5 Apr 2020 22:23:14 -0500
Subject: [PATCH] Add tick times API and /mspt command
diff --git a/src/main/java/io/papermc/paper/command/MSPTCommand.java b/src/main/java/io/papermc/paper/command/MSPTCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b5293b0c696ef21d0101493ffa41b60bf0bc86b
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/MSPTCommand.java
@@ -0,0 +1,102 @@
+package io.papermc.paper.command;
+
+import net.kyori.adventure.text.Component;
+import net.minecraft.server.MinecraftServer;
+import org.bukkit.Location;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.GOLD;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
+
+@DefaultQualifier(NonNull.class)
+public final class MSPTCommand extends Command {
+ private static final DecimalFormat DF = new DecimalFormat("########0.0");
+ private static final Component SLASH = text("/");
+
+ public MSPTCommand(final String name) {
+ super(name);
+ this.description = "View server tick times";
+ this.usageMessage = "/mspt";
+ this.setPermission("bukkit.command.mspt");
+ }
+
+ @Override
+ public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
+ if (!testPermission(sender)) return true;
+
+ MinecraftServer server = MinecraftServer.getServer();
+
+ List<Component> times = new ArrayList<>();
+ times.addAll(eval(server.tickTimes5s.getTimes()));
+ times.addAll(eval(server.tickTimes10s.getTimes()));
+ times.addAll(eval(server.tickTimes60s.getTimes()));
+
+ sender.sendMessage(text().content("Server tick times ").color(GOLD)
+ .append(text().color(YELLOW)
+ .append(
+ text("("),
+ text("avg", GRAY),
+ text("/"),
+ text("min", GRAY),
+ text("/"),
+ text("max", GRAY),
+ text(")")
+ )
+ ).append(
+ text(" from last 5s"),
+ text(",", GRAY),
+ text(" 10s"),
+ text(",", GRAY),
+ text(" 1m"),
+ text(":", YELLOW)
+ )
+ );
+ sender.sendMessage(text().content("◴ ").color(GOLD)
+ .append(text().color(GRAY)
+ .append(
+ times.get(0), SLASH, times.get(1), SLASH, times.get(2), text(", ", YELLOW),
+ times.get(3), SLASH, times.get(4), SLASH, times.get(5), text(", ", YELLOW),
+ times.get(6), SLASH, times.get(7), SLASH, times.get(8)
+ )
+ )
+ );
+ return true;
+ }
+
+ private static List<Component> eval(long[] times) {
+ long min = Integer.MAX_VALUE;
+ long max = 0L;
+ long total = 0L;
+ for (long value : times) {
+ if (value > 0L && value < min) min = value;
+ if (value > max) max = value;
+ total += value;
+ }
+ double avgD = ((double) total / (double) times.length) * 1.0E-6D;
+ double minD = ((double) min) * 1.0E-6D;
+ double maxD = ((double) max) * 1.0E-6D;
+ return Arrays.asList(getColor(avgD), getColor(minD), getColor(maxD));
+ }
+
+ private static Component getColor(double avg) {
+ return text(DF.format(avg), avg >= 50 ? RED : avg >= 40 ? YELLOW : GREEN);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/command/PaperCommands.java b/src/main/java/io/papermc/paper/command/PaperCommands.java
index 72f2e81b9905a0d57ed8e2a88578f62d5235c456..7b58b2d6297800c2dcdbf7539e5ab8e7703f39f1 100644
--- a/src/main/java/io/papermc/paper/command/PaperCommands.java
+++ b/src/main/java/io/papermc/paper/command/PaperCommands.java
@@ -18,6 +18,7 @@ public final class PaperCommands {
static {
COMMANDS.put("paper", new PaperCommand("paper"));
COMMANDS.put("callback", new CallbackCommand("callback"));
+ COMMANDS.put("mspt", new MSPTCommand("mspt"));
}
public static void registerCommands(final MinecraftServer server) {
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index ef6d98d503fdca4322000278de4cf325df56f99d..f700e677d75f911b786a22b4cc9d0a016d99b02f 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -247,6 +247,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
private net.kyori.adventure.text.Component motd; // Paper - Adventure
private int playerIdleTimeout;
public final long[] tickTimes;
+ // Paper start
+ public final TickTimes tickTimes5s = new TickTimes(100);
+ public final TickTimes tickTimes10s = new TickTimes(200);
+ public final TickTimes tickTimes60s = new TickTimes(1200);
+ // Paper end
@Nullable
private KeyPair keyPair;
@Nullable
@@ -1322,6 +1327,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.averageTickTime = this.averageTickTime * 0.8F + (float) j / 1000000.0F * 0.19999999F;
long k = Util.getNanos();
+ // Paper start
+ tickTimes5s.add(this.tickCount, j);
+ tickTimes10s.add(this.tickCount, j);
+ tickTimes60s.add(this.tickCount, j);
+ // Paper end
this.logTickTime(k - i);
this.profiler.pop();
org.spigotmc.WatchdogThread.tick(); // Spigot
@@ -2568,4 +2578,30 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public static record ServerResourcePackInfo(String url, String hash, boolean isRequired, @Nullable Component prompt) {
}
+
+ // Paper start
+ public static class TickTimes {
+ private final long[] times;
+
+ public TickTimes(int length) {
+ times = new long[length];
+ }
+
+ void add(int index, long time) {
+ times[index % times.length] = time;
+ }
+
+ public long[] getTimes() {
+ return times.clone();
+ }
+
+ public double getAverage() {
+ long total = 0L;
+ for (long value : times) {
+ total += value;
+ }
+ return ((double) total / (double) times.length) * 1.0E-6D;
+ }
+ }
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 871a1095d28bde74cfb63091d77c860f92a2ea0e..4545083ceb2e180881b981842ef8857dbe4773dd 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2596,6 +2596,16 @@ public final class CraftServer implements Server {
net.minecraft.server.MinecraftServer.getServer().tps15.getAverage()
};
}
+
+ @Override
+ public long[] getTickTimes() {
+ return getServer().tickTimes5s.getTimes();
+ }
+
+ @Override
+ public double getAverageTickTime() {
+ return getServer().tickTimes5s.getAverage();
+ }
// Paper end
// Spigot start

View file

@ -1,22 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: JRoy <joshroy126@gmail.com>
Date: Fri, 10 Apr 2020 21:24:12 -0400
Subject: [PATCH] Expose MinecraftServer#isRunning
This allows for plugins to detect if the server is actually turning off in onDisable rather than just plugins reloading.
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 4545083ceb2e180881b981842ef8857dbe4773dd..83661761abdb31e62ea56af9d60fed393326be50 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2832,5 +2832,10 @@ public final class CraftServer implements Server {
public int getCurrentTick() {
return net.minecraft.server.MinecraftServer.currentTick;
}
+
+ @Override
+ public boolean isStopping() {
+ return net.minecraft.server.MinecraftServer.getServer().hasStopped();
+ }
// Paper end
}

View file

@ -1,64 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mariell Hoversholm <proximyst@proximyst.com>
Date: Thu, 30 Apr 2020 16:56:54 +0200
Subject: [PATCH] Add Raw Byte ItemStack Serialization
Serializes using NBT which is safer for server data migrations than bukkits format.
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index f967975d641fadce813a34a9344f6294368b797c..a78c6610be6e89dd89881867b8b91ed9cece7562 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -461,6 +461,52 @@ public final class CraftMagicNumbers implements UnsafeValues {
public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() {
return new com.destroystokyo.paper.PaperVersionFetcher();
}
+
+ @Override
+ public byte[] serializeItem(ItemStack item) {
+ Preconditions.checkNotNull(item, "null cannot be serialized");
+ Preconditions.checkArgument(item.getType() != Material.AIR, "air cannot be serialized");
+
+ return serializeNbtToBytes((item instanceof CraftItemStack ? ((CraftItemStack) item).handle : CraftItemStack.asNMSCopy(item)).save(new CompoundTag()));
+ }
+
+ @Override
+ public ItemStack deserializeItem(byte[] data) {
+ Preconditions.checkNotNull(data, "null cannot be deserialized");
+ Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing");
+
+ CompoundTag compound = deserializeNbtFromBytes(data);
+ int dataVersion = compound.getInt("DataVersion");
+ return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.of(ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.ITEM_STACK, compound, dataVersion, getDataVersion())));
+ }
+
+ private byte[] serializeNbtToBytes(CompoundTag compound) {
+ compound.putInt("DataVersion", getDataVersion());
+ java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();
+ try {
+ net.minecraft.nbt.NbtIo.writeCompressed(
+ compound,
+ outputStream
+ );
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ return outputStream.toByteArray();
+ }
+
+ private CompoundTag deserializeNbtFromBytes(byte[] data) {
+ CompoundTag compound;
+ try {
+ compound = net.minecraft.nbt.NbtIo.readCompressed(
+ new java.io.ByteArrayInputStream(data)
+ );
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ int dataVersion = compound.getInt("DataVersion");
+ Preconditions.checkArgument(dataVersion <= getDataVersion(), "Newer version! Server downgrades are not supported!");
+ return compound;
+ }
// Paper end
/**

View file

@ -1,96 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Phoenix616 <mail@moep.tv>
Date: Sat, 1 Feb 2020 16:50:39 +0100
Subject: [PATCH] Pillager patrol spawn settings and per player options
This adds config options for defining the spawn chance, spawn delay and
spawn start day as well as toggles for handling the spawn delay and
start day per player. (Based on the time played statistic)
When not per player it will use the Vanilla mechanic of one delay per
world and the world age for the start day.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 0952769c94519f9fb300bc14c498f19107364643..100bcf5174c06bb4450f0056bb5b5ef610ee1da6 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -242,6 +242,7 @@ public class ServerPlayer extends Player {
public boolean wonGame;
private int containerUpdateDelay; // Paper
public long loginTime; // Paper
+ public int patrolSpawnDelay; // Paper - per player patrol spawns
// Paper start - cancellable death event
public boolean queueHealthUpdatePacket = false;
public net.minecraft.network.protocol.game.ClientboundSetHealthPacket queuedHealthUpdatePacket;
diff --git a/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java b/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java
index e5918fa3be107ac3a2fc8831fd78733a7506730a..a908652f1ebb426d265ef614746f70cd1e538268 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java
@@ -25,7 +25,7 @@ public class PatrolSpawner implements CustomSpawner {
@Override
public int tick(ServerLevel world, boolean spawnMonsters, boolean spawnAnimals) {
- if (world.paperConfig().entities.behavior.pillagerPatrols.disable) return 0; // Paper
+ if (world.paperConfig().entities.behavior.pillagerPatrols.disable || world.paperConfig().entities.behavior.pillagerPatrols.spawnChance == 0) return 0; // Paper
if (!spawnMonsters) {
return 0;
} else if (!world.getGameRules().getBoolean(GameRules.RULE_DO_PATROL_SPAWNING)) {
@@ -33,23 +33,51 @@ public class PatrolSpawner implements CustomSpawner {
} else {
RandomSource randomsource = world.random;
- --this.nextTick;
- if (this.nextTick > 0) {
+ // Paper start - Patrol settings
+ // Random player selection moved up for per player spawning and configuration
+ int j = world.players().size();
+ if (j < 1) {
return 0;
+ }
+
+ net.minecraft.server.level.ServerPlayer entityhuman = world.players().get(randomsource.nextInt(j));
+ if (entityhuman.isSpectator()) {
+ return 0;
+ }
+
+ int patrolSpawnDelay;
+ if (world.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.perPlayer) {
+ --entityhuman.patrolSpawnDelay;
+ patrolSpawnDelay = entityhuman.patrolSpawnDelay;
} else {
- this.nextTick += 12000 + randomsource.nextInt(1200);
- long i = world.getDayTime() / 24000L;
+ this.nextTick--;
+ patrolSpawnDelay = this.nextTick;
+ }
+
+ if (patrolSpawnDelay > 0) {
+ return 0;
+ } else {
+ long days;
+ if (world.paperConfig().entities.behavior.pillagerPatrols.start.perPlayer) {
+ days = entityhuman.getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.PLAY_TIME)) / 24000L; // PLAY_ONE_MINUTE is actually counting in ticks, a misnomer by Mojang
+ } else {
+ days = world.getDayTime() / 24000L;
+ }
+ if (world.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.perPlayer) {
+ entityhuman.patrolSpawnDelay += world.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.ticks + randomsource.nextInt(1200);
+ } else {
+ this.nextTick += world.paperConfig().entities.behavior.pillagerPatrols.spawnDelay.ticks + randomsource.nextInt(1200);
+ }
- if (i >= 5L && world.isDay()) {
- if (randomsource.nextInt(5) != 0) {
+ if (days >= world.paperConfig().entities.behavior.pillagerPatrols.start.day && world.isDay()) {
+ if (randomsource.nextDouble() >= world.paperConfig().entities.behavior.pillagerPatrols.spawnChance) {
+ // Paper end
return 0;
} else {
- int j = world.players().size();
if (j < 1) {
return 0;
} else {
- Player entityhuman = (Player) world.players().get(randomsource.nextInt(j));
if (entityhuman.isSpectator()) {
return 0;

View file

@ -1,44 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 31 Mar 2020 03:50:42 -0400
Subject: [PATCH] Remote Connections shouldn't hold up shutdown
Bugs in the connection logic appears to leave stale connections even, preventing shutdown
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index 97f80226793e700c8b05e41005bb2751a6b00f33..307f3ab69186c0980f33d664bb2db8d1aae228f8 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -390,11 +390,11 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
}
if (this.rconThread != null) {
- this.rconThread.stop();
+ this.rconThread.stopNonBlocking(); // Paper - don't wait for remote connections
}
if (this.queryThreadGs4 != null) {
- this.queryThreadGs4.stop();
+ // this.remoteStatusListener.stop(); // Paper - don't wait for remote connections
}
System.exit(0); // CraftBukkit
diff --git a/src/main/java/net/minecraft/server/rcon/thread/RconThread.java b/src/main/java/net/minecraft/server/rcon/thread/RconThread.java
index 3bf60f640aa9fa4cabd2b3e5d3931e8467b9df24..2c1289aa2bf8b7bb67709190263b82b811c17fff 100644
--- a/src/main/java/net/minecraft/server/rcon/thread/RconThread.java
+++ b/src/main/java/net/minecraft/server/rcon/thread/RconThread.java
@@ -107,6 +107,14 @@ public class RconThread extends GenericThread {
this.clients.clear();
}
+ // Paper start
+ public void stopNonBlocking() {
+ this.running = false;
+ for (RconClient client : this.clients) {
+ client.running = false;
+ }
+ }
+ // Paper stop
private void closeSocket(ServerSocket socket) {
LOGGER.debug("closeSocket: {}", (Object)socket);

View file

@ -1,42 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: chickeneer <emcchickeneer@gmail.com>
Date: Tue, 17 Mar 2020 14:18:50 -0500
Subject: [PATCH] Do not allow bees to load chunks for beehives
diff --git a/src/main/java/net/minecraft/world/entity/animal/Bee.java b/src/main/java/net/minecraft/world/entity/animal/Bee.java
index 27e3bed06cfb5c36125b55c4862fca94ade26a09..41f5b4fc4a4b7d2a54b08869d4afa450f34caf91 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Bee.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java
@@ -413,6 +413,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
if (this.hivePos == null) {
return false;
} else {
+ if (!this.level().isLoadedAndInBounds(this.hivePos)) return false; // Paper
BlockEntity tileentity = this.level().getBlockEntity(this.hivePos);
return tileentity instanceof BeehiveBlockEntity && ((BeehiveBlockEntity) tileentity).isFireNearby();
@@ -446,6 +447,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
}
private boolean doesHiveHaveSpace(BlockPos pos) {
+ if (!this.level().isLoadedAndInBounds(pos)) return false; // Paper
BlockEntity tileentity = this.level().getBlockEntity(pos);
return tileentity instanceof BeehiveBlockEntity ? !((BeehiveBlockEntity) tileentity).isFull() : false;
@@ -922,6 +924,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
@Override
public boolean canBeeUse() {
if (Bee.this.hasHive() && Bee.this.wantsToEnterHive() && Bee.this.hivePos.closerToCenterThan(Bee.this.position(), 2.0D)) {
+ if (!Bee.this.level().isLoadedAndInBounds(Bee.this.hivePos)) return false; // Paper
BlockEntity tileentity = Bee.this.level().getBlockEntity(Bee.this.hivePos);
if (tileentity instanceof BeehiveBlockEntity) {
@@ -945,6 +948,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
@Override
public void start() {
+ if (!Bee.this.level().isLoadedAndInBounds(Bee.this.hivePos)) return; // Paper
BlockEntity tileentity = Bee.this.level().getBlockEntity(Bee.this.hivePos);
if (tileentity instanceof BeehiveBlockEntity) {

View file

@ -1,47 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 2 Apr 2020 01:42:39 -0400
Subject: [PATCH] Prevent Double PlayerChunkMap adds crashing server
Suspected case would be around the technique used in .stopRiding
Stack will identify any causer of this and warn instead of crashing.
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index bd15131b7506e965bcf64be20330731256a1e1f0..0e3c89b9d75160d0e8947d042a1568da13d62fcf 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -994,6 +994,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public void addEntity(Entity entity) {
org.spigotmc.AsyncCatcher.catchOp("entity track"); // Spigot
+ // Paper start - ignore and warn about illegal addEntity calls instead of crashing server
+ if (!entity.valid || entity.level() != this.level || this.entityMap.containsKey(entity.getId())) {
+ LOGGER.error("Illegal ChunkMap::addEntity for world " + this.level.getWorld().getName()
+ + ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable());
+ return;
+ }
+ // Paper end
if (!(entity instanceof EnderDragonPart)) {
EntityType<?> entitytypes = entity.getType();
int i = entitytypes.clientTrackingRange() * 16;
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 6c8fa0ed1e445e77593b6d26c09eeaac1437787d..0353b3e3a9d9a0bd44f48a61a02811ec0bad186a 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -2466,7 +2466,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
public void onTrackingStart(Entity entity) {
org.spigotmc.AsyncCatcher.catchOp("entity register"); // Spigot
- ServerLevel.this.getChunkSource().addEntity(entity);
+ // ServerLevel.this.getChunkSource().addEntity(entity); // Paper - moved down below valid=true
if (entity instanceof ServerPlayer) {
ServerPlayer entityplayer = (ServerPlayer) entity;
@@ -2500,6 +2500,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
entity.updateDynamicGameEventListener(DynamicGameEventListener::add);
entity.valid = true; // CraftBukkit
+ ServerLevel.this.getChunkSource().addEntity(entity);
// Paper start - Set origin location when the entity is being added to the world
if (entity.getOriginVector() == null) {
entity.setOrigin(entity.getBukkitEntity().getLocation());

View file

@ -1,21 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 2 Apr 2020 17:16:48 -0400
Subject: [PATCH] Don't tick dead players
Causes sync chunk loads and who knows what all else.
This is safe because Spectators are skipped in unloaded chunks too in vanilla.
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 100bcf5174c06bb4450f0056bb5b5ef610ee1da6..d35b358ec97762788fd72b37470d97f1d6d927ef 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -725,7 +725,7 @@ public class ServerPlayer extends Player {
public void doTick() {
try {
- if (!this.isSpectator() || !this.touchingUnloadedChunk()) {
+ if (valid && !this.isSpectator() || !this.touchingUnloadedChunk()) { // Paper - don't tick dead players that are not in the world currently (pending respawn)
super.tick();
}

View file

@ -1,21 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 2 Apr 2020 19:31:16 -0400
Subject: [PATCH] Dead Player's shouldn't be able to move
This fixes a lot of game state issues where packets were delayed for processing
due to 1.15's new queue but processed while dead.
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
index cc6a9e9566d6f9bf453f3d6fba132c886248bf1c..fb3f65feab6f5febf58be71f0b9b1a4381958f1d 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -1166,7 +1166,7 @@ public abstract class Player extends LivingEntity {
@Override
protected boolean isImmobile() {
- return super.isImmobile() || this.isSleeping();
+ return super.isImmobile() || this.isSleeping() || this.isRemoved() || !valid; // Paper - player's who are dead or not in a world shouldn't move...
}
@Override

View file

@ -1,111 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 2 Apr 2020 02:37:57 -0400
Subject: [PATCH] Optimize Collision to not load chunks
The collision code takes an AABB and generates a cuboid of checks rather
than a cylinder, so at high velocity this can generate a lot of chunk checks.
Treat an unloaded chunk as a collision for entities, and also for players if
the "prevent moving into unloaded chunks" setting is enabled.
If that serting is not enabled, collisions will be ignored for players, since
movement will load only the chunk the player enters anyways and avoids loading
massive amounts of surrounding chunks due to large AABB lookups.
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index c5287f08c675550c897d72fd7eff3331d0640800..211cc5e508473f14827910aef88ee75a1203edab 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -824,6 +824,7 @@ public abstract class PlayerList {
entityplayer1.setShiftKeyDown(false);
entityplayer1.forceSetPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
+ worldserver1.getChunkSource().addRegionTicket(net.minecraft.server.level.TicketType.POST_TELEPORT, new net.minecraft.world.level.ChunkPos(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
while (avoidSuffocation && !worldserver1.noCollision((Entity) entityplayer1) && entityplayer1.getY() < (double) worldserver1.getMaxBuildHeight()) {
// CraftBukkit end
entityplayer1.setPos(entityplayer1.getX(), entityplayer1.getY() + 1.0D, entityplayer1.getZ());
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 15b807c0a49461ae5daecd6aef43f5c2d464b224..2f189712675b988a5bbb796abf54740470d836f4 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -237,6 +237,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper
public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper
+ public boolean collisionLoadChunks = false; // Paper
private CraftEntity bukkitEntity;
public @org.jetbrains.annotations.Nullable net.minecraft.server.level.ChunkMap.TrackedEntity tracker; // Paper
diff --git a/src/main/java/net/minecraft/world/level/BlockCollisions.java b/src/main/java/net/minecraft/world/level/BlockCollisions.java
index f2c423154ed6a00882a46d93b69ed4f6ba73782c..a3eaf80b020c3bbc0306c5d17659ee661dfd275b 100644
--- a/src/main/java/net/minecraft/world/level/BlockCollisions.java
+++ b/src/main/java/net/minecraft/world/level/BlockCollisions.java
@@ -65,22 +65,41 @@ public class BlockCollisions<T> extends AbstractIterator<T> {
protected T computeNext() {
while(true) {
if (this.cursor.advance()) {
- int i = this.cursor.nextX();
- int j = this.cursor.nextY();
- int k = this.cursor.nextZ();
+ int i = this.cursor.nextX(); final int x = i; // Paper
+ int j = this.cursor.nextY(); final int y = j; // Paper
+ int k = this.cursor.nextZ(); final int z = k; // Paper
int l = this.cursor.getNextType();
if (l == 3) {
continue;
}
+ // Paper start - ensure we don't load chunks
+ final @Nullable Entity source = this.context instanceof net.minecraft.world.phys.shapes.EntityCollisionContext entityContext ? entityContext.getEntity() : null;
+ boolean far = source != null && io.papermc.paper.util.MCUtil.distanceSq(source.getX(), y, source.getZ(), x, y, z) > 14;
+ this.pos.set(x, y, z);
- BlockGetter blockGetter = this.getChunk(i, k);
- if (blockGetter == null) {
+ BlockState blockState;
+ if (this.collisionGetter instanceof net.minecraft.server.level.WorldGenRegion) {
+ BlockGetter blockGetter = this.getChunk(x, z);
+ if (blockGetter == null) {
+ continue;
+ }
+ blockState = blockGetter.getBlockState(this.pos);
+ } else if ((!far && source instanceof net.minecraft.server.level.ServerPlayer) || (source != null && source.collisionLoadChunks)) {
+ blockState = this.collisionGetter.getBlockState(this.pos);
+ } else {
+ blockState = this.collisionGetter.getBlockStateIfLoaded(this.pos);
+ }
+
+ if (blockState == null) {
+ if (!(source instanceof net.minecraft.server.level.ServerPlayer) || source.level().paperConfig().chunks.preventMovingIntoUnloadedChunks) {
+ return this.resultProvider.apply(new BlockPos.MutableBlockPos(x, y, z), Shapes.create(far ? source.getBoundingBox() : new AABB(new BlockPos(x, y, z))));
+ }
+ // Paper end
continue;
}
- this.pos.set(i, j, k);
- BlockState blockState = blockGetter.getBlockState(this.pos);
- if (this.onlySuffocatingBlocks && !blockState.isSuffocating(blockGetter, this.pos) || l == 1 && !blockState.hasLargeCollisionShape() || l == 2 && !blockState.is(Blocks.MOVING_PISTON)) {
+ // Paper - moved up
+ if (/*this.onlySuffocatingBlocks && (!blockState.isSuffocating(blockGetter, this.pos)) ||*/ l == 1 && !blockState.hasLargeCollisionShape() || l == 2 && !blockState.is(Blocks.MOVING_PISTON)) { // Paper - onlySuffocatingBlocks is only true on the client, so we don't care about it here
continue;
}
diff --git a/src/main/java/net/minecraft/world/level/CollisionGetter.java b/src/main/java/net/minecraft/world/level/CollisionGetter.java
index 140d10807a3a6806578cd203ba58383590c2f2c0..c476e37df8a75d77f5093b2a449e04f25ef2c2dd 100644
--- a/src/main/java/net/minecraft/world/level/CollisionGetter.java
+++ b/src/main/java/net/minecraft/world/level/CollisionGetter.java
@@ -44,11 +44,13 @@ public interface CollisionGetter extends BlockGetter {
}
default boolean noCollision(@Nullable Entity entity, AABB box) {
+ try { if (entity != null) entity.collisionLoadChunks = true; // Paper
for(VoxelShape voxelShape : this.getBlockCollisions(entity, box)) {
if (!voxelShape.isEmpty()) {
return false;
}
}
+ } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
if (!this.getEntityCollisions(entity, box).isEmpty()) {
return false;

View file

@ -1,48 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 9 Apr 2020 21:20:33 -0400
Subject: [PATCH] Don't move existing players to world spawn
This can cause a nasty server lag the spawn chunks are not kept loaded
or they aren't finished loading yet, or if the world spawn radius is
larger than the keep loaded range.
By skipping this, we avoid potential for a large spike on server start.
== AT ==
public net.minecraft.server.level.ServerPlayer fudgeSpawnLocation(Lnet/minecraft/server/level/ServerLevel;)V
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index d35b358ec97762788fd72b37470d97f1d6d927ef..a5dd060ad0b978fddbc193feb8bbab99342b4d56 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -378,7 +378,7 @@ public class ServerPlayer extends Player {
this.stats = server.getPlayerList().getPlayerStats(this);
this.advancements = server.getPlayerList().getPlayerAdvancements(this);
this.setMaxUpStep(1.0F);
- this.fudgeSpawnLocation(world);
+ // this.fudgeSpawnLocation(world); // Paper - don't move to spawn on login, only first join
this.updateOptions(clientOptions);
this.cachedSingleHashSet = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
@@ -612,7 +612,7 @@ public class ServerPlayer extends Player {
position = Vec3.atCenterOf(world.getSharedSpawnPos());
}
this.setLevel(world);
- this.setPos(position);
+ this.setPosRaw(position.x(), position.y(), position.z()); // Paper - don't register to chunks yet
}
this.gameMode.setLevel((ServerLevel) world);
}
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 211cc5e508473f14827910aef88ee75a1203edab..f894d6549e4abf1001de68c8bc3ea0129e973e18 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -228,6 +228,7 @@ public abstract class PlayerList {
// Paper start
if (nbttagcompound == null) {
player.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT; // set Player SpawnReason to DEFAULT on first login
+ player.fudgeSpawnLocation(worldserver1); // only move to spawn on first login, otherwise, stay where you are....
}
// Paper end
player.setServerLevel(worldserver1);

View file

@ -1,169 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 6 Apr 2020 17:53:29 -0700
Subject: [PATCH] Optimize GoalSelector Goal.Flag Set operations
Optimise the stream.anyMatch statement to move to a bitset
where we can replace the call with a single bitwise operation.
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
index 6667ecc4b7eded4e20a415cef1e1b1179e6710b8..4379b9948f1eecfe6fd7dea98e298ad5f761019a 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/Goal.java
@@ -4,7 +4,8 @@ import java.util.EnumSet;
import net.minecraft.util.Mth;
public abstract class Goal {
- private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class);
+ private final EnumSet<Goal.Flag> flags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
+ private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
public abstract boolean canUse();
@@ -30,8 +31,10 @@ public abstract class Goal {
}
public void setFlags(EnumSet<Goal.Flag> controls) {
- this.flags.clear();
- this.flags.addAll(controls);
+ // Paper start - remove streams from pathfindergoalselector
+ this.goalTypes.clear();
+ this.goalTypes.addAllUnchecked(controls);
+ // Paper end - remove streams from pathfindergoalselector
}
@Override
@@ -39,8 +42,10 @@ public abstract class Goal {
return this.getClass().getSimpleName();
}
- public EnumSet<Goal.Flag> getFlags() {
- return this.flags;
+ // Paper start - remove streams from pathfindergoalselector
+ public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
+ return this.goalTypes;
+ // Paper end - remove streams from pathfindergoalselector
}
protected int adjustedTickDelay(int ticks) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
index e5995d0db5dcfba59a873ff439601894fdacd556..b738ee2d3801fadfd09313f05ae24593e56b0ec6 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/GoalSelector.java
@@ -30,10 +30,12 @@ public class GoalSelector {
private final Map<Goal.Flag, WrappedGoal> lockedFlags = new EnumMap<>(Goal.Flag.class);
private final Set<WrappedGoal> availableGoals = Sets.newLinkedHashSet();
private final Supplier<ProfilerFiller> profiler;
- private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class);
+ private final EnumSet<Goal.Flag> disabledFlags = EnumSet.noneOf(Goal.Flag.class); // Paper unused, but dummy to prevent plugins from crashing as hard. Theyll need to support paper in a special case if this is super important, but really doesn't seem like it would be.
+ private final com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<net.minecraft.world.entity.ai.goal.Goal.Flag> goalTypes = new com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector
private int tickCount;
private int newGoalRate = 3;
private int curRate;
+ private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from pathfindergoalselector
public GoalSelector(Supplier<ProfilerFiller> profiler) {
this.profiler = profiler;
@@ -65,26 +67,32 @@ public class GoalSelector {
}
// Paper end
public void removeGoal(Goal goal) {
- this.availableGoals.stream().filter((wrappedGoal) -> {
- return wrappedGoal.getGoal() == goal;
- }).filter(WrappedGoal::isRunning).forEach(WrappedGoal::stop);
- this.availableGoals.removeIf((wrappedGoal) -> {
- return wrappedGoal.getGoal() == goal;
- });
- }
-
- private static boolean goalContainsAnyFlags(WrappedGoal goal, EnumSet<Goal.Flag> controls) {
- for(Goal.Flag flag : goal.getFlags()) {
- if (controls.contains(flag)) {
- return true;
+ // Paper start - remove streams from pathfindergoalselector
+ for (java.util.Iterator<WrappedGoal> iterator = this.availableGoals.iterator(); iterator.hasNext();) {
+ WrappedGoal goalWrapped = iterator.next();
+ if (goalWrapped.getGoal() != goal) {
+ continue;
}
+ if (goalWrapped.isRunning()) {
+ goalWrapped.stop();
+ }
+ iterator.remove();
}
+ // Paper end - remove streams from pathfindergoalselector
+ }
- return false;
+ private static boolean goalContainsAnyFlags(WrappedGoal goal, com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> controls) {
+ return goal.getFlags().hasCommonElements(controls); // Paper
}
private static boolean goalCanBeReplacedForAllFlags(WrappedGoal goal, Map<Goal.Flag, WrappedGoal> goalsByControl) {
- for(Goal.Flag flag : goal.getFlags()) {
+ // Paper start
+ long flagIterator = goal.getFlags().getBackingSet();
+ int wrappedGoalSize = goal.getFlags().size();
+ for (int i = 0; i < wrappedGoalSize; ++i) {
+ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
+ flagIterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(flagIterator);
+ // Paper end
if (!goalsByControl.getOrDefault(flag, NO_GOAL).canBeReplacedBy(goal)) {
return false;
}
@@ -98,7 +106,7 @@ public class GoalSelector {
profilerFiller.push("goalCleanup");
for(WrappedGoal wrappedGoal : this.availableGoals) {
- if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.disabledFlags) || !wrappedGoal.canContinueToUse())) {
+ if (wrappedGoal.isRunning() && (goalContainsAnyFlags(wrappedGoal, this.goalTypes) || !wrappedGoal.canContinueToUse())) {
wrappedGoal.stop();
}
}
@@ -116,8 +124,14 @@ public class GoalSelector {
profilerFiller.push("goalUpdate");
for(WrappedGoal wrappedGoal2 : this.availableGoals) {
- if (!wrappedGoal2.isRunning() && !goalContainsAnyFlags(wrappedGoal2, this.disabledFlags) && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) && wrappedGoal2.canUse()) {
- for(Goal.Flag flag : wrappedGoal2.getFlags()) {
+ // Paper start
+ if (!wrappedGoal2.isRunning() && !goalContainsAnyFlags(wrappedGoal2, this.goalTypes) && goalCanBeReplacedForAllFlags(wrappedGoal2, this.lockedFlags) && wrappedGoal2.canUse()) {
+ long flagIterator = wrappedGoal2.getFlags().getBackingSet();
+ int wrappedGoalSize = wrappedGoal2.getFlags().size();
+ for (int i = 0; i < wrappedGoalSize; ++i) {
+ final Goal.Flag flag = GOAL_FLAG_VALUES[Long.numberOfTrailingZeros(flagIterator)];
+ flagIterator ^= io.papermc.paper.util.IntegerUtil.getTrailingBit(flagIterator);
+ // Paper end
WrappedGoal wrappedGoal3 = this.lockedFlags.getOrDefault(flag, NO_GOAL);
wrappedGoal3.stop();
this.lockedFlags.put(flag, wrappedGoal2);
@@ -157,11 +171,11 @@ public class GoalSelector {
}
public void disableControlFlag(Goal.Flag control) {
- this.disabledFlags.add(control);
+ this.goalTypes.addUnchecked(control); // Paper - remove streams from pathfindergoalselector
}
public void enableControlFlag(Goal.Flag control) {
- this.disabledFlags.remove(control);
+ this.goalTypes.removeUnchecked(control); // Paper - remove streams from pathfindergoalselector
}
public void setControlFlag(Goal.Flag control, boolean enabled) {
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
index 6665ce5f48316e626907e6937d5ef1bc398a7ebd..51deb4455cac055ffa455e4f34aa30858d2fb448 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/WrappedGoal.java
@@ -69,8 +69,10 @@ public class WrappedGoal extends Goal {
}
@Override
- public EnumSet<Goal.Flag> getFlags() {
+ // Paper start - remove streams from pathfindergoalselector
+ public com.destroystokyo.paper.util.set.OptimizedSmallEnumSet<Goal.Flag> getFlags() {
return this.goal.getFlags();
+ // Paper end - remove streams from pathfindergoalselector
}
public boolean isRunning() {

View file

@ -1,548 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 12 Apr 2020 15:50:48 -0400
Subject: [PATCH] Improved Watchdog Support
Forced Watchdog Crash support and Improve Async Shutdown
If the request to shut down the server is received while we are in
a watchdog hang, immediately treat it as a crash and begin the shutdown
process. Shutdown process is now improved to also shutdown cleanly when
not using restart scripts either.
If a server is deadlocked, a server owner can send SIGUP (or any other signal
the JVM understands to shut down as it currently does) and the watchdog
will no longer need to wait until the full timeout, allowing you to trigger
a close process and try to shut the server down gracefully, saving player and
world data.
Previously there was no way to trigger this outside of waiting for a full watchdog
timeout, which may be set to a really long time...
Additionally, fix everything to do with shutting the server down asynchronously.
Previously, nearly everything about the process was fragile and unsafe. Main might
not have actually been frozen, and might still be manipulating state.
Or, some reuest might ask main to do something in the shutdown but main is dead.
Or worse, other things might start closing down items such as the Console or Thread Pool
before we are fully shutdown.
This change tries to resolve all of these issues by moving everything into the stop
method and guaranteeing only one thread is stopping the server.
We then issue Thread Death to the main thread of another thread initiates the stop process.
We have to ensure Thread Death propagates correctly though to stop main completely.
This is to ensure that if main isn't truely stuck, it's not manipulating state we are trying to save.
This also moves all plugins who register "delayed init" tasks to occur just before "Done" so they
are properly accounted for and wont trip watchdog on init.
diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java
index 6aaed8e8bf8c721fc834da5c76ac72a4c3e92458..4b002e8b75d117b726b0de274a76d3596fce015b 100644
--- a/src/main/java/com/destroystokyo/paper/Metrics.java
+++ b/src/main/java/com/destroystokyo/paper/Metrics.java
@@ -92,7 +92,12 @@ public class Metrics {
* Starts the Scheduler which submits our data every 30 minutes.
*/
private void startSubmitting() {
- final Runnable submitTask = this::submitData;
+ final Runnable submitTask = () -> {
+ if (MinecraftServer.getServer().hasStopped()) {
+ return;
+ }
+ submitData();
+ };
// Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution of requests on the
// bStats backend. To circumvent this problem, we introduce some randomness into the initial and second delay.
diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java
index d7995cadda8f48bbf642114935311180d3ebde5b..226cba0c1eeedd9e80acd603c46b802c183db1fa 100644
--- a/src/main/java/net/minecraft/CrashReport.java
+++ b/src/main/java/net/minecraft/CrashReport.java
@@ -230,6 +230,7 @@ public class CrashReport {
}
public static CrashReport forThrowable(Throwable cause, String title) {
+ if (cause instanceof ThreadDeath) com.destroystokyo.paper.util.SneakyThrow.sneaky(cause); // Paper
while (cause instanceof CompletionException && cause.getCause() != null) {
cause = cause.getCause();
}
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index f700e677d75f911b786a22b4cc9d0a016d99b02f..5f8cf962a4b630223fb93ffea49d1b2151e1604d 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -291,7 +291,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
public int autosavePeriod;
public Commands vanillaCommandDispatcher;
- private boolean forceTicks;
+ public boolean forceTicks; // Paper
// CraftBukkit end
// Spigot start
public static final int TPS = 20;
@@ -302,6 +302,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public final io.papermc.paper.configuration.PaperConfigurations paperConfigurations;
public static long currentTickLong = 0L; // Paper
+ public volatile Thread shutdownThread; // Paper
+ public volatile boolean abnormalExit = false; // Paper
+
public static <S extends MinecraftServer> S spin(Function<Thread, S> serverFactory) {
AtomicReference<S> atomicreference = new AtomicReference();
Thread thread = new io.papermc.paper.util.TickThread(() -> { // Paper - rewrite chunk system
@@ -868,6 +871,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// CraftBukkit start
private boolean hasStopped = false;
+ public volatile boolean hasFullyShutdown = false; // Paper
private final Object stopLock = new Object();
public final boolean hasStopped() {
synchronized (this.stopLock) {
@@ -882,6 +886,19 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (this.hasStopped) return;
this.hasStopped = true;
}
+ // Paper start - kill main thread, and kill it hard
+ shutdownThread = Thread.currentThread();
+ org.spigotmc.WatchdogThread.doStop(); // Paper
+ if (!isSameThread()) {
+ MinecraftServer.LOGGER.info("Stopping main thread (Ignore any thread death message you see! - DO NOT REPORT THREAD DEATH TO PAPER)");
+ while (this.getRunningThread().isAlive()) {
+ this.getRunningThread().stop();
+ try {
+ Thread.sleep(1);
+ } catch (InterruptedException e) {}
+ }
+ }
+ // Paper end
// CraftBukkit end
if (this.metricsRecorder.isRecording()) {
this.cancelRecordingMetrics();
@@ -935,7 +952,20 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.getProfileCache().save(false); // Paper
}
// Spigot end
+ // Paper start - move final shutdown items here
+ LOGGER.info("Flushing Chunk IO");
+ // Paper end - move final shutdown items here
io.papermc.paper.chunk.system.io.RegionFileIOThread.close(true); // Paper // Paper - rewrite chunk system
+ // Paper start - move final shutdown items here
+ LOGGER.info("Closing Thread Pool");
+ Util.shutdownExecutors(); // Paper
+ LOGGER.info("Closing Server");
+ try {
+ net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
+ } catch (Exception e) {
+ }
+ this.onServerExit();
+ // Paper end - move final shutdown items here
}
public String getLocalIp() {
@@ -1030,6 +1060,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
protected void runServer() {
try {
+ long serverStartTime = Util.getNanos(); // Paper
if (!this.initServer()) {
throw new IllegalStateException("Failed to initialize server");
}
@@ -1039,6 +1070,18 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.status = this.buildServerStatus();
// Spigot start
+ // Paper start - move done tracking
+ LOGGER.info("Running delayed init tasks");
+ this.server.getScheduler().mainThreadHeartbeat(this.tickCount); // run all 1 tick delay tasks during init,
+ // this is going to be the first thing the tick process does anyways, so move done and run it after
+ // everything is init before watchdog tick.
+ // anything at 3+ won't be caught here but also will trip watchdog....
+ // tasks are default scheduled at -1 + delay, and first tick will tick at 1
+ String doneTime = String.format(java.util.Locale.ROOT, "%.3fs", (double) (Util.getNanos() - serverStartTime) / 1.0E9D);
+ LOGGER.info("Done ({})! For help, type \"help\"", doneTime);
+ // Paper end
+
+ org.spigotmc.WatchdogThread.tick(); // Paper
org.spigotmc.WatchdogThread.hasStarted = true; // Paper
Arrays.fill( this.recentTps, 20 );
long start = System.nanoTime(), curTime, tickSection = start; // Paper - Further improve server tick loop
@@ -1099,6 +1142,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
JvmProfiler.INSTANCE.onServerTick(this.averageTickTime);
}
} catch (Throwable throwable) {
+ // Paper start
+ if (throwable instanceof ThreadDeath) {
+ MinecraftServer.LOGGER.error("Main thread terminated by WatchDog due to hard crash", throwable);
+ return;
+ }
+ // Paper end
MinecraftServer.LOGGER.error("Encountered an unexpected exception", throwable);
// Spigot Start
if ( throwable.getCause() != null )
@@ -1129,14 +1178,14 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.services.profileCache().clearExecutor();
}
- org.spigotmc.WatchdogThread.doStop(); // Spigot
+ //org.spigotmc.WatchdogThread.doStop(); // Spigot // Paper - move into stop
// CraftBukkit start - Restore terminal to original settings
try {
- net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
+ //net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Move into stop
} catch (Exception ignored) {
}
// CraftBukkit end
- this.onServerExit();
+ //this.onServerExit(); // Paper - moved into stop
}
}
@@ -1205,6 +1254,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@Override
public TickTask wrapRunnable(Runnable runnable) {
+ // Paper start - anything that does try to post to main during watchdog crash, run on watchdog
+ if (this.hasStopped && Thread.currentThread().equals(shutdownThread)) {
+ runnable.run();
+ runnable = () -> {};
+ }
+ // Paper end
return new TickTask(this.tickCount, runnable);
}
@@ -1445,6 +1500,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
try {
crashreport = CrashReport.forThrowable(throwable, "Exception ticking world");
} catch (Throwable t) {
+ if (throwable instanceof ThreadDeath) { throw (ThreadDeath)throwable; } // Paper
throw new RuntimeException("Error generating crash report", t);
}
// Spigot End
@@ -1958,7 +2014,15 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.worldData.setDataConfiguration(worlddataconfiguration);
this.resources.managers.updateRegistryTags(this.registryAccess());
- this.getPlayerList().saveAll();
+ // Paper start
+ if (Thread.currentThread() != this.serverThread) {
+ return;
+ }
+ // this.getPlayerList().saveAll(); // Paper - we don't need to save everything, just advancements
+ for (ServerPlayer player : this.getPlayerList().getPlayers()) {
+ player.getAdvancements().save();
+ }
+ // Paper end
this.getPlayerList().reloadResources();
this.functionManager.replaceLibrary(this.resources.managers.getFunctionLibrary());
this.structureTemplateManager.onResourceManagerReload(this.resources.resourceManager);
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index 307f3ab69186c0980f33d664bb2db8d1aae228f8..fce3edc97c7df45389bb3754f873a314d42929e4 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -269,7 +269,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
long j = Util.getNanos() - i;
String s = String.format(Locale.ROOT, "%.3fs", (double) j / 1.0E9D);
- DedicatedServer.LOGGER.info("Done ({})! For help, type \"help\"", s);
+ //DedicatedServer.LOGGER.info("Done ({})! For help, type \"help\"", s); // Paper moved to after init
if (dedicatedserverproperties.announcePlayerAchievements != null) {
((GameRules.BooleanValue) this.getGameRules().getRule(GameRules.RULE_ANNOUNCE_ADVANCEMENTS)).set(dedicatedserverproperties.announcePlayerAchievements, this);
}
@@ -397,7 +397,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
// this.remoteStatusListener.stop(); // Paper - don't wait for remote connections
}
- System.exit(0); // CraftBukkit
+ hasFullyShutdown = true; // Paper
+ System.exit(this.abnormalExit ? 70 : 0); // CraftBukkit // Paper
}
@Override
@@ -774,7 +775,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@Override
public void stopServer() {
super.stopServer();
- Util.shutdownExecutors();
+ //Util.shutdownExecutors(); // Paper - moved into super
SkullBlockEntity.clear();
}
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index f894d6549e4abf1001de68c8bc3ea0129e973e18..0267886463facdfda43b2dde7efc62001a9adf97 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -534,7 +534,7 @@ public abstract class PlayerList {
this.cserver.getPluginManager().callEvent(playerQuitEvent);
entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage());
- entityplayer.doTick(); // SPIGOT-924
+ if (server.isSameThread()) entityplayer.doTick(); // SPIGOT-924 // Paper - don't tick during emergency shutdowns (Watchdog)
// CraftBukkit end
// Paper start - Remove from collideRule team if needed
diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
index f5829ae484d93b547a5437b85a9621346384a11b..83701fbfaa56a232593ee8f11a3afb8941238bfa 100644
--- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
+++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java
@@ -152,6 +152,7 @@ public abstract class BlockableEventLoop<R extends Runnable> implements Profiler
try {
task.run();
} catch (Exception var3) {
+ if (var3.getCause() instanceof ThreadDeath) throw var3; // Paper
LOGGER.error(LogUtils.FATAL_MARKER, "Error executing task on {}", this.name(), var3);
}
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 4b0a7ef056ff7dd687d5135ac748ee57d556121e..046fa6e5b2013c0872176592dd8cc6681a9b308a 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -904,6 +904,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
try {
tickConsumer.accept(entity);
} catch (Throwable throwable) {
+ if (throwable instanceof ThreadDeath) throw throwable; // Paper
// Paper start - Prevent tile entity and entity crashes
final String msg = String.format("Entity threw exception at %s:%s,%s,%s", entity.level().getWorld().getName(), entity.getX(), entity.getY(), entity.getZ());
MinecraftServer.LOGGER.error(msg, throwable);
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
index 12834995b2bab9e82a40feb01f038532961296cf..12af77215bfd6df3b6802a567ac3c013a4cdf06a 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -1177,6 +1177,7 @@ public class LevelChunk extends ChunkAccess {
gameprofilerfiller.pop();
} catch (Throwable throwable) {
+ if (throwable instanceof ThreadDeath) throw throwable; // Paper
// Paper start - Prevent tile entity and entity crashes
final String msg = String.format("BlockEntity threw exception at %s:%s,%s,%s", LevelChunk.this.getLevel().getWorld().getName(), this.getPos().getX(), this.getPos().getY(), this.getPos().getZ());
net.minecraft.server.MinecraftServer.LOGGER.error(msg, throwable);
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
index a288b405052716ba13adefe43ef2006c8bf6242d..bec85212b48f8ae0c89aef01c3cd245ae0c282fc 100644
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -179,6 +179,36 @@ public class Main {
OptionSet options = null;
+ // Paper start - preload logger classes to avoid plugins mixing versions
+ tryPreloadClass("org.apache.logging.log4j.core.Core");
+ tryPreloadClass("org.apache.logging.log4j.core.appender.AsyncAppender");
+ tryPreloadClass("org.apache.logging.log4j.core.Appender");
+ tryPreloadClass("org.apache.logging.log4j.core.ContextDataInjector");
+ tryPreloadClass("org.apache.logging.log4j.core.Filter");
+ tryPreloadClass("org.apache.logging.log4j.core.ErrorHandler");
+ tryPreloadClass("org.apache.logging.log4j.core.LogEvent");
+ tryPreloadClass("org.apache.logging.log4j.core.Logger");
+ tryPreloadClass("org.apache.logging.log4j.core.LoggerContext");
+ tryPreloadClass("org.apache.logging.log4j.core.LogEventListener");
+ tryPreloadClass("org.apache.logging.log4j.core.AbstractLogEvent");
+ tryPreloadClass("org.apache.logging.log4j.message.AsynchronouslyFormattable");
+ tryPreloadClass("org.apache.logging.log4j.message.FormattedMessage");
+ tryPreloadClass("org.apache.logging.log4j.message.ParameterizedMessage");
+ tryPreloadClass("org.apache.logging.log4j.message.Message");
+ tryPreloadClass("org.apache.logging.log4j.message.MessageFactory");
+ tryPreloadClass("org.apache.logging.log4j.message.TimestampMessage");
+ tryPreloadClass("org.apache.logging.log4j.message.SimpleMessage");
+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncLogger");
+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncLoggerContext");
+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncQueueFullPolicy");
+ tryPreloadClass("org.apache.logging.log4j.core.async.AsyncLoggerDisruptor");
+ tryPreloadClass("org.apache.logging.log4j.core.async.RingBufferLogEvent");
+ tryPreloadClass("org.apache.logging.log4j.core.async.DisruptorUtil");
+ tryPreloadClass("org.apache.logging.log4j.core.async.RingBufferLogEventHandler");
+ tryPreloadClass("org.apache.logging.log4j.core.impl.ThrowableProxy");
+ tryPreloadClass("org.apache.logging.log4j.core.impl.ExtendedClassInfo");
+ tryPreloadClass("org.apache.logging.log4j.core.impl.ExtendedStackTraceElement");
+ // Paper end
try {
options = parser.parse(args);
} catch (joptsimple.OptionException ex) {
@@ -289,8 +319,64 @@ public class Main {
} catch (Throwable t) {
t.printStackTrace();
}
+ // Paper start
+ // load some required classes to avoid errors during shutdown if jar is replaced
+ // also to guarantee our version loads over plugins
+ tryPreloadClass("com.destroystokyo.paper.util.SneakyThrow");
+ tryPreloadClass("com.google.common.collect.Iterators$PeekingImpl");
+ tryPreloadClass("com.google.common.collect.MapMakerInternalMap$Values");
+ tryPreloadClass("com.google.common.collect.MapMakerInternalMap$ValueIterator");
+ tryPreloadClass("com.google.common.collect.MapMakerInternalMap$WriteThroughEntry");
+ tryPreloadClass("com.google.common.collect.Iterables");
+ for (int i = 1; i <= 15; i++) {
+ tryPreloadClass("com.google.common.collect.Iterables$" + i, false);
+ }
+ tryPreloadClass("org.apache.commons.lang3.mutable.MutableBoolean");
+ tryPreloadClass("org.apache.commons.lang3.mutable.MutableInt");
+ tryPreloadClass("org.jline.terminal.impl.MouseSupport");
+ tryPreloadClass("org.jline.terminal.impl.MouseSupport$1");
+ tryPreloadClass("org.jline.terminal.Terminal$MouseTracking");
+ tryPreloadClass("co.aikar.timings.TimingHistory");
+ tryPreloadClass("co.aikar.timings.TimingHistory$MinuteReport");
+ tryPreloadClass("io.netty.channel.AbstractChannelHandlerContext");
+ tryPreloadClass("io.netty.channel.AbstractChannelHandlerContext$11");
+ tryPreloadClass("io.netty.channel.AbstractChannelHandlerContext$12");
+ tryPreloadClass("io.netty.channel.AbstractChannel$AbstractUnsafe$8");
+ tryPreloadClass("io.netty.util.concurrent.DefaultPromise");
+ tryPreloadClass("io.netty.util.concurrent.DefaultPromise$1");
+ tryPreloadClass("io.netty.util.internal.PromiseNotificationUtil");
+ tryPreloadClass("io.netty.util.internal.SystemPropertyUtil");
+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler");
+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$1");
+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$2");
+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$3");
+ tryPreloadClass("org.bukkit.craftbukkit.scheduler.CraftScheduler$4");
+ tryPreloadClass("org.slf4j.helpers.MessageFormatter");
+ tryPreloadClass("org.slf4j.helpers.FormattingTuple");
+ tryPreloadClass("org.slf4j.helpers.BasicMarker");
+ tryPreloadClass("org.slf4j.helpers.Util");
+ tryPreloadClass("com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent");
+ tryPreloadClass("com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent");
+ // Minecraft, seen during saving
+ tryPreloadClass(net.minecraft.world.level.lighting.LayerLightEventListener.DummyLightLayerEventListener.class.getName());
+ tryPreloadClass(net.minecraft.world.level.lighting.LayerLightEventListener.class.getName());
+ tryPreloadClass(net.minecraft.util.ExceptionCollector.class.getName());
+ // Paper end
+ }
+ }
+
+ // Paper start
+ private static void tryPreloadClass(String className) {
+ tryPreloadClass(className, true);
+ }
+ private static void tryPreloadClass(String className, boolean printError) {
+ try {
+ Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ if (printError) System.err.println("An expected class " + className + " was not found for preloading: " + e.getMessage());
}
}
+ // Paper end
private static List<String> asList(String... params) {
return Arrays.asList(params);
diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
index c6e8441e299f477ddb22c1ce2618710763978f1a..e8e93538dfd71de86515d9405f728db1631e949a 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
@@ -12,11 +12,27 @@ public class ServerShutdownThread extends Thread {
@Override
public void run() {
try {
+ // Paper start - try to shutdown on main
+ server.safeShutdown(false, false);
+ for (int i = 1000; i > 0 && !server.hasStopped(); i -= 100) {
+ Thread.sleep(100);
+ }
+ if (server.hasStopped()) {
+ while (!server.hasFullyShutdown) Thread.sleep(1000);
+ return;
+ }
+ // Looks stalled, close async
org.spigotmc.AsyncCatcher.enabled = false; // Spigot
+ server.forceTicks = true;
this.server.close();
+ while (!server.hasFullyShutdown) Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ // Paper end
} finally {
+ org.apache.logging.log4j.LogManager.shutdown(); // Paper
try {
- net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
+ //net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Move into stop
} catch (Exception e) {
}
}
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
index 051b9e3a5d29a5840d596468e3ddd013bedc8da3..631457374fda492776f4af0515cee2e43a934d2c 100644
--- a/src/main/java/org/spigotmc/RestartCommand.java
+++ b/src/main/java/org/spigotmc/RestartCommand.java
@@ -138,7 +138,7 @@ public class RestartCommand extends Command
// Paper end
// Paper start - copied from above and modified to return if the hook registered
- private static boolean addShutdownHook(String restartScript)
+ public static boolean addShutdownHook(String restartScript)
{
String[] split = restartScript.split( " " );
if ( split.length > 0 && new File( split[0] ).isFile() )
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
index 230d55820778e84c1c8aa2b013ae0e5e35568ea1..5ca863aa1859922fa359eba32539229db40e5b98 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -11,6 +11,7 @@ import org.bukkit.Bukkit;
public final class WatchdogThread extends io.papermc.paper.util.TickThread // Paper - rewrite chunk system
{
+ public static final boolean DISABLE_WATCHDOG = Boolean.getBoolean("disable.watchdog"); // Paper
private static WatchdogThread instance;
private long timeoutTime;
private boolean restart;
@@ -39,6 +40,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
{
if ( WatchdogThread.instance == null )
{
+ if (timeoutTime <= 0) timeoutTime = 300; // Paper
WatchdogThread.instance = new WatchdogThread( timeoutTime * 1000L, restart );
WatchdogThread.instance.start();
} else
@@ -70,12 +72,13 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
// Paper start
Logger log = Bukkit.getServer().getLogger();
long currentTime = WatchdogThread.monotonicMillis();
- if ( this.lastTick != 0 && this.timeoutTime > 0 && currentTime > this.lastTick + this.earlyWarningEvery && !Boolean.getBoolean("disable.watchdog")) // Paper - Add property to disable
+ MinecraftServer server = MinecraftServer.getServer();
+ if ( this.lastTick != 0 && this.timeoutTime > 0 && WatchdogThread.hasStarted && (!server.isRunning() || (currentTime > this.lastTick + this.earlyWarningEvery && !DISABLE_WATCHDOG) )) // Paper - add property to disable
{
- boolean isLongTimeout = currentTime > lastTick + timeoutTime;
+ boolean isLongTimeout = currentTime > lastTick + timeoutTime || (!server.isRunning() && !server.hasStopped() && currentTime > lastTick + 1000);
// Don't spam early warning dumps
if ( !isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay)) continue;
- if ( !isLongTimeout && MinecraftServer.getServer().hasStopped()) continue; // Don't spam early watchdog warnings during shutdown, we'll come back to this...
+ if ( !isLongTimeout && server.hasStopped()) continue; // Don't spam early watchdog warnings during shutdown, we'll come back to this...
lastEarlyWarning = currentTime;
if (isLongTimeout) {
// Paper end
@@ -137,9 +140,24 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
if ( isLongTimeout )
{
- if ( this.restart && !MinecraftServer.getServer().hasStopped() )
+ if ( !server.hasStopped() )
{
- RestartCommand.restart();
+ AsyncCatcher.enabled = false; // Disable async catcher incase it interferes with us
+ server.forceTicks = true;
+ if (restart) {
+ RestartCommand.addShutdownHook( SpigotConfig.restartScript );
+ }
+ // try one last chance to safe shutdown on main incase it 'comes back'
+ server.abnormalExit = true;
+ server.safeShutdown(false, restart);
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (!server.hasStopped()) {
+ server.close();
+ }
}
break;
} // Paper end
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
index ea4e2161c0bd43884055cc6b8d70b2139f70e720..266b4e6fb3988b5848021c83fdc68e342c70b188 100644
--- a/src/main/resources/log4j2.xml
+++ b/src/main/resources/log4j2.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<Configuration status="WARN" packages="com.mojang.util">
+<Configuration status="WARN" packages="com.mojang.util" shutdownHook="disable">
<Appenders>
<Queue name="ServerGuiConsole">
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />

View file

@ -1,43 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 3 Mar 2016 02:02:07 -0600
Subject: [PATCH] Optimize Pathfinding
Prevents pathfinding from spamming failures for things such as
arrow attacks.
diff --git a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
index 68edd488087a6ec1e65797cfbd4118bd0efbab50..b37415d45dda8e658c8995a4519e552dc378bb41 100644
--- a/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
+++ b/src/main/java/net/minecraft/world/entity/ai/navigation/PathNavigation.java
@@ -192,9 +192,29 @@ public abstract class PathNavigation {
return this.moveTo(this.createPath(x, y, z, 1), speed);
}
+ // Paper start - optimise pathfinding
+ private int lastFailure = 0;
+ private int pathfindFailures = 0;
+ // Paper end
+
public boolean moveTo(Entity entity, double speed) {
+ // Paper start - Pathfinding optimizations
+ if (this.pathfindFailures > 10 && this.path == null && net.minecraft.server.MinecraftServer.currentTick < this.lastFailure + 40) {
+ return false;
+ }
+ // Paper end
Path path = this.createPath(entity, 1);
- return path != null && this.moveTo(path, speed);
+ // Paper start - Pathfinding optimizations
+ if (path != null && this.moveTo(path, speed)) {
+ this.lastFailure = 0;
+ this.pathfindFailures = 0;
+ return true;
+ } else {
+ this.pathfindFailures++;
+ this.lastFailure = net.minecraft.server.MinecraftServer.currentTick;
+ return false;
+ }
+ // Paper end
}
public boolean moveTo(@Nullable Path path, double speed) {

View file

@ -1,48 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 6 Apr 2020 18:35:09 -0700
Subject: [PATCH] Reduce Either Optional allocation
In order to get chunk values, we shouldn't need to create
an optional each time.
diff --git a/src/main/java/com/mojang/datafixers/util/Either.java b/src/main/java/com/mojang/datafixers/util/Either.java
index de524d485fada3c3cca8c2fe6c63db0e0b33dad8..6eb0c94965a6e96ec8ae112125e98c6c4809805b 100644
--- a/src/main/java/com/mojang/datafixers/util/Either.java
+++ b/src/main/java/com/mojang/datafixers/util/Either.java
@@ -22,7 +22,7 @@ public abstract class Either<L, R> implements App<Either.Mu<R>, L> {
}
private static final class Left<L, R> extends Either<L, R> {
- private final L value;
+ private final L value; private Optional<L> valueOptional; // Paper - reduce the optional allocation...
public Left(final L value) {
this.value = value;
@@ -51,7 +51,7 @@ public abstract class Either<L, R> implements App<Either.Mu<R>, L> {
@Override
public Optional<L> left() {
- return Optional.of(value);
+ return this.valueOptional == null ? this.valueOptional = Optional.of(this.value) : this.valueOptional; // Paper - reduce the optional allocation...
}
@Override
@@ -83,7 +83,7 @@ public abstract class Either<L, R> implements App<Either.Mu<R>, L> {
}
private static final class Right<L, R> extends Either<L, R> {
- private final R value;
+ private final R value; private Optional<R> valueOptional; // Paper - reduce the optional allocation...
public Right(final R value) {
this.value = value;
@@ -117,7 +117,7 @@ public abstract class Either<L, R> implements App<Either.Mu<R>, L> {
@Override
public Optional<R> right() {
- return Optional.of(value);
+ return this.valueOptional == null ? this.valueOptional = Optional.of(this.value) : this.valueOptional; // Paper - reduce the optional allocation...
}
@Override

View file

@ -1,50 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 6 Apr 2020 17:39:25 -0700
Subject: [PATCH] Reduce memory footprint of NBTTagCompound
Fastutil maps are going to have a lower memory footprint - which
is important because we clone chunk data after reading it for safety.
So, reduce the impact of the clone on GC.
diff --git a/src/main/java/net/minecraft/nbt/CompoundTag.java b/src/main/java/net/minecraft/nbt/CompoundTag.java
index 92095b494a0c4fb89b84f0b1c0376615d28e34ce..ad0251b73d21b36bf19e9aa649817b4da2d0a6b4 100644
--- a/src/main/java/net/minecraft/nbt/CompoundTag.java
+++ b/src/main/java/net/minecraft/nbt/CompoundTag.java
@@ -46,7 +46,7 @@ public class CompoundTag implements Tag {
private static CompoundTag loadCompound(DataInput input, NbtAccounter tracker) throws IOException {
tracker.accountBytes(48L);
- Map<String, Tag> map = Maps.newHashMap();
+ it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<String, Tag> map = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f); // Paper - reduce memory footprint of NBTTagCompound
byte b;
while((b = input.readByte()) != 0) {
@@ -167,7 +167,7 @@ public class CompoundTag implements Tag {
}
public CompoundTag() {
- this(Maps.newHashMap());
+ this(new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(8, 0.8f)); // Paper - reduce memory footprint of NBTTagCompound
}
@Override
@@ -486,8 +486,16 @@ public class CompoundTag implements Tag {
@Override
public CompoundTag copy() {
- Map<String, Tag> map = Maps.newHashMap(Maps.transformValues(this.tags, Tag::copy));
- return new CompoundTag(map);
+ // Paper start - reduce memory footprint of NBTTagCompound
+ it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<String, Tag> ret = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap<>(this.tags.size(), 0.8f);
+ java.util.Iterator<java.util.Map.Entry<String, Tag>> iterator = (this.tags instanceof it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap) ? ((it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap)this.tags).object2ObjectEntrySet().fastIterator() : this.tags.entrySet().iterator();
+ while (iterator.hasNext()) {
+ Map.Entry<String, Tag> entry = iterator.next();
+ ret.put(entry.getKey(), entry.getValue().copy());
+ }
+
+ return new CompoundTag(ret);
+ // Paper end - reduce memory footprint of NBTTagCompound
}
@Override

View file

@ -1,50 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Mon, 13 Apr 2020 07:31:44 +0100
Subject: [PATCH] Prevent opening inventories when frozen
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index a5dd060ad0b978fddbc193feb8bbab99342b4d56..b5e42c3602f45a5b540dfc54421ab22866f2e4d9 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -694,7 +694,7 @@ public class ServerPlayer extends Player {
containerUpdateDelay = this.level().paperConfig().tickRates.containerUpdate;
}
// Paper end
- if (!this.level().isClientSide && !this.containerMenu.stillValid(this)) {
+ if (!this.level().isClientSide && this.containerMenu != this.inventoryMenu && (this.isImmobile() || !this.containerMenu.stillValid(this))) { // Paper - auto close while frozen
this.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); // Paper
this.containerMenu = this.inventoryMenu;
}
@@ -1543,7 +1543,7 @@ public class ServerPlayer extends Player {
} else {
// CraftBukkit start
this.containerMenu = container;
- this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), container.getTitle()));
+ if (!this.isImmobile()) this.connection.send(new ClientboundOpenScreenPacket(container.containerId, container.getType(), container.getTitle())); // Paper
// CraftBukkit end
this.initMenu(container);
return OptionalInt.of(this.containerCounter);
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
index 607dc510ac856a0bf3a54bf1004bdf98825131e7..6a9ab8bab7eb443f83e939bed17cb6fc9d1f3906 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java
@@ -325,7 +325,7 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
if (adventure$title == null) adventure$title = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(container.getBukkitView().getTitle()); // Paper
//player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, CraftChatMessage.fromString(title)[0])); // Paper - comment
- player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
+ if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
player.containerMenu = container;
player.initMenu(container);
}
@@ -399,7 +399,7 @@ public class CraftHumanEntity extends CraftLivingEntity implements HumanEntity {
net.kyori.adventure.text.Component adventure$title = inventory.title(); // Paper
if (adventure$title == null) adventure$title = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(inventory.getTitle()); // Paper
//player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, CraftChatMessage.fromString(title)[0])); // Paper - comment
- player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
+ if (!player.isImmobile()) player.connection.send(new ClientboundOpenScreenPacket(container.containerId, windowType, io.papermc.paper.adventure.PaperAdventure.asVanilla(adventure$title))); // Paper
player.containerMenu = container;
player.initMenu(container);
}

View file

@ -1,43 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Wed, 15 Apr 2020 17:56:07 -0700
Subject: [PATCH] Don't run entity collision code if not needed
Will not run if:
Max entity cramming is disabled and the max collisions per entity is less than or equal to 0.
Entity#isPushable() returns false, meaning all entities will not be able to collide with this
entity anyways.
The entity's current team collision rule causes them to NEVER collide.
Co-authored-by: Owen1212055 <23108066+Owen1212055@users.noreply.github.com>
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 63e00b3aa1b31c14fc4630fb4b2d3355349bdf24..ad0c8e43d27feb24e614fa15569edab625eff875 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -3396,10 +3396,24 @@ public abstract class LivingEntity extends Entity implements Attackable {
if (this.level().isClientSide()) {
this.level().getEntities(EntityTypeTest.forClass(net.minecraft.world.entity.player.Player.class), this.getBoundingBox(), EntitySelector.pushableBy(this)).forEach(this::doPush);
} else {
+ // Paper start - don't run getEntities if we're not going to use its result
+ if (!this.isPushable()) {
+ return;
+ }
+ net.minecraft.world.scores.Team team = this.getTeam();
+ if (team != null && team.getCollisionRule() == net.minecraft.world.scores.Team.CollisionRule.NEVER) {
+ return;
+ }
+
+ int i = this.level().getGameRules().getInt(GameRules.RULE_MAX_ENTITY_CRAMMING);
+ if (i <= 0 && this.level().paperConfig().collisions.maxEntityCollisions <= 0) {
+ return;
+ }
+ // Paper end - don't run getEntities if we're not going to use its result
List<Entity> list = this.level().getEntities((Entity) this, this.getBoundingBox(), EntitySelector.pushableBy(this));
if (!list.isEmpty()) {
- int i = this.level().getGameRules().getInt(GameRules.RULE_MAX_ENTITY_CRAMMING);
+ // Paper - moved up
if (i > 0 && list.size() > i - 1 && this.random.nextInt(4) == 0) {
int j = 0;

View file

@ -1,194 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MiniDigger <admin@benndorf.dev>
Date: Mon, 20 Jan 2020 21:38:15 +0100
Subject: [PATCH] Implement Player Client Options API
== AT ==
public net.minecraft.world.entity.player.Player DATA_PLAYER_MODE_CUSTOMISATION
diff --git a/src/main/java/com/destroystokyo/paper/PaperSkinParts.java b/src/main/java/com/destroystokyo/paper/PaperSkinParts.java
new file mode 100644
index 0000000000000000000000000000000000000000..b6f4400df3d8ec7e06a996de54f8cabba57885e1
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/PaperSkinParts.java
@@ -0,0 +1,74 @@
+package com.destroystokyo.paper;
+
+import com.google.common.base.Objects;
+
+import java.util.StringJoiner;
+
+public class PaperSkinParts implements SkinParts {
+
+ private final int raw;
+
+ public PaperSkinParts(int raw) {
+ this.raw = raw;
+ }
+
+ public boolean hasCapeEnabled() {
+ return (raw & 1) == 1;
+ }
+
+ public boolean hasJacketEnabled() {
+ return (raw >> 1 & 1) == 1;
+ }
+
+ public boolean hasLeftSleeveEnabled() {
+ return (raw >> 2 & 1) == 1;
+ }
+
+ public boolean hasRightSleeveEnabled() {
+ return (raw >> 3 & 1) == 1;
+ }
+
+ public boolean hasLeftPantsEnabled() {
+ return (raw >> 4 & 1) == 1;
+ }
+
+ public boolean hasRightPantsEnabled() {
+ return (raw >> 5 & 1) == 1;
+ }
+
+ public boolean hasHatsEnabled() {
+ return (raw >> 6 & 1) == 1;
+ }
+
+ @Override
+ public int getRaw() {
+ return raw;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ PaperSkinParts that = (PaperSkinParts) o;
+ return raw == that.raw;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(raw);
+ }
+
+ @Override
+ public String toString() {
+ return new StringJoiner(", ", PaperSkinParts.class.getSimpleName() + "[", "]")
+ .add("raw=" + raw)
+ .add("cape=" + hasCapeEnabled())
+ .add("jacket=" + hasJacketEnabled())
+ .add("leftSleeve=" + hasLeftSleeveEnabled())
+ .add("rightSleeve=" + hasRightSleeveEnabled())
+ .add("leftPants=" + hasLeftPantsEnabled())
+ .add("rightPants=" + hasRightPantsEnabled())
+ .add("hats=" + hasHatsEnabled())
+ .toString();
+ }
+}
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index b5e42c3602f45a5b540dfc54421ab22866f2e4d9..983b006d6db81bbc285fceac23ea4b9f15b2029b 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -379,7 +379,7 @@ public class ServerPlayer extends Player {
this.advancements = server.getPlayerList().getPlayerAdvancements(this);
this.setMaxUpStep(1.0F);
// this.fudgeSpawnLocation(world); // Paper - don't move to spawn on login, only first join
- this.updateOptions(clientOptions);
+ this.updateOptionsNoEvents(clientOptions); // Paper - don't call options events on login
this.cachedSingleHashSet = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets.PooledObjectLinkedOpenHashSet<>(this); // Paper
@@ -1950,7 +1950,23 @@ public class ServerPlayer extends Player {
}
}
+ // Paper start - Client option API
+ private java.util.Map<com.destroystokyo.paper.ClientOption<?>, ?> getClientOptionMap(String locale, int viewDistance, com.destroystokyo.paper.ClientOption.ChatVisibility chatVisibility, boolean chatColors, com.destroystokyo.paper.PaperSkinParts skinParts, org.bukkit.inventory.MainHand mainHand, boolean allowsServerListing, boolean textFilteringEnabled) {
+ java.util.Map<com.destroystokyo.paper.ClientOption<?>, Object> map = new java.util.HashMap<>();
+ map.put(com.destroystokyo.paper.ClientOption.LOCALE, locale);
+ map.put(com.destroystokyo.paper.ClientOption.VIEW_DISTANCE, viewDistance);
+ map.put(com.destroystokyo.paper.ClientOption.CHAT_VISIBILITY, chatVisibility);
+ map.put(com.destroystokyo.paper.ClientOption.CHAT_COLORS_ENABLED, chatColors);
+ map.put(com.destroystokyo.paper.ClientOption.SKIN_PARTS, skinParts);
+ map.put(com.destroystokyo.paper.ClientOption.MAIN_HAND, mainHand);
+ map.put(com.destroystokyo.paper.ClientOption.ALLOW_SERVER_LISTINGS, allowsServerListing);
+ map.put(com.destroystokyo.paper.ClientOption.TEXT_FILTERING_ENABLED, textFilteringEnabled);
+ return map;
+ }
+ // Paper end
+
public void updateOptions(ClientInformation clientOptions) {
+ new com.destroystokyo.paper.event.player.PlayerClientOptionsChangeEvent(getBukkitEntity(), getClientOptionMap(clientOptions.language(), clientOptions.viewDistance(), com.destroystokyo.paper.ClientOption.ChatVisibility.valueOf(clientOptions.chatVisibility().name()), clientOptions.chatColors(), new com.destroystokyo.paper.PaperSkinParts(clientOptions.modelCustomisation()), clientOptions.mainHand() == HumanoidArm.LEFT ? MainHand.LEFT : MainHand.RIGHT, clientOptions.allowsListing(), clientOptions.textFilteringEnabled())).callEvent(); // Paper - settings event
// CraftBukkit start
if (this.getMainArm() != clientOptions.mainHand()) {
PlayerChangedMainHandEvent event = new PlayerChangedMainHandEvent(this.getBukkitEntity(), this.getMainArm() == HumanoidArm.LEFT ? MainHand.LEFT : MainHand.RIGHT);
@@ -1962,6 +1978,11 @@ public class ServerPlayer extends Player {
this.server.server.getPluginManager().callEvent(new com.destroystokyo.paper.event.player.PlayerLocaleChangeEvent(this.getBukkitEntity(), this.language, clientOptions.language())); // Paper
}
// CraftBukkit end
+ // Paper start - don't call options events on login
+ updateOptionsNoEvents(clientOptions);
+ }
+ public void updateOptionsNoEvents(ClientInformation clientOptions) {
+ // Paper end
this.language = clientOptions.language();
this.adventure$locale = net.kyori.adventure.translation.Translator.parseLocale(this.language); // Paper
this.requestedViewDistance = clientOptions.viewDistance();
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 8341d66dd5cad3cebdd95cfbc995cc3e8d43d934..7c40718a53d2ef480441fd053de7c45971efe416 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -604,6 +604,28 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
connection.disconnect(message == null ? net.kyori.adventure.text.Component.empty() : message);
}
}
+
+ @Override
+ public <T> T getClientOption(com.destroystokyo.paper.ClientOption<T> type) {
+ if (com.destroystokyo.paper.ClientOption.SKIN_PARTS == type) {
+ return type.getType().cast(new com.destroystokyo.paper.PaperSkinParts(getHandle().getEntityData().get(net.minecraft.world.entity.player.Player.DATA_PLAYER_MODE_CUSTOMISATION)));
+ } else if (com.destroystokyo.paper.ClientOption.CHAT_COLORS_ENABLED == type) {
+ return type.getType().cast(getHandle().canChatInColor());
+ } else if (com.destroystokyo.paper.ClientOption.CHAT_VISIBILITY == type) {
+ return type.getType().cast(getHandle().getChatVisibility() == null ? com.destroystokyo.paper.ClientOption.ChatVisibility.UNKNOWN : com.destroystokyo.paper.ClientOption.ChatVisibility.valueOf(getHandle().getChatVisibility().name()));
+ } else if (com.destroystokyo.paper.ClientOption.LOCALE == type) {
+ return type.getType().cast(getLocale());
+ } else if (com.destroystokyo.paper.ClientOption.MAIN_HAND == type) {
+ return type.getType().cast(getMainHand());
+ } else if (com.destroystokyo.paper.ClientOption.VIEW_DISTANCE == type) {
+ return type.getType().cast(getClientViewDistance());
+ } else if (com.destroystokyo.paper.ClientOption.ALLOW_SERVER_LISTINGS == type) {
+ return type.getType().cast(getHandle().allowsListing());
+ } else if (com.destroystokyo.paper.ClientOption.TEXT_FILTERING_ENABLED == type) {
+ return type.getType().cast(getHandle().isTextFilteringEnabled());
+ }
+ throw new RuntimeException("Unknown settings type");
+ }
// Paper end
@Override
diff --git a/src/test/java/io/papermc/paper/world/TranslationKeyTest.java b/src/test/java/io/papermc/paper/world/TranslationKeyTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7f8b6462d2a1bbd39a870d2543bebc135f7eb45b
--- /dev/null
+++ b/src/test/java/io/papermc/paper/world/TranslationKeyTest.java
@@ -0,0 +1,18 @@
+package io.papermc.paper.world;
+
+import com.destroystokyo.paper.ClientOption;
+import net.minecraft.world.entity.player.ChatVisiblity;
+import org.bukkit.Difficulty;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class TranslationKeyTest {
+
+ @Test
+ public void testChatVisibilityKeys() {
+ for (ClientOption.ChatVisibility chatVisibility : ClientOption.ChatVisibility.values()) {
+ if (chatVisibility == ClientOption.ChatVisibility.UNKNOWN) continue;
+ Assertions.assertEquals(ChatVisiblity.valueOf(chatVisibility.name()).getKey(), chatVisibility.translationKey(), chatVisibility + "'s translation key doesn't match");
+ }
+ }
+}

View file

@ -1,23 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 18 Apr 2020 15:59:41 -0400
Subject: [PATCH] Don't crash if player is attempted to be removed from
untracked chunk.
I suspect it deals with teleporting as it uses players current x/y/z
diff --git a/src/main/java/net/minecraft/server/level/DistanceManager.java b/src/main/java/net/minecraft/server/level/DistanceManager.java
index 942042863f0105769c15b865d4d98a09a7053a68..68550d4497a5f10bf653482f79be77373df53f27 100644
--- a/src/main/java/net/minecraft/server/level/DistanceManager.java
+++ b/src/main/java/net/minecraft/server/level/DistanceManager.java
@@ -147,8 +147,8 @@ public abstract class DistanceManager {
ObjectSet<ServerPlayer> objectset = (ObjectSet) this.playersPerChunk.get(i);
if (objectset == null) return; // CraftBukkit - SPIGOT-6208
- objectset.remove(player);
- if (objectset.isEmpty()) {
+ if (objectset != null) objectset.remove(player); // Paper - some state corruption happens here, don't crash, clean up gracefully.
+ if (objectset == null || objectset.isEmpty()) { // Paper
this.playersPerChunk.remove(i);
this.naturalSpawnChunkCounter.update(i, Integer.MAX_VALUE, false);
//this.playerTicketManager.update(i, Integer.MAX_VALUE, false); // Paper - no longer used

View file

@ -1,113 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 19 Apr 2020 00:05:46 -0400
Subject: [PATCH] Fix Longstanding Broken behavior of PlayerJoinEvent
For years, plugin developers have had to delay many things they do
inside of the PlayerJoinEvent by 1 tick to make it actually work.
This all boiled down to 1 reason why: The event fired before the
player was fully ready and joined to the world!
Additionally, if that player logged out on a vehicle, the event
fired before the vehicle was even loaded, so that plugins had no
access to the vehicle during this event either.
This change finally fixes this issue, fully preparing the player
into the world as a fully ready entity, vehicle included.
There should be no plugins that break because of this change, but might
improve consistency with other plugins instead.
For example, if 2 plugins listens to this event, and the first one
teleported the player in the event, then the 2nd plugin actually
would be getting a valid player!
This was very non deterministic. This change will ensure every plugin
receives a deterministic result, and should no longer require 1 tick
delays anymore.
== AT ==
public net.minecraft.server.level.ChunkMap addEntity(Lnet/minecraft/world/entity/Entity;)V
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 0e3c89b9d75160d0e8947d042a1568da13d62fcf..8d12a6da0f0679fd14c2a498f9645bd04b2d8ed7 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1000,6 +1000,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
+ ": " + entity + (this.entityMap.containsKey(entity.getId()) ? " ALREADY CONTAINED (This would have crashed your server)" : ""), new Throwable());
return;
}
+ if (entity instanceof ServerPlayer && ((ServerPlayer) entity).supressTrackerForLogin) return; // Delay adding to tracker until after list packets
// Paper end
if (!(entity instanceof EnderDragonPart)) {
EntityType<?> entitytypes = entity.getType();
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 983b006d6db81bbc285fceac23ea4b9f15b2029b..49d341605ac7e82c5dcdcc960b6581aa7e1fb8f3 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -264,6 +264,7 @@ public class ServerPlayer extends Player {
public double maxHealthCache;
public boolean joining = true;
public boolean sentListPacket = false;
+ public boolean supressTrackerForLogin = false; // Paper
public String kickLeaveMessage = null; // SPIGOT-3034: Forward leave message to PlayerQuitEvent
// CraftBukkit end
public boolean isRealPlayer; // Paper
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 0267886463facdfda43b2dde7efc62001a9adf97..f8679d4b52a5948048a91338dc2cac8bfa5ef9e3 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -297,6 +297,12 @@ public abstract class PlayerList {
this.playersByUUID.put(player.getUUID(), player);
// this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(entityplayer))); // CraftBukkit - replaced with loop below
+ // Paper start - correctly register player BEFORE PlayerJoinEvent, so the entity is valid and doesn't require tick delay hacks
+ player.supressTrackerForLogin = true;
+ worldserver1.addNewPlayer(player);
+ this.server.getCustomBossEvents().onPlayerConnect(player); // see commented out section below worldserver.addPlayerJoin(entityplayer);
+ mountSavedVehicle(player, worldserver1, nbttagcompound);
+ // Paper end
// CraftBukkit start
CraftPlayer bukkitPlayer = player.getBukkitEntity();
@@ -335,6 +341,8 @@ public abstract class PlayerList {
player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(entityplayer1)));
}
player.sentListPacket = true;
+ player.supressTrackerForLogin = false; // Paper
+ ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - track entity now
// CraftBukkit end
player.getEntityData().refresh(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn
@@ -357,6 +365,11 @@ public abstract class PlayerList {
playerconnection.send(new ClientboundUpdateMobEffectPacket(player.getId(), mobeffect));
}
+ // Paper start - move vehicle into method so it can be called above - short circuit around that code
+ onPlayerJoinFinish(player, worldserver1, s1);
+ }
+ private void mountSavedVehicle(ServerPlayer player, ServerLevel worldserver1, CompoundTag nbttagcompound) {
+ // Paper end
if (nbttagcompound != null && nbttagcompound.contains("RootVehicle", 10)) {
CompoundTag nbttagcompound1 = nbttagcompound.getCompound("RootVehicle");
// CraftBukkit start
@@ -405,6 +418,10 @@ public abstract class PlayerList {
}
}
+ // Paper start
+ }
+ public void onPlayerJoinFinish(ServerPlayer player, ServerLevel worldserver1, String s1) {
+ // Paper end
player.initInventoryMenu();
// CraftBukkit - Moved from above, added world
// Paper start - Add to collideRule team if needed
@@ -414,6 +431,7 @@ public abstract class PlayerList {
scoreboard.addPlayerToTeam(player.getScoreboardName(), collideRuleTeam);
}
// Paper end
+ // CraftBukkit - Moved from above, added world
PlayerList.LOGGER.info("{}[{}] logged in with entity id {} at ([{}]{}, {}, {})", player.getName().getString(), s1, player.getId(), worldserver1.serverLevelData.getLevelName(), player.getX(), player.getY(), player.getZ());
}

View file

@ -1,122 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: 2277 <38501234+2277@users.noreply.github.com>
Date: Tue, 31 Mar 2020 10:33:55 +0100
Subject: [PATCH] Move player to spawn point if spawn in unloaded world
If the playerdata contains an invalid world (missing, unloaded, invalid,
etc.), spawn the player at the spawn point of the main world.
Co-authored-by: Wyatt Childers <wchilders@nearce.com>
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index f8679d4b52a5948048a91338dc2cac8bfa5ef9e3..ac6c1c1cf0d736c31a4e3666cb9e10ba8bc1c7c5 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -196,7 +196,7 @@ public abstract class PlayerList {
}
CompoundTag nbttagcompound = this.load(player);
- ResourceKey resourcekey;
+ ResourceKey<Level> resourcekey = null; // Paper
// CraftBukkit start - Better rename detection
if (nbttagcompound != null && nbttagcompound.contains("bukkit")) {
CompoundTag bukkit = nbttagcompound.getCompound("bukkit");
@@ -204,15 +204,42 @@ public abstract class PlayerList {
}
// CraftBukkit end
+ // Paper start - move logic in Entity to here, to use bukkit supplied world UUID & reset to main world spawn if no valid world is found
+ boolean invalidPlayerWorld = false;
+ bukkitData: if (nbttagcompound != null) {
+ // The main way for bukkit worlds to store the world is the world UUID despite mojang adding custom worlds
+ final org.bukkit.World bWorld;
+ if (nbttagcompound.contains("WorldUUIDMost") && nbttagcompound.contains("WorldUUIDLeast")) {
+ bWorld = org.bukkit.Bukkit.getServer().getWorld(new UUID(nbttagcompound.getLong("WorldUUIDMost"), nbttagcompound.getLong("WorldUUIDLeast")));
+ } else if (nbttagcompound.contains("world", net.minecraft.nbt.Tag.TAG_STRING)) { // Paper - legacy bukkit world name
+ bWorld = org.bukkit.Bukkit.getServer().getWorld(nbttagcompound.getString("world"));
+ } else {
+ break bukkitData; // if neither of the bukkit data points exist, proceed to the vanilla migration section
+ }
+ if (bWorld != null) {
+ resourcekey = ((CraftWorld) bWorld).getHandle().dimension();
+ } else {
+ resourcekey = Level.OVERWORLD;
+ invalidPlayerWorld = true;
+ }
+ }
+ if (resourcekey == null) { // only run the vanilla logic if we haven't found a world from the bukkit data
+ // Below is the vanilla way of getting the dimension, this is for migration from vanilla servers
+ // Paper end
if (nbttagcompound != null) {
DataResult<ResourceKey<Level>> dataresult = DimensionType.parseLegacy(new Dynamic(NbtOps.INSTANCE, nbttagcompound.get("Dimension"))); // CraftBukkit - decompile error
Logger logger = PlayerList.LOGGER;
Objects.requireNonNull(logger);
- resourcekey = (ResourceKey) dataresult.resultOrPartial(logger::error).orElse(player.serverLevel().dimension()); // CraftBukkit - SPIGOT-7507: If no dimension, fall back to existing dimension loaded from "WorldUUID", which in turn defaults to World.OVERWORLD
+ // Paper start - reset to main world spawn if no valid world is found
+ final Optional<ResourceKey<Level>> result = dataresult.resultOrPartial(logger::error);
+ invalidPlayerWorld = result.isEmpty();
+ resourcekey = result.orElse(Level.OVERWORLD);
+ // Paper end
} else {
- resourcekey = player.serverLevel().dimension(); // CraftBukkit - SPIGOT-7507: If no dimension, fall back to existing dimension loaded from "WorldUUID", which in turn defaults to World.OVERWORLD
+ resourcekey = Level.OVERWORLD; // Paper - revert to vanilla default main world, this isn't an "invalid world" since no player data existed
}
+ } // Paper
ResourceKey<Level> resourcekey1 = resourcekey;
ServerLevel worldserver = this.server.getLevel(resourcekey1);
@@ -221,6 +248,7 @@ public abstract class PlayerList {
if (worldserver == null) {
PlayerList.LOGGER.warn("Unknown respawn dimension {}, defaulting to overworld", resourcekey1);
worldserver1 = this.server.overworld();
+ invalidPlayerWorld = true; // Paper - reset to main world if no world with parsed value is found
} else {
worldserver1 = worldserver;
}
@@ -228,6 +256,10 @@ public abstract class PlayerList {
// Paper start
if (nbttagcompound == null) {
player.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT; // set Player SpawnReason to DEFAULT on first login
+ // Paper start - reset to main world spawn if first spawn or invalid world
+ }
+ if (nbttagcompound == null || invalidPlayerWorld) {
+ // Paper end
player.fudgeSpawnLocation(worldserver1); // only move to spawn on first login, otherwise, stay where you are....
}
// Paper end
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 2f189712675b988a5bbb796abf54740470d836f4..186e9541031eb2daaf479d7d14782e917b3e16e5 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -2312,27 +2312,8 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
// CraftBukkit end
- // CraftBukkit start - Reset world
- if (this instanceof ServerPlayer) {
- Server server = Bukkit.getServer();
- org.bukkit.World bworld = null;
-
- // TODO: Remove World related checks, replaced with WorldUID
- String worldName = nbt.getString("world");
-
- if (nbt.contains("WorldUUIDMost") && nbt.contains("WorldUUIDLeast")) {
- UUID uid = new UUID(nbt.getLong("WorldUUIDMost"), nbt.getLong("WorldUUIDLeast"));
- bworld = server.getWorld(uid);
- } else {
- bworld = server.getWorld(worldName);
- }
-
- if (bworld == null) {
- bworld = ((org.bukkit.craftbukkit.CraftServer) server).getServer().getLevel(Level.OVERWORLD).getWorld();
- }
-
- ((ServerPlayer) this).setLevel(bworld == null ? null : ((CraftWorld) bworld).getHandle());
- }
+ // CraftBukkit start
+ // Paper - move world parsing/loading to PlayerList#placeNewPlayer
this.getBukkitEntity().readBukkitValues(nbt);
if (nbt.contains("Bukkit.invisible")) {
boolean bukkitInvisible = nbt.getBoolean("Bukkit.invisible");

View file

@ -1,28 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: nossr50 <nossr50@gmail.com>
Date: Thu, 26 Mar 2020 19:44:50 -0700
Subject: [PATCH] Add PlayerAttackEntityCooldownResetEvent
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index ad0c8e43d27feb24e614fa15569edab625eff875..5d4bf7390a4b368e9ef8f39da2aa4ff60329c161 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -2193,7 +2193,16 @@ public abstract class LivingEntity extends Entity implements Attackable {
EntityDamageEvent event = CraftEventFactory.handleLivingEntityDamageEvent(this, damagesource, originalDamage, hardHatModifier, blockingModifier, armorModifier, resistanceModifier, magicModifier, absorptionModifier, hardHat, blocking, armor, resistance, magic, absorption);
if (damagesource.getEntity() instanceof net.minecraft.world.entity.player.Player) {
- ((net.minecraft.world.entity.player.Player) damagesource.getEntity()).resetAttackStrengthTicker(); // Moved from EntityHuman in order to make the cooldown reset get called after the damage event is fired
+ // Paper start - PlayerAttackEntityCooldownResetEvent
+ if (damagesource.getEntity() instanceof ServerPlayer) {
+ ServerPlayer player = (ServerPlayer) damagesource.getEntity();
+ if (new com.destroystokyo.paper.event.player.PlayerAttackEntityCooldownResetEvent(player.getBukkitEntity(), this.getBukkitEntity(), player.getAttackStrengthScale(0F)).callEvent()) {
+ player.resetAttackStrengthTicker();
+ }
+ } else {
+ ((net.minecraft.world.entity.player.Player) damagesource.getEntity()).resetAttackStrengthTicker();
+ }
+ // Paper end
}
if (event.isCancelled()) {
return false;

View file

@ -1,28 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 23 Apr 2020 01:36:39 -0400
Subject: [PATCH] Don't fire BlockFade on worldgen threads
Caused a deadlock
diff --git a/src/main/java/net/minecraft/world/level/block/FireBlock.java b/src/main/java/net/minecraft/world/level/block/FireBlock.java
index 9cda3353c033b3fb4e34a8a0e2cc4085fec9cdaa..893ff998afaa47500a03ae55ce45e9862ab1cc18 100644
--- a/src/main/java/net/minecraft/world/level/block/FireBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/FireBlock.java
@@ -101,6 +101,7 @@ public class FireBlock extends BaseFireBlock {
@Override
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) {
// CraftBukkit start
+ if (!(world instanceof ServerLevel)) return this.canSurvive(state, world, pos) ? (BlockState) this.getStateWithAge(world, pos, (Integer) state.getValue(FireBlock.AGE)) : Blocks.AIR.defaultBlockState(); // Paper - don't fire events in world generation
if (!this.canSurvive(state, world, pos)) {
// Suppress during worldgen
if (!(world instanceof Level)) {
@@ -116,7 +117,7 @@ public class FireBlock extends BaseFireBlock {
return blockState.getHandle();
}
}
- return this.getStateWithAge(world, pos, (Integer) state.getValue(FireBlock.AGE));
+ return this.getStateWithAge(world, pos, (Integer) state.getValue(FireBlock.AGE)); // Paper - diff on change, see "don't fire events in world generation"
// CraftBukkit end
}

View file

@ -1,43 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
Date: Sat, 25 Apr 2020 15:13:41 -0500
Subject: [PATCH] Add phantom creative and insomniac controls
diff --git a/src/main/java/net/minecraft/world/entity/EntitySelector.java b/src/main/java/net/minecraft/world/entity/EntitySelector.java
index 668a7c3f36cdbe48e472cb810b27ae4ab39a65d6..d15e62da0307728a7c2be191a27f87da1bb29f49 100644
--- a/src/main/java/net/minecraft/world/entity/EntitySelector.java
+++ b/src/main/java/net/minecraft/world/entity/EntitySelector.java
@@ -27,6 +27,7 @@ public final class EntitySelector {
return !entity.isSpectator();
};
public static final Predicate<Entity> CAN_BE_COLLIDED_WITH = EntitySelector.NO_SPECTATORS.and(Entity::canBeCollidedWith);
+ public static Predicate<Player> IS_INSOMNIAC = (player) -> net.minecraft.util.Mth.clamp(((net.minecraft.server.level.ServerPlayer) player).getStats().getValue(net.minecraft.stats.Stats.CUSTOM.get(net.minecraft.stats.Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE) >= 72000; // Paper
private EntitySelector() {}
// Paper start
diff --git a/src/main/java/net/minecraft/world/entity/monster/Phantom.java b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
index 658393f451e46a93c5665fe3c580aa395ace68d1..a40852acf5d175cc3a06bc17fb021c76f0c11a28 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Phantom.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Phantom.java
@@ -568,6 +568,7 @@ public class Phantom extends FlyingMob implements Enemy {
Player entityhuman = (Player) iterator.next();
if (Phantom.this.canAttack(entityhuman, TargetingConditions.DEFAULT)) {
+ if (!level().paperConfig().entities.behavior.phantomsOnlyAttackInsomniacs || EntitySelector.IS_INSOMNIAC.test(entityhuman)) // Paper
Phantom.this.setTarget(entityhuman, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER, true); // CraftBukkit - reason
return true;
}
diff --git a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
index 94ee9c399f59e0198b4d9bc2a4255e8b821bcd36..b1fc786970b5288a02cc3a46e3fe7784ac566c07 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/PhantomSpawner.java
@@ -49,7 +49,7 @@ public class PhantomSpawner implements CustomSpawner {
while (iterator.hasNext()) {
ServerPlayer entityplayer = (ServerPlayer) iterator.next();
- if (!entityplayer.isSpectator()) {
+ if (!entityplayer.isSpectator() && (!world.paperConfig().entities.behavior.phantomsDoNotSpawnOnCreativePlayers || !entityplayer.isCreative())) { // Paper
BlockPos blockposition = entityplayer.blockPosition();
if (!world.dimensionType().hasSkyLight() || blockposition.getY() >= world.getSeaLevel() && world.canSeeSky(blockposition)) {

View file

@ -1,167 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 25 Apr 2020 06:46:35 -0400
Subject: [PATCH] Fix numerous item duplication issues and teleport issues
This notably fixes the newest "Donkey Dupe", but also fixes a lot
of dupe bugs in general around nether portals and entity world transfer
We also fix item duplication generically by anytime we clone an item
to drop it on the ground, destroy the source item.
This avoid an itemstack ever existing twice in the world state pre
clean up stage.
So even if something NEW comes up, it would be impossible to drop the
same item twice because the source was destroyed.
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 186e9541031eb2daaf479d7d14782e917b3e16e5..8ecb398ca26753e602c2862bf0c35b76f9a413c0 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -2436,11 +2436,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
} else {
// CraftBukkit start - Capture drops for death event
if (this instanceof net.minecraft.world.entity.LivingEntity && !((net.minecraft.world.entity.LivingEntity) this).forceDrops) {
- ((net.minecraft.world.entity.LivingEntity) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(stack));
+ ((net.minecraft.world.entity.LivingEntity) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack)); // Paper - mirror so we can destroy it later
return null;
}
// CraftBukkit end
- ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY() + (double) yOffset, this.getZ(), stack);
+ ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY() + (double) yOffset, this.getZ(), stack.copy()); // Paper - copy so we can destroy original
+ stack.setCount(0); // Paper - destroy this item - if this ever leaks due to game bugs, ensure it doesn't dupe
entityitem.setDefaultPickUpDelay();
// CraftBukkit start
@@ -3242,6 +3243,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
@Nullable
public Entity teleportTo(ServerLevel worldserver, Vec3 location) {
// CraftBukkit end
+ // Paper start - fix bad state entities causing dupes
+ if (!this.isAlive() || !this.valid) {
+ LOGGER.warn("Illegal Entity Teleport " + this + " to " + worldserver + ":" + location, new Throwable());
+ return null;
+ }
+ // Paper end
if (this.level() instanceof ServerLevel && !this.isRemoved()) {
this.level().getProfiler().push("changeDimension");
// CraftBukkit start
@@ -3268,6 +3275,11 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
// CraftBukkit end
this.level().getProfiler().popPush("reloading");
+ // Paper start - Change lead drop timing to prevent dupe
+ if (this instanceof Mob) {
+ ((Mob) this).dropLeash(true, true); // Paper drop lead
+ }
+ // Paper end
Entity entity = this.getType().create(worldserver);
if (entity != null) {
@@ -3281,10 +3293,6 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
// CraftBukkit start - Forward the CraftEntity to the new entity
this.getBukkitEntity().setHandle(entity);
entity.bukkitEntity = this.getBukkitEntity();
-
- if (this instanceof Mob) {
- ((Mob) this).dropLeash(true, false); // Unleash to prevent duping of leads.
- }
// CraftBukkit end
}
@@ -3405,7 +3413,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
public boolean canChangeDimensions() {
- return !this.isPassenger() && !this.isVehicle();
+ return !this.isPassenger() && !this.isVehicle() && isAlive() && valid; // Paper
}
public float getBlockExplosionResistance(Explosion explosion, BlockGetter world, BlockPos pos, BlockState blockState, FluidState fluidState, float max) {
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 5d4bf7390a4b368e9ef8f39da2aa4ff60329c161..109c381adefd61c69768ce1572873b2e629aff0e 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -1708,9 +1708,9 @@ public abstract class LivingEntity extends Entity implements Attackable {
// Paper start
org.bukkit.event.entity.EntityDeathEvent deathEvent = this.dropAllDeathLoot(damageSource);
if (deathEvent == null || !deathEvent.isCancelled()) {
- if (this.deathScore >= 0 && entityliving != null) {
- entityliving.awardKillScore(this, this.deathScore, damageSource);
- }
+ // if (this.deathScore >= 0 && entityliving != null) { // Paper moved to be run earlier in #dropAllDeathLoot before destroying the drop items in CraftEventFactory#callEntityDeathEvent
+ // entityliving.awardKillScore(this, this.deathScore, damageSource);
+ // }
// Paper start - clear equipment if event is not cancelled
if (this instanceof Mob) {
for (EquipmentSlot slot : this.clearedEquipmentSlots) {
@@ -1811,8 +1811,13 @@ public abstract class LivingEntity extends Entity implements Attackable {
this.dropCustomDeathLoot(source, i, flag);
this.clearEquipmentSlots = prev; // Paper
}
- // CraftBukkit start - Call death event
- org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, this.drops); // Paper
+ // CraftBukkit start - Call death event // Paper start - call advancement triggers with correct entity equipment
+ org.bukkit.event.entity.EntityDeathEvent deathEvent = CraftEventFactory.callEntityDeathEvent(this, this.drops, () -> {
+ final LivingEntity entityliving = this.getKillCredit();
+ if (this.deathScore >= 0 && entityliving != null) {
+ entityliving.awardKillScore(this, this.deathScore, source);
+ }
+ }); // Paper end
this.postDeathDropItems(deathEvent); // Paper
this.drops = new ArrayList<>();
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
index 91b9ec5831f439426a853ba9ac7a3f225629b099..e4e734e0f4c43c1687c8e3a8bbe15441e0bd8e76 100644
--- a/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
+++ b/src/main/java/net/minecraft/world/entity/decoration/ArmorStand.java
@@ -637,7 +637,7 @@ public class ArmorStand extends LivingEntity {
for (i = 0; i < this.handItems.size(); ++i) {
itemstack = (ItemStack) this.handItems.get(i);
if (!itemstack.isEmpty()) {
- this.drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); // CraftBukkit - add to drops
+ this.drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack)); // CraftBukkit - add to drops // Paper - mirror so we can destroy it later - though this call site was safe
this.handItems.set(i, ItemStack.EMPTY);
}
}
@@ -645,7 +645,7 @@ public class ArmorStand extends LivingEntity {
for (i = 0; i < this.armorItems.size(); ++i) {
itemstack = (ItemStack) this.armorItems.get(i);
if (!itemstack.isEmpty()) {
- this.drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); // CraftBukkit - add to drops
+ this.drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemstack)); // CraftBukkit - add to drops // Paper - mirror so we can destroy it later - though this call site was safe
this.armorItems.set(i, ItemStack.EMPTY);
}
}
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 96e33c433605ae2e0473b36f03894e030e79cd0d..39d710d98ca5435e7645ab91c109090c42ffc52f 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -896,6 +896,11 @@ public class CraftEventFactory {
}
public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, List<org.bukkit.inventory.ItemStack> drops) {
+ // Paper start
+ return CraftEventFactory.callEntityDeathEvent(victim, drops, com.google.common.util.concurrent.Runnables.doNothing());
+ }
+ public static EntityDeathEvent callEntityDeathEvent(net.minecraft.world.entity.LivingEntity victim, List<org.bukkit.inventory.ItemStack> drops, Runnable lootCheck) {
+ // Paper end
CraftLivingEntity entity = (CraftLivingEntity) victim.getBukkitEntity();
EntityDeathEvent event = new EntityDeathEvent(entity, drops, victim.getExpReward());
populateFields(victim, event); // Paper - make cancellable
@@ -909,11 +914,13 @@ public class CraftEventFactory {
playDeathSound(victim, event);
// Paper end
victim.expToDrop = event.getDroppedExp();
+ lootCheck.run(); // Paper - advancement triggers before destroying items
for (org.bukkit.inventory.ItemStack stack : event.getDrops()) {
if (stack == null || stack.getType() == Material.AIR || stack.getAmount() == 0) continue;
- world.dropItem(entity.getLocation(), stack);
+ world.dropItem(entity.getLocation(), stack); // Paper - note: dropItem already clones due to this being bukkit -> NMS
+ if (stack instanceof CraftItemStack) stack.setAmount(0); // Paper - destroy this item - if this ever leaks due to game bugs, ensure it doesn't dupe, but don't nuke bukkit stacks of manually added items
}
return event;

View file

@ -1,31 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: zbk <zbk@projectsolaris.net>
Date: Sun, 26 Apr 2020 23:49:01 -0400
Subject: [PATCH] Villager Restocks API
== AT ==
public net.minecraft.world.entity.npc.Villager numberOfRestocksToday
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
index 3b765b9f3b449ef8ff9c82967e4e730a090d4e5d..423f6fcaf49252553d2285308633f13e2427b607 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
@@ -87,6 +87,18 @@ public class CraftVillager extends CraftAbstractVillager implements Villager {
this.getHandle().setVillagerXp(experience);
}
+ // Paper start
+ @Override
+ public int getRestocksToday() {
+ return getHandle().numberOfRestocksToday;
+ }
+
+ @Override
+ public void setRestocksToday(int restocksToday) {
+ getHandle().numberOfRestocksToday = restocksToday;
+ }
+ // Paper end
+
@Override
public boolean sleep(Location location) {
Preconditions.checkArgument(location != null, "Location cannot be null");

View file

@ -1,26 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 2 May 2020 03:09:46 -0400
Subject: [PATCH] Validate PickItem Packet and kick for invalid
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 93b1e0b287d195181cb2253b3490ed537efa6b8e..c06ce8822891e7d2a8fa2b649b02555ce6e3373c 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -867,7 +867,14 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
@Override
public void handlePickItem(ServerboundPickItemPacket packet) {
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
- this.player.getInventory().pickSlot(packet.getSlot());
+ // Paper start - validate pick item position
+ if (!(packet.getSlot() >= 0 && packet.getSlot() < this.player.getInventory().items.size())) {
+ ServerGamePacketListenerImpl.LOGGER.warn("{} tried to set an invalid carried item", this.player.getName().getString());
+ this.disconnect("Invalid hotbar selection (Hacking?)");
+ return;
+ }
+ this.player.getInventory().pickSlot(packet.getSlot()); // Paper - Diff above if changed
+ // Paper end
this.player.connection.send(new ClientboundContainerSetSlotPacket(-2, 0, this.player.getInventory().selected, this.player.getInventory().getItem(this.player.getInventory().selected)));
this.player.connection.send(new ClientboundContainerSetSlotPacket(-2, 0, packet.getSlot(), this.player.getInventory().getItem(packet.getSlot())));
this.player.connection.send(new ClientboundSetCarriedItemPacket(this.player.getInventory().selected));

View file

@ -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 83661761abdb31e62ea56af9d60fed393326be50..e7fbdccdd93bef036a7785acb19def334b039f0f 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -586,6 +586,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;

View file

@ -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 0d9c15120148409967027dead617e80769939697..2214a3198c712deaac0f3d3478c85352185761d4 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 {

View file

@ -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 bec85212b48f8ae0c89aef01c3cd245ae0c282fc..334743a8c82bbcb2f09d0919fc9597750a25b8dd 100644
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -26,6 +26,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() {
{
this.acceptsAll(Main.asList("?", "help"), "Show the help");

View file

@ -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 5f8cf962a4b630223fb93ffea49d1b2151e1604d..fdaf757cbbc90841747989add56c13f252f645e5 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -872,6 +872,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) {
@@ -886,6 +887,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
@@ -986,6 +988,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
public void safeShutdown(boolean waitForShutdown, 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 (waitForShutdown) {
diff --git a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
index ed80d9551cd7cc1ec26a5d5fd7bc185b38fddd78..77e19f345bf68d12686a65e669cd597cd92af910 100644
--- a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -131,6 +131,11 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis
this.connection.resumeInboundAfterProtocolChange();
} catch (Exception exception) {
ServerConfigurationPacketListenerImpl.LOGGER.error("Couldn't place player in world", exception);
+ // Paper start
+ if (MinecraftServer.getServer().isDebugging()) {
+ exception.printStackTrace();
+ }
+ // Paper end
this.connection.send(new ClientboundDisconnectPacket(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA));
this.connection.disconnect(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index e7fbdccdd93bef036a7785acb19def334b039f0f..6ad11ded56422417f3407b043c42eb5d17557e60 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1010,6 +1010,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
}
io.papermc.paper.plugin.PluginInitializerManager.reload(this.console); // Paper
this.loadPlugins();

View file

@ -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 c06ce8822891e7d2a8fa2b649b02555ce6e3373c..c251202cb6b69b4dfa46b6836c6da1664f533738 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1515,6 +1515,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
public void internalTeleport(double d0, double d1, double d2, float f, float f1, Set<RelativeMovement> set) { // Paper
+ // 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;

View file

@ -1,705 +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 80ffd7fd14893c70da92dddb5ec37d409c76b729..dfd12484b424fffb529f408b6a9974c21598f9c2 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -36,6 +36,7 @@ dependencies {
implementation("org.ow2.asm:asm-commons:9.5") // Paper - ASM event executor generation
implementation("org.spongepowered:configurate-yaml:4.2.0-SNAPSHOT") // Paper - config files
implementation("commons-lang:commons-lang:2.6")
+ implementation("net.fabricmc:mapping-io:0.5.0") // Paper - needed to read mappings for stacktrace deobfuscation
runtimeOnly("org.xerial:sqlite-jdbc:3.42.0.1")
runtimeOnly("com.mysql:mysql-connector-j:8.1.0")
runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
@@ -125,6 +126,18 @@ tasks.check {
}
// Paper end
+// Paper start - include reobf mappings in jar for stacktrace deobfuscation
+val includeMappings = tasks.register<io.papermc.paperweight.tasks.IncludeMappings>("includeMappings") {
+ inputJar.set(tasks.fixJarForReobf.flatMap { it.outputJar })
+ mappings.set(tasks.reobfJar.flatMap { it.mappingsFile })
+ mappingsDest.set("META-INF/mappings/reobf.tiny")
+}
+
+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")
useJUnitPlatform()
diff --git a/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java b/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java
new file mode 100644
index 0000000000000000000000000000000000000000..66b6011ee3684695b2ab9292961c80bf2a420ee9
--- /dev/null
+++ b/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java
@@ -0,0 +1,66 @@
+package io.papermc.paper.logging;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+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 static final MethodHandle DEOBFUSCATE_THROWABLE;
+
+ static {
+ try {
+ final Class<?> cls = Class.forName("io.papermc.paper.util.StacktraceDeobfuscator");
+ final MethodHandles.Lookup lookup = MethodHandles.lookup();
+ final VarHandle instanceHandle = lookup.findStaticVarHandle(cls, "INSTANCE", cls);
+ final Object deobfuscator = instanceHandle.get();
+ DEOBFUSCATE_THROWABLE = lookup
+ .unreflect(cls.getDeclaredMethod("deobfuscateThrowable", Throwable.class))
+ .bindTo(deobfuscator);
+ } catch (final ReflectiveOperationException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ private StacktraceDeobfuscatingRewritePolicy() {
+ }
+
+ @Override
+ public @NonNull LogEvent rewrite(final @NonNull LogEvent rewrite) {
+ final Throwable thrown = rewrite.getThrown();
+ if (thrown != null) {
+ deobfuscateThrowable(thrown);
+ return new Log4jLogEvent.Builder(rewrite)
+ .setThrownProxy(null)
+ .build();
+ }
+ return rewrite;
+ }
+
+ private static void deobfuscateThrowable(final Throwable thrown) {
+ try {
+ DEOBFUSCATE_THROWABLE.invoke(thrown);
+ } catch (final Error e) {
+ throw e;
+ } catch (final Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @PluginFactory
+ public static @NonNull StacktraceDeobfuscatingRewritePolicy createPolicy() {
+ return new StacktraceDeobfuscatingRewritePolicy();
+ }
+}
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/util/ObfHelper.java b/src/main/java/io/papermc/paper/util/ObfHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8ff684d8bd994c64ff34f20e1e0601b678244c1
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/ObfHelper.java
@@ -0,0 +1,147 @@
+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.Objects;
+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_FILE, 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(
+ Objects.requireNonNull(methodMapping.getName(SPIGOT_NAMESPACE)),
+ Objects.requireNonNull(methodMapping.getDesc(SPIGOT_NAMESPACE))
+ )),
+ pool.string(Objects.requireNonNull(methodMapping.getName(MOJANG_PLUS_YARN_NAMESPACE)))
+ );
+ }
+
+ final ClassMapping map = new ClassMapping(
+ Objects.requireNonNull(cls.getName(SPIGOT_NAMESPACE)).replace('/', '.'),
+ Objects.requireNonNull(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..eb910d4abf91488fa7cf1f5d47e0ee916c47f512
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
@@ -0,0 +1,163 @@
+package io.papermc.paper.util;
+
+import io.papermc.paper.configuration.GlobalConfiguration;
+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 (GlobalConfiguration.get() != null && !GlobalConfiguration.get().logging.deobfuscateStacktraces) { // handle null as true
+ 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 (GlobalConfiguration.get() != null && !GlobalConfiguration.get().logging.deobfuscateStacktraces) { // handle null as true
+ 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..0b210bdf7c1f5962afbd44195af6f84f625635e3 100644
--- a/src/main/java/io/papermc/paper/util/TraceUtil.java
+++ b/src/main/java/io/papermc/paper/util/TraceUtil.java
@@ -6,13 +6,20 @@ 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();
+ }
+
+ public static void printStackTrace(Throwable thr) {
+ StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thr);
+ thr.printStackTrace();
}
}
diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java
index 226cba0c1eeedd9e80acd603c46b802c183db1fa..1d714d3eff11ed14f218656008190017494d4830 100644
--- a/src/main/java/net/minecraft/CrashReport.java
+++ b/src/main/java/net/minecraft/CrashReport.java
@@ -32,6 +32,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 52eb3176437113f9a0ff85d10ce5c2415e1b5570..b54ddd0ba0b001fbcb1838a838ca4890df936f1b 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 1931db6936773657bd43b9b16de950cb3e7a2303..36a78cc103ddf1cc7ccddefc0b3fd6cef987f67d 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -75,13 +75,13 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
public static final AttributeKey<ConnectionProtocol.CodecData<?>> ATTRIBUTE_SERVERBOUND_PROTOCOL = AttributeKey.valueOf("serverbound_protocol");
public static final AttributeKey<ConnectionProtocol.CodecData<?>> ATTRIBUTE_CLIENTBOUND_PROTOCOL = AttributeKey.valueOf("clientbound_protocol");
public static final Supplier<NioEventLoopGroup> NETWORK_WORKER_GROUP = Suppliers.memoize(() -> {
- 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 Supplier<EpollEventLoopGroup> NETWORK_EPOLL_WORKER_GROUP = Suppliers.memoize(() -> {
- 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 Supplier<DefaultEventLoopGroup> LOCAL_WORKER_GROUP = Suppliers.memoize(() -> {
- 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<WrappedConsumer> pendingActions = Queues.newConcurrentLinkedQueue();
@@ -211,7 +211,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
}
- if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) throwable.printStackTrace(); // Spigot
+ if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) io.papermc.paper.util.TraceUtil.printStackTrace(throwable); // Spigot // Paper
}
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet<?> packet) {
diff --git a/src/main/java/net/minecraft/network/PacketEncoder.java b/src/main/java/net/minecraft/network/PacketEncoder.java
index 45b4f1c295eda2fcc5067a4b21de247218ef117f..d364bd57b1675c8b21d781c2bc16c3e65800455c 100644
--- a/src/main/java/net/minecraft/network/PacketEncoder.java
+++ b/src/main/java/net/minecraft/network/PacketEncoder.java
@@ -47,7 +47,14 @@ public class PacketEncoder extends MessageToByteEncoder<Packet<?>> {
JvmProfiler.INSTANCE.onPacketSent(codecData.protocol(), i, channelHandlerContext.channel().remoteAddress(), k);
} catch (Throwable var13) {
- LOGGER.error("Packet encoding of packet ID {} threw (skippable? {})", i, packet.isSkippable(), var13); // Paper - Give proper error message
+ // Paper start - Give proper error message
+ String packetName = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(packet.getClass().getName());
+ if (packetName.contains(".")) {
+ packetName = packetName.substring(packetName.lastIndexOf(".") + 1);
+ }
+
+ LOGGER.error("Packet encoding of packet {} (ID: {}) threw (skippable? {})", packetName, i, packet.isSkippable(), var13);
+ // Paper end
if (packet.isSkippable()) {
throw new SkipPacketException(var13);
}
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index fce3edc97c7df45389bb3754f873a314d42929e4..557fc4e380c00bc2ca34381b36eb3d6a38177209 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -195,6 +195,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
org.spigotmc.SpigotConfig.registerCommands();
// Spigot end
// Paper start
+ io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // Paper - load mappings for stacktrace deobf and etc.
paperConfigurations.initializeGlobalConfiguration(this.registryAccess());
paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
org.spigotmc.WatchdogThread.doStart(org.spigotmc.SpigotConfig.timeoutTime, org.spigotmc.SpigotConfig.restartOnCrash);
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 0353b3e3a9d9a0bd44f48a61a02811ec0bad186a..58a44898144af605e6e7d9253d99149ddc12bcbd 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -221,7 +221,9 @@ public class ServerLevel extends Level implements WorldGenLevel {
public final UUID uuid;
public boolean hasPhysicsEvent = true; // Paper
public static Throwable getAddToWorldStackTrace(Entity entity) {
- return new Throwable(entity + " Added to world at " + new java.util.Date());
+ final Throwable thr = new Throwable(entity + " Added to world at " + new java.util.Date());
+ io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thr);
+ return thr;
}
@Override public LevelChunk getChunkIfLoaded(int x, int z) { // Paper - this was added in world too but keeping here for NMS ABI
@@ -1469,7 +1471,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
if (entity.isRemoved()) {
// Paper start
if (DEBUG_ENTITIES) {
- new Throwable("Tried to add entity " + entity + " but it was marked as removed already").printStackTrace(); // CraftBukkit
+ io.papermc.paper.util.TraceUtil.dumpTraceForThread("Tried to add entity " + entity + " but it was marked as removed already"); // CraftBukkit
getAddToWorldStackTrace(entity).printStackTrace();
}
// Paper end
diff --git a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
index 77e19f345bf68d12686a65e669cd597cd92af910..8dbcc1b3a70b6bbea3bd2d15b6d66cc4f9cd53f8 100644
--- a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -133,7 +133,7 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis
ServerConfigurationPacketListenerImpl.LOGGER.error("Couldn't place player in world", exception);
// Paper start
if (MinecraftServer.getServer().isDebugging()) {
- exception.printStackTrace();
+ io.papermc.paper.util.TraceUtil.printStackTrace(exception);
}
// Paper end
this.connection.send(new ClientboundDisconnectPacket(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA));
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
index caeead6c6082855f1651ee28263cc9f60423ca0c..b2bfb3893200362ac35ae60982f203f86a1148ec 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 Supplier<NioEventLoopGroup> SERVER_EVENT_GROUP = Suppliers.memoize(() -> {
- 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 Supplier<EpollEventLoopGroup> SERVER_EPOLL_EVENT_GROUP = Suppliers.memoize(() -> {
- 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/net/minecraft/server/players/OldUsersConverter.java b/src/main/java/net/minecraft/server/players/OldUsersConverter.java
index 5db27d7bcaaa2eeaeeb08401513d8d23f6cb63c7..ce43cb0152ba07c6c21e08142d65813d47c3b63b 100644
--- a/src/main/java/net/minecraft/server/players/OldUsersConverter.java
+++ b/src/main/java/net/minecraft/server/players/OldUsersConverter.java
@@ -359,7 +359,7 @@ public class OldUsersConverter {
try {
root = NbtIo.readCompressed(new java.io.FileInputStream(file5));
} catch (Exception exception) {
- exception.printStackTrace();
+ io.papermc.paper.util.TraceUtil.printStackTrace(exception); // Paper
ServerInternalException.reportInternalException(exception); // Paper
}
@@ -373,7 +373,7 @@ public class OldUsersConverter {
try {
NbtIo.writeCompressed(root, new java.io.FileOutputStream(file2));
} catch (Exception exception) {
- exception.printStackTrace();
+ io.papermc.paper.util.TraceUtil.printStackTrace(exception); // Paper
ServerInternalException.reportInternalException(exception); // Paper
}
}
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
index 12af77215bfd6df3b6802a567ac3c013a4cdf06a..fa170cc1ce7011d201295b89718292d696c7fc24 100644
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
@@ -600,7 +600,7 @@ public class LevelChunk extends ChunkAccess {
+ " (" + getBlockState(blockposition) + ") where there was no entity tile!\n" +
"Chunk coordinates: " + (this.chunkPos.x * 16) + "," + (this.chunkPos.z * 16) +
"\nWorld: " + level.getLevel().dimension().location());
- e.printStackTrace();
+ io.papermc.paper.util.TraceUtil.printStackTrace(e);
ServerInternalException.reportInternalException(e);
// Paper end
// CraftBukkit end
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 5ca863aa1859922fa359eba32539229db40e5b98..dca163ff5436f1007383c8261cac1ac7c0613f23 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -105,7 +105,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
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 );
}
@@ -192,7 +192,7 @@ public final class WatchdogThread extends io.papermc.paper.util.TickThread // Pa
}
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 266b4e6fb3988b5848021c83fdc68e342c70b188..2b247d55e39246fbef31279b14c45fc40f956bfb 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">

View file

@ -1,921 +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 dfd12484b424fffb529f408b6a9974c21598f9c2..08962fc6c7b66a79e7f6985c45ef593407594f96 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -45,6 +45,7 @@ dependencies {
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.7.3")
runtimeOnly("org.apache.maven.resolver:maven-resolver-transport-http:1.7.3")
+ testImplementation("io.github.classgraph:classgraph:4.8.47") // Paper - mob goal test
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
testImplementation("org.hamcrest:hamcrest:2.2")
testImplementation("org.mockito:mockito-core:5.5.0")
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..f80a6ad7638453348ee82ea00b166a3aac029142
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
@@ -0,0 +1,374 @@
+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.camel.Camel;
+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(Camel.class, org.bukkit.entity.Camel.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);
+ bukkitMap.put(net.minecraft.world.entity.animal.frog.Frog.class, org.bukkit.entity.Frog.class);
+ bukkitMap.put(net.minecraft.world.entity.animal.frog.Tadpole.class, org.bukkit.entity.Tadpole.class);
+ bukkitMap.put(net.minecraft.world.entity.monster.warden.Warden.class, org.bukkit.entity.Warden.class);
+ bukkitMap.put(net.minecraft.world.entity.animal.allay.Allay.class, org.bukkit.entity.Allay.class);
+ bukkitMap.put(net.minecraft.world.entity.animal.sniffer.Sniffer.class, org.bukkit.entity.Sniffer.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..5e7b4a8698a00f72d6e817cc7c6716e7605a3484
--- /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()).getAvailableGoals()) {
+ 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).getAvailableGoals()) {
+ 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).getAvailableGoals()) {
+ 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 6ad11ded56422417f3407b043c42eb5d17557e60..03ff1e8faec27f042aa9b3b5f5bcbe1abbde2cbd 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2845,5 +2845,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..654ed436e99dd56f1fe7c1d4f38da34d95ce9349
--- /dev/null
+++ b/src/test/java/com/destroystokyo/paper/entity/ai/VanillaMobGoalTest.java
@@ -0,0 +1,106 @@
+package com.destroystokyo.paper.entity.ai;
+
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ScanResult;
+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 org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+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) {
+ 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();
+ }
+ assertNotEquals(Collections.emptyList(), classes, "There are supposed to be more than 0 entity types!");
+
+ 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) {
+ fail("See above");
+ }
+ }
+}

View file

@ -1,122 +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
== AT ==
public net.minecraft.world.entity.ai.gossip.GossipContainer$EntityGossips
public net.minecraft.world.entity.ai.gossip.GossipContainer$EntityGossips <init>()V
public net.minecraft.world.entity.ai.gossip.GossipContainer gossips
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 a28f359202e6502c6ea5e9c918ec0b3e9a3fca61..76dffb2705e5207db96895f82f1c7c5638f817c6 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
@@ -231,6 +231,43 @@ public class GossipContainer {
public void remove(GossipType gossipType) {
this.entries.removeInt(gossipType);
}
+
+ // Paper start - Add villager reputation API
+ private static final GossipType[] TYPES = GossipType.values();
+ public com.destroystokyo.paper.entity.villager.Reputation getPaperReputation() {
+ Map<com.destroystokyo.paper.entity.villager.ReputationType, Integer> map = new java.util.EnumMap<>(com.destroystokyo.paper.entity.villager.ReputationType.class);
+ for (Object2IntMap.Entry<GossipType> type : this.entries.object2IntEntrySet()) {
+ map.put(toApi(type.getKey()), type.getIntValue());
+ }
+
+ return new com.destroystokyo.paper.entity.villager.Reputation(map);
+ }
+
+ public void assignFromPaperReputation(com.destroystokyo.paper.entity.villager.Reputation rep) {
+ for (GossipType type : TYPES) {
+ com.destroystokyo.paper.entity.villager.ReputationType api = toApi(type);
+
+ if (rep.hasReputationSet(api)) {
+ int reputation = rep.getReputation(api);
+ if (reputation == 0) {
+ this.entries.removeInt(type);
+ } else {
+ this.entries.put(type, reputation);
+ }
+ }
+ }
+ }
+
+ private static com.destroystokyo.paper.entity.villager.ReputationType toApi(GossipType type) {
+ return switch (type) {
+ case MAJOR_NEGATIVE -> com.destroystokyo.paper.entity.villager.ReputationType.MAJOR_NEGATIVE;
+ case MINOR_NEGATIVE -> com.destroystokyo.paper.entity.villager.ReputationType.MINOR_NEGATIVE;
+ case MINOR_POSITIVE -> com.destroystokyo.paper.entity.villager.ReputationType.MINOR_POSITIVE;
+ case MAJOR_POSITIVE -> com.destroystokyo.paper.entity.villager.ReputationType.MAJOR_POSITIVE;
+ case TRADING -> com.destroystokyo.paper.entity.villager.ReputationType.TRADING;
+ };
+ }
+ // Paper end
}
static record GossipEntry(UUID target, GossipType type, int value) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
index 423f6fcaf49252553d2285308633f13e2427b607..00fb708bce2c79817cd9fccadec72f07f0d26317 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVillager.java
@@ -18,6 +18,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) {
@@ -176,4 +183,45 @@ public class CraftVillager extends CraftAbstractVillager implements Villager {
.getOptional(CraftNamespacedKey.toMinecraft(bukkit.getKey())).orElseThrow();
}
}
+
+ // Paper start - Add villager reputation API
+ @Override
+ public Reputation getReputation(UUID uniqueId) {
+ net.minecraft.world.entity.ai.gossip.GossipContainer.EntityGossips rep = getHandle().getGossips().gossips.get(uniqueId);
+ if (rep == null) {
+ return new Reputation(new java.util.EnumMap<>(com.destroystokyo.paper.entity.villager.ReputationType.class));
+ }
+
+ return rep.getPaperReputation();
+ }
+
+ @Override
+ public Map<UUID, Reputation> getReputations() {
+ return getHandle().getGossips().gossips.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().gossips.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().gossips.clear();
+ }
+ // Paper end
}

View file

@ -1,42 +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/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 39d710d98ca5435e7645ab91c109090c42ffc52f..195735de5f35c0d80a8c5ae4bca20f149ce49f90 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -690,16 +690,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().entities.behavior.experienceMergeMaxValue;
+ final boolean mergeUnconditionally = world.paperConfig().entities.behavior.experienceMergeMaxValue <= 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)) {

View file

@ -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 195735de5f35c0d80a8c5ae4bca20f149ce49f90..c634a4b0d835df3ad6129ca03e11668f715ab8d2 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -700,7 +700,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) {

View file

@ -1,60 +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
Co-authored-by: Tamion <70228790+notTamion@users.noreply.github.com>
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
index 904f34f52659e864d148204cb0eebb9b381cc24a..38cad5b0d7e6018adda56b3292280738c46622e9 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@@ -443,7 +443,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
@Override
public boolean addPotionEffect(PotionEffect effect, boolean force) {
- this.getHandle().addEffect(new MobEffectInstance(CraftPotionEffectType.bukkitToMinecraft(effect.getType()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles()), EntityPotionEffectEvent.Cause.PLUGIN);
+ this.getHandle().addEffect(CraftPotionUtil.fromBukkit(effect), EntityPotionEffectEvent.Cause.PLUGIN); // Paper - Don't ignore icon
return true;
}
@@ -464,7 +464,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
@Override
public PotionEffect getPotionEffect(PotionEffectType type) {
MobEffectInstance handle = this.getHandle().getEffect(CraftPotionEffectType.bukkitToMinecraft(type));
- return (handle == null) ? null : new PotionEffect(CraftPotionEffectType.minecraftToBukkit(handle.getEffect()), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isVisible());
+ return (handle == null) ? null : CraftPotionUtil.toBukkit(handle); // Paper
}
@Override
@@ -476,7 +476,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
public Collection<PotionEffect> getActivePotionEffects() {
List<PotionEffect> effects = new ArrayList<PotionEffect>();
for (MobEffectInstance handle : this.getHandle().activeEffects.values()) {
- effects.add(new PotionEffect(CraftPotionEffectType.minecraftToBukkit(handle.getEffect()), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isVisible()));
+ effects.add(CraftPotionUtil.toBukkit(handle)); // Paper
}
return effects;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java
index e29679a92da5ec05e122bb972a5ee469059a7a0a..844fb8c662a409670f631228f687d85c5436d3dd 100644
--- a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java
+++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionUtil.java
@@ -73,7 +73,7 @@ public class CraftPotionUtil {
public static MobEffectInstance fromBukkit(PotionEffect effect) {
MobEffect type = CraftPotionEffectType.bukkitToMinecraft(effect.getType());
- return new MobEffectInstance(type, effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles());
+ return new MobEffectInstance(type, effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles(), effect.hasIcon()); // Paper
}
public static PotionEffect toBukkit(MobEffectInstance effect) {
@@ -82,7 +82,7 @@ public class CraftPotionUtil {
int duration = effect.getDuration();
boolean ambient = effect.isAmbient();
boolean particles = effect.isVisible();
- return new PotionEffect(type, duration, amp, ambient, particles);
+ return new PotionEffect(type, duration, amp, ambient, particles, effect.showIcon()); // Paper
}
public static boolean equals(MobEffect mobEffect, PotionEffectType type) {

View file

@ -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 3384501f83d445f45aa8233e98c7597daa67b8ef..20a7cdf87f307878d66922aaac0c60cff218e46c 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;
@@ -107,6 +107,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) {

View file

@ -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 6a9ab8bab7eb443f83e939bed17cb6fc9d1f3906..4ac714e39804fc8d269a717e8abf98f5814319e8 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;
@@ -130,6 +131,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");

View file

@ -1,59 +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 fdaf757cbbc90841747989add56c13f252f645e5..6b7bacb0ea667c488b09da8c2a8a685d3a86608e 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -912,6 +912,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// CraftBukkit start
if (this.server != null) {
this.server.disablePlugins();
+ this.server.waitForAsyncTasksShutdown(); // Paper
}
// CraftBukkit end
this.getConnection().stop();
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 03ff1e8faec27f042aa9b3b5f5bcbe1abbde2cbd..8d78720a613c089ad1bd2d78b83da8a42b76fc76 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1020,6 +1020,31 @@ 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();
+ getLogger().log(Level.SEVERE, String.format(
+ "Nag author(s): '%s' of '%s' about the following: %s",
+ plugin.getPluginMeta().getAuthors(),
+ plugin.getPluginMeta().getDisplayName(),
+ "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(this.console);

View file

@ -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 b35fcab88e9fd883a854c6861b6c69d725105539..49e07b33fccfe339712e5d1bd72e6c0edbd2e922 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())) {

View file

@ -1,169 +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/net/minecraft/world/level/Explosion.java b/src/main/java/net/minecraft/world/level/Explosion.java
index c0227cda09b9ca508c1de1fbe1e57afd743a9426..93529defa5d36bb9b4c69eedda9b7ac3d0a189ce 100644
--- a/src/main/java/net/minecraft/world/level/Explosion.java
+++ b/src/main/java/net/minecraft/world/level/Explosion.java
@@ -169,6 +169,7 @@ public class Explosion {
for (float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) {
BlockPos blockposition = BlockPos.containing(d4, d5, d6);
BlockState iblockdata = this.level.getBlockState(blockposition);
+ if (!iblockdata.isDestroyable()) continue; // Paper
FluidState fluid = iblockdata.getFluidState(); // Paper
if (!this.level.isInWorldBounds(blockposition)) {
@@ -371,7 +372,7 @@ public class Explosion {
}
// CraftBukkit end
- 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 046fa6e5b2013c0872176592dd8cc6681a9b308a..7825f6185a9a73dd395cdb6d86947d33d8512200 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -518,6 +518,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 7646b66bc5ba0288608de0d836c7307e02eebe67..36b196c8834c4eb873bfca0b12f1fc2b421ea071 100644
--- a/src/main/java/net/minecraft/world/level/block/Block.java
+++ b/src/main/java/net/minecraft/world/level/block/Block.java
@@ -88,6 +88,19 @@ public class Block extends BlockBehaviour implements ItemLike {
protected final StateDefinition<Block, BlockState> stateDefinition;
private BlockState defaultBlockState;
// Paper start
+ public final boolean isDestroyable() {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits ||
+ 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 e4e4ad933db6484589dc2b51ef2f5a6745ffb891..6b0f70907236065bc2769541bf430ccc66df5081 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
@@ -199,6 +199,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 (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits && enumdirection != directionQueuedAs) {
+ return false;
+ }
+ // Paper end - prevent retracting when we're facing the wrong way
BlockState iblockdata1 = (BlockState) state.setValue(PistonBaseBlock.EXTENDED, true);
if (!world.isClientSide) {
@@ -232,7 +238,7 @@ public class PistonBaseBlock extends DirectionalBlock {
BlockState iblockdata2 = (BlockState) ((BlockState) Blocks.MOVING_PISTON.defaultBlockState().setValue(MovingPistonBlock.FACING, enumdirection)).setValue(MovingPistonBlock.TYPE, this.isSticky ? PistonType.STICKY : PistonType.DEFAULT);
world.setBlock(pos, iblockdata2, 20);
- world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true));
+ world.setBlockEntity(MovingPistonBlock.newMovingBlockEntity(pos, iblockdata2, (BlockState) this.defaultBlockState().setValue(PistonBaseBlock.FACING, Direction.from3DDataValue(data & 7)), enumdirection, false, true)); // Paper - diff on change
world.blockUpdated(pos, iblockdata2.getBlock());
iblockdata2.updateNeighbourShapes(world, pos, 2);
if (this.isSticky) {
@@ -261,7 +267,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 (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits || 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 916d7b35145345cb3bf5ca46df38eae0512c4e95..a5942b6683d38f067f8ca1dfbe467b72df242632 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
@@ -229,7 +229,7 @@ public abstract class BlockBehaviour implements FeatureElement {
/** @deprecated */
@Deprecated
public boolean canBeReplaced(BlockState state, BlockPlaceContext context) {
- return state.canBeReplaced() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem()));
+ return state.canBeReplaced() && (context.getItemInHand().isEmpty() || !context.getItemInHand().is(this.asItem())) && (state.isDestroyable() || (context.getPlayer() != null && context.getPlayer().getAbilities().instabuild)); // Paper;
}
/** @deprecated */
@@ -891,6 +891,12 @@ public abstract class BlockBehaviour implements FeatureElement {
return this.legacySolid;
}
+ // Paper start
+ public final boolean isDestroyable() {
+ return getBlock().isDestroyable();
+ }
+ // Paper end
+
public boolean isValidSpawn(BlockGetter world, BlockPos pos, EntityType<?> type) {
return this.getBlock().properties.isValidSpawn.test(this.asState(), world, pos, type);
}
@@ -994,7 +1000,7 @@ public abstract class BlockBehaviour implements FeatureElement {
}
public PushReaction getPistonPushReaction() {
- return this.pushReaction;
+ return !this.isDestroyable() ? PushReaction.BLOCK : this.pushReaction; // 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 ed5210c63d964be7c28f59df315766794ec3ea1f..08325c055b04355089d75031522c7b74d83c6cca 100644
--- a/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
+++ b/src/main/java/net/minecraft/world/level/portal/PortalForcer.java
@@ -221,6 +221,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 (!io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPermanentBlockBreakExploits) {
+ if (!this.level.getBlockState(temp).isDestroyable()) {
+ return false;
+ }
+ }
+ // Paper end - prevent destroying unbreakable blocks
if (k < 0 && !this.level.getBlockState(temp).isSolid()) {
return false;
}

View file

@ -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 85914124014b4e6f0a561cf560918af68682b6f5..1ec80f9c901dff1c9f29befa5a8e3c3f6f37aaf7 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) {

View file

@ -1,47 +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/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java b/src/main/java/org/bukkit/craftbukkit/command/CraftConsoleCommandSender.java
index 324e6d1a4fadd3e557e4ba05f04e6a5891cc54df..4e56018b64d11f76c8da43fd8f85c6de72204e36 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 io.papermc.paper.configuration.GlobalConfiguration.get().console.hasAllPermissions || super.hasPermission(name);
+ }
+
+ @Override
+ public boolean hasPermission(org.bukkit.permissions.Permission perm) {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().console.hasAllPermissions || 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 5b7d230103f421fb939072e1526854f715430e51..b5e325a7b2d3f49299b35e233ed6539b5bfc3465 100644
--- a/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/CraftRemoteConsoleCommandSender.java
@@ -55,4 +55,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 io.papermc.paper.configuration.GlobalConfiguration.get().console.hasAllPermissions || super.hasPermission(name);
+ }
+
+ @Override
+ public boolean hasPermission(org.bukkit.permissions.Permission perm) {
+ return io.papermc.paper.configuration.GlobalConfiguration.get().console.hasAllPermissions || super.hasPermission(perm);
+ }
+ // Paper end
}

View file

@ -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 a580011cd3b2b7129f2d09f37a3c26cdaafeefe8..1d755d04515f20dbd69931084b4cc894e52d35c9 100644
--- a/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java
+++ b/src/main/java/net/minecraft/world/item/trading/MerchantOffer.java
@@ -130,7 +130,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() {

View file

@ -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 592bdf191f8787cf35d8844f66371c92179545ad..709928f6220c2148f95afa94c34df2a87cff0e1f 100644
--- a/src/main/java/net/minecraft/world/item/MapItem.java
+++ b/src/main/java/net/minecraft/world/item/MapItem.java
@@ -134,9 +134,9 @@ public class MapItem extends ComplexItem {
int j2 = (j / i + k1 - 64) * i;
int k2 = (k / i + l1 - 64) * i;
Multiset<MapColor> multiset = LinkedHashMultiset.create();
- LevelChunk chunk = world.getChunk(SectionPos.blockToSectionCoord(j2), SectionPos.blockToSectionCoord(k2));
+ LevelChunk chunk = world.getChunkIfLoaded(SectionPos.blockToSectionCoord(j2), SectionPos.blockToSectionCoord(k2)); // Paper - Maps shouldn't load chunks
- if (!chunk.isEmpty()) {
+ if (chunk != null && !chunk.isEmpty()) { // Paper - Maps shouldn't load chunks
int l2 = 0;
double d1 = 0.0D;
int i3;

View file

@ -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 709928f6220c2148f95afa94c34df2a87cff0e1f..53515854d05a0c27a65f71193424236a21b11f5f 100644
--- a/src/main/java/net/minecraft/world/item/MapItem.java
+++ b/src/main/java/net/minecraft/world/item/MapItem.java
@@ -250,14 +250,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) {
- Holder<Biome> holder = world.getBiome(blockposition_mutableblockposition.set((l + k1) * i, 0, (i1 + j1) * i));
+ Holder<Biome> holder = world.getUncachedNoiseBiome((l + k1) * i, 0, (i1 + j1) * i); // Paper
aboolean[j1 * 128 + k1] = holder.is(BiomeTags.WATER_ON_MAP_OUTLINES);
}

View file

@ -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 215310984100722757d9dd38182f7cbc163a4a0f..acb2af336184c0215c409c748b56fddd8d1fb4be 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftScheduler.java
@@ -196,7 +196,7 @@ public class CraftScheduler implements BukkitScheduler {
@Override
public void runTaskTimerAsynchronously(Plugin plugin, Consumer<? super BukkitTask> task, long delay, long period) throws IllegalArgumentException {
- this.runTaskTimerAsynchronously(plugin, (Object) task, delay, CraftTask.NO_REPEATING);
+ this.runTaskTimerAsynchronously(plugin, (Object) task, delay, period); // Paper
}
@Override

View file

@ -1,80 +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/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/src/main/java/net/minecraft/world/level/block/piston/PistonBaseBlock.java
index 6b0f70907236065bc2769541bf430ccc66df5081..929241e479a42011f073fb9d144739b6504132b1 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
@@ -413,14 +413,26 @@ public class PistonBaseBlock extends DirectionalBlock {
}
for (j = list.size() - 1; j >= 0; --j) {
- blockposition3 = (BlockPos) list.get(j);
- iblockdata1 = world.getBlockState(blockposition3);
+ // Paper start - fix a variety of piston desync dupes
+ boolean allowDesync = io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPistonDuplication;
+ BlockPos oldPos = blockposition3 = (BlockPos) list.get(j);
+ 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(j), 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(j) : iblockdata1, dir, retract, false));
+ if (!allowDesync) {
+ world.setBlock(oldPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_MOVE_BY_PISTON | 1024); // set air to prevent later physics updates from seeing this block
+ }
+ // Paper end - fix a variety of piston desync dupes
aiblockdata[i++] = 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 17a6327ab7b26dfab38881bbc0689b0b25f8f025..1ef87580574919796dbba707f44a413ee5c5781b 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
@@ -288,7 +288,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, io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPistonDuplication ? 84 : (84 | Block.UPDATE_CLIENTS)); // 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)) {

View file

@ -1,38 +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 c431ba283c31cf9bb7bfdb05dc8c181c9e1e0eb3..5164c2bfb32275beff01b6e76dfbd9d031231bc6 100644
--- a/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
+++ b/src/main/java/net/minecraft/world/entity/item/FallingBlockEntity.java
@@ -131,6 +131,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 {
@@ -142,6 +147,13 @@ 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().fixes.fallingBlockHeightNerf.test(v -> this.getY() > v)) {
if (this.dropItem && this.level().getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {

View file

@ -1,29 +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 EndIslandDensityFunction 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.
This issue is being tracked in Mojira ticket MC-159283
diff --git a/src/main/java/net/minecraft/world/level/levelgen/DensityFunctions.java b/src/main/java/net/minecraft/world/level/levelgen/DensityFunctions.java
index 299116c5c4d25c78a2af00bb44c4f51ac04286e8..fac92f37c32e0398ebc05d9a0378446fcabaef1a 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/DensityFunctions.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/DensityFunctions.java
@@ -502,7 +502,7 @@ public final class DensityFunctions {
int j = z / 2;
int k = x % 2;
int l = z % 2;
- float f = 100.0F - Mth.sqrt((float)(x * x + z * z)) * 8.0F;
+ float f = 100.0F - Mth.sqrt((long) x * (long) x + (long) z * (long) z) * 8.0F; // Paper - cast ints to long to avoid integer overflow
f = Mth.clamp(f, -100.0F, 80.0F);
for(int m = -12; m <= 12; ++m) {

View file

@ -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 c251202cb6b69b4dfa46b6836c6da1664f533738..3f9f2e4b64df72f3f01aed191d57766978c5886c 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1341,6 +1341,11 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
this.player.move(MoverType.PLAYER, new Vec3(d6, d7, d8));
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 d11 = d7;
d6 = d0 - this.player.getX();

View file

@ -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 c064022d248ff3e0b52c0e815ab90527f9132fb7..3680f101036d98814fe47b707baeeb8e55bfc4b7 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventory.java
@@ -538,6 +538,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 c00c787a73b0796b645667427666b7ec4e333992..c3e2c9e2c3cbec2eda38096b6482bac1a0ea1dce 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryDoubleChest.java
@@ -63,6 +63,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);

View file

@ -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 35d8fbd9c5751568a1a3b8928017e23cd41bb163..762c395e45a681a11f3fe9d10e7f0ba310786e80 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftArrow.java
@@ -99,6 +99,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);

View file

@ -1,55 +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 3f9f2e4b64df72f3f01aed191d57766978c5886c..292a80afecb7a67e5e5900578867612d91ca82a5 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2959,16 +2959,40 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
if (!this.player.containerMenu.stillValid(this.player)) {
ServerGamePacketListenerImpl.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, this.player.containerMenu);
} else {
+ // Paper start
+ ResourceLocation recipeName = packet.getRecipe();
+ boolean makeAll = packet.isShiftDown();
+ com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent paperEvent = new com.destroystokyo.paper.event.player.PlayerRecipeBookClickEvent(
+ this.player.getBukkitEntity(), org.bukkit.craftbukkit.util.CraftNamespacedKey.fromMinecraft(recipeName), makeAll
+ );
+ if (!paperEvent.callEvent()) {
+ return;
+ }
+ recipeName = CraftNamespacedKey.toMinecraft(paperEvent.getRecipe());
+ makeAll = paperEvent.isMakeAll();
+ if (org.bukkit.event.player.PlayerRecipeBookClickEvent.getHandlerList().getRegisteredListeners().length > 0) {
+ // Paper end
// CraftBukkit start - implement PlayerRecipeBookClickEvent
- org.bukkit.inventory.Recipe recipe = this.cserver.getRecipe(CraftNamespacedKey.fromMinecraft(packet.getRecipe()));
+ org.bukkit.inventory.Recipe recipe = this.cserver.getRecipe(CraftNamespacedKey.fromMinecraft(recipeName)); // Paper
if (recipe == null) {
return;
}
- org.bukkit.event.player.PlayerRecipeBookClickEvent event = CraftEventFactory.callRecipeBookClickEvent(this.player, recipe, packet.isShiftDown());
+ // Paper start
+ org.bukkit.event.player.PlayerRecipeBookClickEvent event = CraftEventFactory.callRecipeBookClickEvent(this.player, recipe, makeAll);
+ recipeName = CraftNamespacedKey.toMinecraft(((org.bukkit.Keyed) event.getRecipe()).getKey());
+ makeAll = event.isShiftClick();
+ }
+ if (!(this.player.containerMenu instanceof RecipeBookMenu<?> recipeBookMenu)) {
+ return;
+ }
+ // Paper end
// Cast to keyed should be safe as the recipe will never be a MerchantRecipe.
- this.server.getRecipeManager().byKey(CraftNamespacedKey.toMinecraft(((org.bukkit.Keyed) event.getRecipe()).getKey())).ifPresent((recipeholder) -> {
- ((RecipeBookMenu) this.player.containerMenu).handlePlacement(event.isShiftClick(), recipeholder, this.player);
+ // Paper start
+ final boolean finalMakeAll = makeAll;
+ this.server.getRecipeManager().byKey(recipeName).ifPresent((recipeholder) -> {
+ recipeBookMenu.handlePlacement(finalMakeAll, recipeholder, this.player);
+ // Paper end
});
// CraftBukkit end
}

View file

@ -1,341 +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 8d12a6da0f0679fd14c2a498f9645bd04b2d8ed7..290a231deb203eb46cb91eb23c28d8e9d49b0f34 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -147,6 +147,23 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Paper start - distance maps
private final com.destroystokyo.paper.util.misc.PooledLinkedHashSets<ServerPlayer> pooledLinkedPlayerHashSets = new com.destroystokyo.paper.util.misc.PooledLinkedHashSets<>();
+ // 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 net.minecraft.server.MinecraftServer.getServer().getScaledTrackingDistance(vanilla);
+ }
+ // Paper end - use distance map to optimise tracker
void addPlayerToDistanceMaps(ServerPlayer player) {
int chunkX = io.papermc.paper.util.MCUtil.getChunkCoordinate(player.getX());
@@ -154,6 +171,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Note: players need to be explicitly added to distance maps before they can be updated
this.nearbyPlayers.addPlayer(player);
this.level.playerChunkLoader.addPlayer(player); // Paper - replace chunk loader
+ // 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, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player)));
+ }
+ // Paper end - use distance map to optimise entity tracker
}
void removePlayerFromDistanceMaps(ServerPlayer player) {
@@ -162,6 +187,11 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Note: players need to be explicitly added to distance maps before they can be updated
this.nearbyPlayers.removePlayer(player);
this.level.playerChunkLoader.removePlayer(player); // Paper - replace chunk loader
+ // 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
}
void updateMaps(ServerPlayer player) {
@@ -170,6 +200,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// Note: players need to be explicitly added to distance maps before they can be updated
this.nearbyPlayers.tickPlayer(player);
this.level.playerChunkLoader.updatePlayer(player); // Paper - replace chunk loader
+ // 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, io.papermc.paper.chunk.system.ChunkSystem.getSendViewDistance(player)));
+ }
+ // Paper end - use distance map to optimise entity tracker
}
// Paper end
// Paper start
@@ -257,6 +295,48 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.regionManagers.add(this.dataRegionManager);
this.nearbyPlayers = new io.papermc.paper.util.player.NearbyPlayers(this.level);
// Paper end
+ // 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;
+ case DISPLAY:
+ configuredSpigotValue = spigotWorldConfig.displayTrackingRange;
+ 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
@@ -930,17 +1010,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
SectionPos sectionposition = player.getLastSectionPos();
SectionPos sectionposition1 = SectionPos.of((EntityAccess) player);
@@ -1017,7 +1087,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;
@@ -1061,9 +1131,37 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.tracker = null; // Paper - We're no longer tracked
}
- protected void tick() {
- // Paper - replaced by PlayerChunkLoader
+ // 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();
@@ -1213,6 +1311,42 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.lastSectionPos = SectionPos.of((EntityAccess) 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 8ecb398ca26753e602c2862bf0c35b76f9a413c0..082509d2ab18380b37868decef3fdff6542bf764 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -56,6 +56,7 @@ import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
+import io.papermc.paper.util.MCUtil;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
@@ -481,6 +482,38 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
public boolean updatingSectionStatus = false;
// 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();
diff --git a/src/main/java/org/spigotmc/TrackingRange.java b/src/main/java/org/spigotmc/TrackingRange.java
index 172d231adecf043f9f06b7f5e0365ae82327998d..ed8378ad022c375b0d18172aeccf65cb026d9d68 100644
--- a/src/main/java/org/spigotmc/TrackingRange.java
+++ b/src/main/java/org/spigotmc/TrackingRange.java
@@ -55,4 +55,48 @@ public class TrackingRange
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 if (entity instanceof Display) {
+ return TrackingRangeType.DISPLAY;
+ } else
+ {
+ return TrackingRangeType.OTHER;
+ }
+ }
+
+ public static enum TrackingRangeType {
+ PLAYER,
+ ANIMAL,
+ MONSTER,
+ MISC,
+ OTHER,
+ ENDERDRAGON,
+ DISPLAY;
+ }
+ // Paper end - optimise entity tracking
}

View file

@ -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 bdf520c32847658fefaf2bde33bac16ea807002f..c5598adb8cbcbcf7277c2fd4dd72c243d44d9700 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServerProperties.java
@@ -145,7 +145,7 @@ public class DedicatedServerProperties extends Settings<DedicatedServerPropertie
this.maxWorldSize = this.get("max-world-size", (integer) -> {
return Mth.clamp(integer, 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);

View file

@ -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 0ff4372afa4b159acc59e3dbd2e9efbd7b7ab6a2..1685dd08ef6331e133d752cd9ce3874c9eda5a05 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -399,7 +399,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 292a80afecb7a67e5e5900578867612d91ca82a5..6add371484deca6ed041e434fea5dc54c8db12d9 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -778,7 +778,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
if (!this.server.isCommandBlockEnabled()) {
this.player.sendSystemMessage(Component.translatable("advMode.notEnabled"));
- } 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.sendSystemMessage(Component.translatable("advMode.notAllowed"));
} else {
BaseCommandBlock commandblocklistenerabstract = null;
@@ -845,7 +845,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
if (!this.server.isCommandBlockEnabled()) {
this.player.sendSystemMessage(Component.translatable("advMode.notEnabled"));
- } 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.sendSystemMessage(Component.translatable("advMode.notAllowed"));
} 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 9c7b7434ccd64668eb5d7bb61d03a9cb4105ea6e..e05eb08a9c229b371887676da510df948b896a85 100644
--- a/src/main/java/net/minecraft/world/level/BaseCommandBlock.java
+++ b/src/main/java/net/minecraft/world/level/BaseCommandBlock.java
@@ -198,7 +198,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 061a56e3828767cd6576d5a9edde5f3498e609d0..2e7c03b00bc941b86df6a7f1b2b188c9f0aede22 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 245ad120a36b6defca7e6889faae1ca5fc33d0c7..e0e61115ada9a49d4c528c5d4e02a1ca571d9531 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();
}

View file

@ -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 082509d2ab18380b37868decef3fdff6542bf764..78af47c54f8dd3b1b4f81b740bcd438132c27947 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -721,8 +721,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() {
@@ -4202,6 +4202,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
// Paper start - rewrite chunk system
if (this.updatingSectionStatus) {
LOGGER.error("Refusing to update position for entity " + this + " to position " + new Vec3(x, y, z) + " since it is processing a section status update", new Throwable());
@@ -4225,6 +4230,12 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
this.levelCallback.onMove();
}
+ // 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() {}

View file

@ -1,131 +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 6b7bacb0ea667c488b09da8c2a8a685d3a86608e..219e191ff4d590aee9b7a704ba6fe84c3c12f43a 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -782,7 +782,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (worldserver.getWorld().getKeepSpawnInMemory()) worldloadlistener.stop(); // Paper
// 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
@@ -1726,11 +1726,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
}
}
@@ -1744,7 +1747,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 89be3991ef4fb2deb7276c5409cb571a7fb1f821..9c272f7cf8cbd2bbe147e57f7fabe135b6ff5c0b 100644
--- a/src/main/java/net/minecraft/server/commands/DifficultyCommand.java
+++ b/src/main/java/net/minecraft/server/commands/DifficultyCommand.java
@@ -49,7 +49,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(() -> {
return Component.translatable("commands.difficulty.success", difficulty.getDisplayName());
}, true);
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index 557fc4e380c00bc2ca34381b36eb3d6a38177209..9e631002278e21f1e0a3989573e7d5b2e1a82dd8 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -325,7 +325,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@Override
public void forceDifficulty() {
- this.setDifficulty(this.getProperties().difficulty, true);
+ // this.setDifficulty(this.getProperties().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 49d341605ac7e82c5dcdcc960b6581aa7e1fb8f3..a3c1797549e3b149f425f857bbf8564c5ef1e30e 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -1204,7 +1204,7 @@ public class ServerPlayer extends Player {
this.isChangingDimension = true; // CraftBukkit - Set teleport invulnerability only if player changing worlds
this.connection.send(new ClientboundRespawnPacket(this.createCommonSpawnInfo(worldserver), (byte) 3));
- 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 6add371484deca6ed041e434fea5dc54c8db12d9..0080136d9aaead083fd1d94d2f7a0df250e3d9d8 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3162,7 +3162,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
public void handleChangeDifficulty(ServerboundChangeDifficultyPacket packet) {
PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
if (this.player.hasPermissions(2) || this.isSingleplayerOwner()) {
- this.server.setDifficulty(packet.getDifficulty(), false);
+ // this.server.setDifficulty(packet.getDifficulty(), 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 8d78720a613c089ad1bd2d78b83da8a42b76fc76..2ac9b9001f60e2b4c2b660cc104387e49141bcf5 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -959,8 +959,8 @@ public final class CraftServer implements Server {
org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot
this.console.paperConfigurations.reloadConfigs(this.console);
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)) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 2d9a298e6f364d7ea6fec689833a72b58aba3c17..5fc0c6652f2dff1b41bb447407a1780bd609c6bb 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -1153,7 +1153,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void setDifficulty(Difficulty difficulty) {
- this.getHandle().serverLevelData.setDifficulty(net.minecraft.world.Difficulty.byId(difficulty.getValue()));
+ this.getHandle().getServer().setDifficulty(this.getHandle(), net.minecraft.world.Difficulty.byId(difficulty.getValue()), true); // Paper - don't skip other difficulty-changing logic
}
@Override

View file

@ -1,84 +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/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
index 5bfa245a621a0bf7ef60cd01f4c04576b770384e..a8b41d6a42e23a7cd839cc3d5e5cae84860f802c 100644
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
@@ -41,6 +41,7 @@ public final class PaperCommand extends Command {
commands.put(Set.of("fixlight"), new FixLightCommand());
commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand());
commands.put(Set.of("syncloadinfo"), new SyncLoadInfoCommand());
+ commands.put(Set.of("dumpitem"), new DumpItemCommand());
return commands.entrySet().stream()
.flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue())))
diff --git a/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java b/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f0b0fe73a47e6a5ca8706f11e78b4b08e6ccd9a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java
@@ -0,0 +1,59 @@
+package io.papermc.paper.command.subcommands;
+
+import io.papermc.paper.adventure.PaperAdventure;
+import io.papermc.paper.command.PaperSubcommand;
+import java.util.Objects;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.event.ClickEvent;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.world.item.ItemStack;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.entity.Player;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+import static net.kyori.adventure.text.Component.text;
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
+import static net.kyori.adventure.text.format.TextDecoration.ITALIC;
+
+@DefaultQualifier(NonNull.class)
+public final class DumpItemCommand implements PaperSubcommand {
+ @Override
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
+ this.doDumpItem(sender);
+ return true;
+ }
+
+ private void doDumpItem(final CommandSender sender) {
+ if (!(sender instanceof Player)) {
+ sender.sendMessage("Only players can use this command");
+ return;
+ }
+ final ItemStack itemStack = CraftItemStack.asNMSCopy(((CraftPlayer) sender).getItemInHand());
+ final @Nullable CompoundTag tag = itemStack.getTag();
+ final @Nullable Component nbtComponent = tag == null ? null : PaperAdventure.asAdventure(net.minecraft.nbt.NbtUtils.toPrettyComponent(tag));
+ final String itemId = Objects.requireNonNull(((CraftWorld) ((CraftPlayer) sender).getWorld()).getHandle().registryAccess()
+ .registryOrThrow(Registries.ITEM).getKey(itemStack.getItem())).toString();
+ final Component message = text()
+ .append(text(itemId, YELLOW))
+ .apply(b -> {
+ if (nbtComponent != null) {
+ b.append(nbtComponent);
+ }
+ })
+ .build();
+ Bukkit.getConsoleSender().sendMessage(message);
+ sender.sendMessage(message);
+ sender.sendMessage(text().content(" Click to copy item to clipboard")
+ .color(GRAY)
+ .decorate(ITALIC)
+ .clickEvent(ClickEvent.copyToClipboard(tag == null ? itemId : (itemId + tag))));
+ }
+}

View file

@ -1,56 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 28 Jun 2020 19:08:41 -0400
Subject: [PATCH] Improve Legacy Component serialization size
Don't constantly send format: false for all formatting options when parent already
has it false
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java b/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java
index 19a102792fab30f8f6a7364d0a9b4063d76f1489..7c36d2bd36926c0ae5a0db6a97bd47a85566cf26 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftChatMessage.java
@@ -47,6 +47,7 @@ public final class CraftChatMessage {
// Separate pattern with no group 3, new lines are part of previous string
private static final Pattern INCREMENTAL_PATTERN_KEEP_NEWLINES = Pattern.compile("(" + String.valueOf(org.bukkit.ChatColor.COLOR_CHAR) + "[0-9a-fk-orx])|((?:(?:https?):\\/\\/)?(?:[-\\w_\\.]{2,}\\.[a-z]{2,4}.*?(?=[\\.\\?!,;:]?(?:[" + String.valueOf(org.bukkit.ChatColor.COLOR_CHAR) + " ]|$))))", Pattern.CASE_INSENSITIVE);
// ChatColor.b does not explicitly reset, its more of empty
+ private static final Style EMPTY = Style.EMPTY.withItalic(false); // Paper - OBFHELPER
private static final Style RESET = Style.EMPTY.withBold(false).withItalic(false).withUnderlined(false).withStrikethrough(false).withObfuscated(false);
private final List<Component> list = new ArrayList<Component>();
@@ -68,6 +69,7 @@ public final class CraftChatMessage {
Matcher matcher = (keepNewlines ? StringMessage.INCREMENTAL_PATTERN_KEEP_NEWLINES : StringMessage.INCREMENTAL_PATTERN).matcher(message);
String match = null;
boolean needsAdd = false;
+ boolean hasReset = false; // Paper
while (matcher.find()) {
int groupId = 0;
while ((match = matcher.group(++groupId)) == null) {
@@ -113,7 +115,26 @@ public final class CraftChatMessage {
throw new AssertionError("Unexpected message format");
}
} else { // Color resets formatting
- this.modifier = StringMessage.RESET.withColor(format);
+ // Paper start - improve legacy formatting
+ Style previous = modifier;
+ modifier = (!hasReset ? RESET : EMPTY).withColor(format);
+ hasReset = true;
+ if (previous.isBold()) {
+ modifier = modifier.withBold(false);
+ }
+ if (previous.isItalic()) {
+ modifier = modifier.withItalic(false);
+ }
+ if (previous.isObfuscated()) {
+ modifier = modifier.withObfuscated(false);
+ }
+ if (previous.isStrikethrough()) {
+ modifier = modifier.withStrikethrough(false);
+ }
+ if (previous.isUnderlined()) {
+ modifier = modifier.withUnderlined(false);
+ }
+ // Paper end
}
needsAdd = true;
break;

View file

@ -1,213 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 4 Jun 2020 02:24:49 -0400
Subject: [PATCH] Optimize Bit Operations by inlining
Inline bit operations and reduce instruction count to make these hot
operations faster
diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java
index fbdbf61f49bd6867eea609d5572fb31ece094944..441ea6b9fd55a5288f264472d7297728d0546d6b 100644
--- a/src/main/java/net/minecraft/core/BlockPos.java
+++ b/src/main/java/net/minecraft/core/BlockPos.java
@@ -37,15 +37,16 @@ public class BlockPos extends Vec3i {
}).stable();
private static final Logger LOGGER = LogUtils.getLogger();
public static final BlockPos ZERO = new BlockPos(0, 0, 0);
- private static final int PACKED_X_LENGTH = 1 + Mth.log2(Mth.smallestEncompassingPowerOfTwo(30000000));
- private static final int PACKED_Z_LENGTH = PACKED_X_LENGTH;
- public static final int PACKED_Y_LENGTH = 64 - PACKED_X_LENGTH - PACKED_Z_LENGTH;
- private static final long PACKED_X_MASK = (1L << PACKED_X_LENGTH) - 1L;
- private static final long PACKED_Y_MASK = (1L << PACKED_Y_LENGTH) - 1L;
- private static final long PACKED_Z_MASK = (1L << PACKED_Z_LENGTH) - 1L;
- private static final int Y_OFFSET = 0;
- private static final int Z_OFFSET = PACKED_Y_LENGTH;
- private static final int X_OFFSET = PACKED_Y_LENGTH + PACKED_Z_LENGTH;
+ // Paper start - static constants
+ private static final int PACKED_X_LENGTH = 26;
+ private static final int PACKED_Z_LENGTH = 26;
+ public static final int PACKED_Y_LENGTH = 12;
+ private static final long PACKED_X_MASK = 67108863;
+ private static final long PACKED_Y_MASK = 4095;
+ private static final long PACKED_Z_MASK = 67108863;
+ private static final int Z_OFFSET = 12;
+ private static final int X_OFFSET = 38;
+ // Paper end
public BlockPos(int x, int y, int z) {
super(x, y, z);
@@ -55,28 +56,29 @@ public class BlockPos extends Vec3i {
this(pos.getX(), pos.getY(), pos.getZ());
}
+ public static long getAdjacent(int baseX, int baseY, int baseZ, Direction enumdirection) { return asLong(baseX + enumdirection.getStepX(), baseY + enumdirection.getStepY(), baseZ + enumdirection.getStepZ()); } // Paper
public static long offset(long value, Direction direction) {
return offset(value, direction.getStepX(), direction.getStepY(), direction.getStepZ());
}
public static long offset(long value, int x, int y, int z) {
- return asLong(getX(value) + x, getY(value) + y, getZ(value) + z);
+ return asLong((int) (value >> 38) + x, (int) ((value << 52) >> 52) + y, (int) ((value << 26) >> 38) + z); // Paper - simplify/inline
}
public static int getX(long packedPos) {
- return (int)(packedPos << 64 - X_OFFSET - PACKED_X_LENGTH >> 64 - PACKED_X_LENGTH);
+ return (int) (packedPos >> 38); // Paper - simplify/inline
}
public static int getY(long packedPos) {
- return (int)(packedPos << 64 - PACKED_Y_LENGTH >> 64 - PACKED_Y_LENGTH);
+ return (int) ((packedPos << 52) >> 52); // Paper - simplify/inline
}
public static int getZ(long packedPos) {
- return (int)(packedPos << 64 - Z_OFFSET - PACKED_Z_LENGTH >> 64 - PACKED_Z_LENGTH);
+ return (int) ((packedPos << 26) >> 38); // Paper - simplify/inline
}
public static BlockPos of(long packedPos) {
- return new BlockPos(getX(packedPos), getY(packedPos), getZ(packedPos));
+ return new BlockPos((int) (packedPos >> 38), (int) ((packedPos << 52) >> 52), (int) ((packedPos << 26) >> 38)); // Paper - simplify/inline
}
public static BlockPos containing(double x, double y, double z) {
@@ -92,10 +94,7 @@ public class BlockPos extends Vec3i {
}
public static long asLong(int x, int y, int z) {
- long l = 0L;
- l |= ((long)x & PACKED_X_MASK) << X_OFFSET;
- l |= ((long)y & PACKED_Y_MASK) << 0;
- return l | ((long)z & PACKED_Z_MASK) << Z_OFFSET;
+ return (((long) x & (long) 67108863) << 38) | (((long) y & (long) 4095)) | (((long) z & (long) 67108863) << 12); // Paper - inline constants and simplify
}
public static long getFlatIndex(long y) {
diff --git a/src/main/java/net/minecraft/core/SectionPos.java b/src/main/java/net/minecraft/core/SectionPos.java
index eb97928380384b8c33cf142dd9a5db65951c94c6..392c3681c08d9e3d99cbabaf72d22c1d1518998d 100644
--- a/src/main/java/net/minecraft/core/SectionPos.java
+++ b/src/main/java/net/minecraft/core/SectionPos.java
@@ -38,7 +38,7 @@ public class SectionPos extends Vec3i {
}
public static SectionPos of(BlockPos pos) {
- return new SectionPos(blockToSectionCoord(pos.getX()), blockToSectionCoord(pos.getY()), blockToSectionCoord(pos.getZ()));
+ return new SectionPos(pos.getX() >> 4, pos.getY() >> 4, pos.getZ() >> 4); // Paper
}
public static SectionPos of(ChunkPos chunkPos, int y) {
@@ -54,7 +54,7 @@ public class SectionPos extends Vec3i {
}
public static SectionPos of(long packed) {
- return new SectionPos(x(packed), y(packed), z(packed));
+ return new SectionPos((int) (packed >> 42), (int) (packed << 44 >> 44), (int) (packed << 22 >> 42)); // Paper
}
public static SectionPos bottomOf(ChunkAccess chunk) {
@@ -65,8 +65,16 @@ public class SectionPos extends Vec3i {
return offset(packed, direction.getStepX(), direction.getStepY(), direction.getStepZ());
}
+ // Paper start
+ public static long getAdjacentFromBlockPos(int x, int y, int z, Direction enumdirection) {
+ return (((long) ((x >> 4) + enumdirection.getStepX()) & 4194303L) << 42) | (((long) ((y >> 4) + enumdirection.getStepY()) & 1048575L)) | (((long) ((z >> 4) + enumdirection.getStepZ()) & 4194303L) << 20);
+ }
+ public static long getAdjacentFromSectionPos(int x, int y, int z, Direction enumdirection) {
+ return (((long) (x + enumdirection.getStepX()) & 4194303L) << 42) | (((long) ((y) + enumdirection.getStepY()) & 1048575L)) | (((long) (z + enumdirection.getStepZ()) & 4194303L) << 20);
+ }
+ // Paper end
public static long offset(long packed, int x, int y, int z) {
- return asLong(x(packed) + x, y(packed) + y, z(packed) + z);
+ return (((long) ((int) (packed >> 42) + x) & 4194303L) << 42) | (((long) ((int) (packed << 44 >> 44) + y) & 1048575L)) | (((long) ((int) (packed << 22 >> 42) + z) & 4194303L) << 20); // Simplify to reduce instruction count
}
public static int posToSectionCoord(double coord) {
@@ -86,10 +94,7 @@ public class SectionPos extends Vec3i {
}
public static short sectionRelativePos(BlockPos pos) {
- int i = sectionRelative(pos.getX());
- int j = sectionRelative(pos.getY());
- int k = sectionRelative(pos.getZ());
- return (short)(i << 8 | k << 4 | j << 0);
+ return (short) ((pos.getX() & 15) << 8 | (pos.getZ() & 15) << 4 | pos.getY() & 15); // Paper - simplify/inline
}
public static int sectionRelativeX(short packedLocalPos) {
@@ -152,16 +157,16 @@ public class SectionPos extends Vec3i {
return this.getZ();
}
- public int minBlockX() {
- return sectionToBlockCoord(this.x());
+ public final int minBlockX() { // Paper - make final
+ return this.getX() << 4; // Paper - inline
}
- public int minBlockY() {
- return sectionToBlockCoord(this.y());
+ public final int minBlockY() { // Paper - make final
+ return this.getY() << 4; // Paper - inline
}
- public int minBlockZ() {
- return sectionToBlockCoord(this.z());
+ public int minBlockZ() { // Paper - make final
+ return this.getZ() << 4; // Paper - inline
}
public int maxBlockX() {
@@ -177,7 +182,8 @@ public class SectionPos extends Vec3i {
}
public static long blockToSection(long blockPos) {
- return asLong(blockToSectionCoord(BlockPos.getX(blockPos)), blockToSectionCoord(BlockPos.getY(blockPos)), blockToSectionCoord(BlockPos.getZ(blockPos)));
+ // b(a(BlockPosition.b(i)), a(BlockPosition.c(i)), a(BlockPosition.d(i)));
+ return (((long) (int) (blockPos >> 42) & 4194303L) << 42) | (((long) (int) ((blockPos << 52) >> 56) & 1048575L)) | (((long) (int) ((blockPos << 26) >> 42) & 4194303L) << 20); // Simplify to reduce instruction count
}
public static long getZeroNode(int x, int z) {
@@ -205,15 +211,18 @@ public class SectionPos extends Vec3i {
return asLong(blockToSectionCoord(pos.getX()), blockToSectionCoord(pos.getY()), blockToSectionCoord(pos.getZ()));
}
+ // Paper start
+ public static long blockPosAsSectionLong(int i, int j, int k) {
+ return (((long) (i >> 4) & 4194303L) << 42) | (((long) (j >> 4) & 1048575L)) | (((long) (k >> 4) & 4194303L) << 20);
+ }
+ // Paper end
+
public static long asLong(int x, int y, int z) {
- long l = 0L;
- l |= ((long)x & 4194303L) << 42;
- l |= ((long)y & 1048575L) << 0;
- return l | ((long)z & 4194303L) << 20;
+ return (((long) x & 4194303L) << 42) | (((long) y & 1048575L)) | (((long) z & 4194303L) << 20); // Paper - Simplify to reduce instruction count
}
public long asLong() {
- return asLong(this.x(), this.y(), this.z());
+ return (((long) getX() & 4194303L) << 42) | (((long) getY() & 1048575L)) | (((long) getZ() & 4194303L) << 20); // Paper - Simplify to reduce instruction count
}
@Override
@@ -226,16 +235,11 @@ public class SectionPos extends Vec3i {
}
public static Stream<SectionPos> cube(SectionPos center, int radius) {
- int i = center.x();
- int j = center.y();
- int k = center.z();
- return betweenClosedStream(i - radius, j - radius, k - radius, i + radius, j + radius, k + radius);
+ return betweenClosedStream(center.getX() - radius, center.getY() - radius, center.getZ() - radius, center.getX() + radius, center.getY() + radius, center.getZ() + radius); // Paper - simplify/inline
}
public static Stream<SectionPos> aroundChunk(ChunkPos center, int radius, int minY, int maxY) {
- int i = center.x;
- int j = center.z;
- return betweenClosedStream(i - radius, minY, j - radius, i + radius, maxY - 1, j + radius);
+ return betweenClosedStream(center.x - radius, 0, center.z - radius, center.x + radius, 15, center.z + radius); // Paper - simplify/inline
}
public static Stream<SectionPos> betweenClosedStream(final int minX, final int minY, final int minZ, final int maxX, final int maxY, final int maxZ) {

View file

@ -1,126 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Tue, 9 Jun 2020 03:33:03 -0400
Subject: [PATCH] Add Plugin Tickets to API Chunk Methods
Like previous versions, plugins loading chunks kept them loaded until
they garbage collected to avoid constant spamming of chunk loads
This adds tickets to a few more places so that they can be unloaded.
Additionally, this drops their ticket level to BORDER so they wont be ticking
so they will just sit inactive instead.
Using .loadChunk to keep a chunk ticking was a horrible idea for upstream
when we have TWO methods that are able to do that already in the API.
Also reduce their collection count down to a maximum of 1 second. Barely
anyone knows what chunk-gc is in bukkit.yml as its less relevant now, and
since this wasn't spigot behavior, this is safe to mostly ignore (unless someone
wants it to collect even faster, they can restore that setting back to 1 instead of 20+)
Not adding it to .getType() though to keep behavior consistent with vanilla for performance reasons.
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 2ac9b9001f60e2b4c2b660cc104387e49141bcf5..c71a409a457deea6817b863ddbb8bcbf64206544 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -386,7 +386,7 @@ public final class CraftServer implements Server {
this.overrideSpawnLimits();
console.autosavePeriod = this.configuration.getInt("ticks-per.autosave");
this.warningState = WarningState.value(this.configuration.getString("settings.deprecated-verbose"));
- TicketType.PLUGIN.timeout = this.configuration.getInt("chunk-gc.period-in-ticks");
+ TicketType.PLUGIN.timeout = Math.min(20, this.configuration.getInt("chunk-gc.period-in-ticks")); // Paper - cap plugin loads to 1 second
this.minimumAPI = this.configuration.getString("settings.minimum-api");
this.loadIcon();
@@ -939,7 +939,7 @@ public final class CraftServer implements Server {
this.console.setMotd(config.motd);
this.overrideSpawnLimits();
this.warningState = WarningState.value(this.configuration.getString("settings.deprecated-verbose"));
- TicketType.PLUGIN.timeout = this.configuration.getInt("chunk-gc.period-in-ticks");
+ TicketType.PLUGIN.timeout = Math.min(20, configuration.getInt("chunk-gc.period-in-ticks")); // Paper - cap plugin loads to 1 second
this.minimumAPI = this.configuration.getString("settings.minimum-api");
this.printSaveWarning = false;
this.console.autosavePeriod = this.configuration.getInt("ticks-per.autosave");
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 5fc0c6652f2dff1b41bb447407a1780bd609c6bb..8306f8cce689260111fbf88b31515440a44d3a8c 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -282,7 +282,13 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public Chunk getChunkAt(int x, int z) {
- net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) this.world.getChunk(x, z, ChunkStatus.FULL, true);
+ // Paper start - add ticket to hold chunk for a little while longer if plugin accesses it
+ net.minecraft.world.level.chunk.LevelChunk chunk = this.world.getChunkSource().getChunkAtIfLoadedImmediately(x, z);
+ if (chunk == null) {
+ this.addTicket(x, z);
+ chunk = this.world.getChunkSource().getChunk(x, z, true);
+ }
+ // Paper end
return new CraftChunk(chunk);
}
@@ -296,6 +302,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return new CraftChunk(this.getHandle(), x, z);
}
+ // Paper start
+ private void addTicket(int x, int z) {
+ io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE)); // Paper
+ }
+ // Paper end
+
@Override
public Chunk getChunkAt(Block block) {
Preconditions.checkArgument(block != null, "null block");
@@ -361,7 +373,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
public boolean unloadChunkRequest(int x, int z) {
org.spigotmc.AsyncCatcher.catchOp("chunk unload"); // Spigot
if (this.isChunkLoaded(x, z)) {
- this.world.getChunkSource().removeRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 1, Unit.INSTANCE);
+ this.world.getChunkSource().removeRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 0, Unit.INSTANCE); // Paper
}
return true;
@@ -447,9 +459,12 @@ public class CraftWorld extends CraftRegionAccessor implements World {
org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
// Paper start - Optimize this method
ChunkPos chunkPos = new ChunkPos(x, z);
+ ChunkAccess immediate = world.getChunkSource().getChunkAtIfLoadedImmediately(x, z); // Paper
+ if (immediate != null) return true; // Paper
if (!generate) {
- ChunkAccess immediate = world.getChunkSource().getChunkAtImmediately(x, z);
+
+ //IChunkAccess immediate = world.getChunkProvider().getChunkAtImmediately(x, z); // Paper
if (immediate == null) {
immediate = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
}
@@ -457,7 +472,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
if (!(immediate instanceof ImposterProtoChunk) && !(immediate instanceof net.minecraft.world.level.chunk.LevelChunk)) {
return false; // not full status
}
- world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE);
+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 0, Unit.INSTANCE); // Paper
world.getChunk(x, z); // make sure we're at ticket level 32 or lower
return true;
}
@@ -483,7 +498,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
// we do this so we do not re-read the chunk data on disk
}
- world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE);
+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 0, Unit.INSTANCE); // Paper
world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
return true;
// Paper end
@@ -2263,6 +2278,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
io.papermc.paper.chunk.system.ChunkSystem.scheduleChunkLoad(this.getHandle(), x, z, gen, ChunkStatus.FULL, true, priority, (c) -> {
net.minecraft.server.MinecraftServer.getServer().scheduleOnMain(() -> {
net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk)c;
+ if (chunk != null) this.addTicket(x, z); // Paper
ret.complete(chunk == null ? null : new CraftChunk(chunk));
});
});