even more work

This commit is contained in:
Jake Potrebic 2023-09-21 19:17:21 -07:00
parent ef6a41195d
commit 7b29d1f4c5
No known key found for this signature in database
GPG key ID: ECE0B3C133C016C5
46 changed files with 169 additions and 185 deletions

View file

@ -5832,7 +5832,7 @@ index dcbbbf1d59a60abfc731d9e422c9569e8460d985..5250bf5029825524c239854db7999883
+ // Paper end
}
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 6dc7999e0cc08efdbffafd80437f31ed590189a9..f2e5f3170a4a54e99a1dab40eb8a5c99ce548adc 100644
index 6dc7999e0cc08efdbffafd80437f31ed590189a9..45a9977abb9abe78dafed455abaa022dfadb1153 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -167,6 +167,56 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@ -6056,17 +6056,15 @@ index 6dc7999e0cc08efdbffafd80437f31ed590189a9..f2e5f3170a4a54e99a1dab40eb8a5c99
} else {
SectionPos sectionposition = player.getLastSectionPos();
@@ -1190,6 +1267,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1190,6 +1267,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.distanceManager.removePlayer(sectionposition, player);
}
+ this.removePlayerFromDistanceMaps(player); // Paper - distance maps
+ }
+
this.applyChunkTrackingView(player, ChunkTrackingView.EMPTY);
}
@@ -1241,6 +1321,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1241,6 +1319,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
this.updateChunkTracking(player);
}
@ -6074,7 +6072,7 @@ index 6dc7999e0cc08efdbffafd80437f31ed590189a9..f2e5f3170a4a54e99a1dab40eb8a5c99
}
private void updateChunkTracking(ServerPlayer player) {
@@ -1493,7 +1574,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1493,7 +1572,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
private class ChunkDistanceManager extends DistanceManager {
protected ChunkDistanceManager(Executor workerExecutor, Executor mainThreadExecutor) {

View file

@ -1010,7 +1010,7 @@ index ff3f00cf1d5180f83b16acac5676aa22cd967c8a..5aa21689e308842fe5b64689265ba45a
// CraftBukkit end
}
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index f2e5f3170a4a54e99a1dab40eb8a5c99ce548adc..c4215430a06983fbedfd21a568d79381b95696c3 100644
index 45a9977abb9abe78dafed455abaa022dfadb1153..9932faa95dcc4f104718dbc57d36180c2bbbcb93 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1,8 +1,10 @@
@ -1040,7 +1040,7 @@ index f2e5f3170a4a54e99a1dab40eb8a5c99ce548adc..c4215430a06983fbedfd21a568d79381
});
}, (runnable) -> {
ProcessorHandle mailbox = this.mainThreadMailbox;
@@ -1459,6 +1463,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1457,6 +1461,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
List<ServerPlayer> list = Lists.newArrayList();
List<ServerPlayer> list1 = this.level.players();
ObjectIterator objectiterator = this.entityMap.values().iterator();
@ -1048,7 +1048,7 @@ index f2e5f3170a4a54e99a1dab40eb8a5c99ce548adc..c4215430a06983fbedfd21a568d79381
ChunkMap.TrackedEntity playerchunkmap_entitytracker;
@@ -1483,14 +1488,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1481,14 +1486,17 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
playerchunkmap_entitytracker.serverEntity.sendChanges();
}
}

View file

@ -4493,7 +4493,7 @@ index 5250bf5029825524c239854db7999883459f1a8a..c5cd2c2c28795593712b0de2bbaa7e38
private final DebugBuffer<ChunkHolder.ChunkSaveDebug> chunkToSaveHistory;
public int oldTicketLevel;
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index c4215430a06983fbedfd21a568d79381b95696c3..17182c7f130853b4b5d942e7d60adaded14f84f6 100644
index 9932faa95dcc4f104718dbc57d36180c2bbbcb93..4c0eb0dbb5caff7944e7bdde803a9eb66d7631be 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -124,7 +124,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider

View file

@ -9,7 +9,7 @@ from triggering monster spawns on a server.
Also a highly more effecient way to blanket block spawns in a world
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 17182c7f130853b4b5d942e7d60adaded14f84f6..425a0baa9321e9b484814f38fe28de3c82333443 100644
index 4c0eb0dbb5caff7944e7bdde803a9eb66d7631be..6352e3f0a78822b4d5167a688a5230b599541159 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1193,7 +1193,9 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider

View file

@ -8,10 +8,10 @@ Add -Ddebug.entities=true to your JVM flags to gain more information
1.17: Needs to be reworked for new entity storage system
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 425a0baa9321e9b484814f38fe28de3c82333443..65916397278ea0cbbc33a46e501a4b6979e6d4ef 100644
index 6352e3f0a78822b4d5167a688a5230b599541159..62c36060989acb54eb12a145909a68193c5e3035 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1417,6 +1417,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1415,6 +1415,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
} else {
ChunkMap.TrackedEntity playerchunkmap_entitytracker = new ChunkMap.TrackedEntity(entity, i, j, entitytypes.trackDeltas());
@ -19,7 +19,7 @@ index 425a0baa9321e9b484814f38fe28de3c82333443..65916397278ea0cbbc33a46e501a4b69
this.entityMap.put(entity.getId(), playerchunkmap_entitytracker);
playerchunkmap_entitytracker.updatePlayers(this.level.players());
if (entity instanceof ServerPlayer) {
@@ -1459,7 +1460,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
@@ -1457,7 +1458,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
if (playerchunkmap_entitytracker1 != null) {
playerchunkmap_entitytracker1.broadcastRemoved();
}

View file

@ -39,10 +39,10 @@ public net.minecraft.server.network.ServerLoginPacketListenerImpl state
public net.minecraft.server.network.ServerLoginPacketListenerImpl gameProfile
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index ff97e47cb248ade6530e52583b0b3865d77ace15..ed89f2f96088c5c05fd42a6795e6e6e342a0ab54 100644
index ff97e47cb248ade6530e52583b0b3865d77ace15..62df5c51fa11f85ec2fad20c0ea5aa6241294e8a 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -632,6 +632,27 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@@ -632,6 +632,26 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
packetlistener1.onDisconnect(ichatbasecomponent);
}
this.pendingActions.clear(); // Free up packet queue.
@ -66,7 +66,6 @@ index ff97e47cb248ade6530e52583b0b3865d77ace15..ed89f2f96088c5c05fd42a6795e6e6e3
+ }
+ }
+ // Paper end
+ }
}
}

View file

@ -0,0 +1,208 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 19 Apr 2020 18:15:29 -0400
Subject: [PATCH] Implement Brigadier Mojang API
Adds AsyncPlayerSendCommandsEvent
- Allows modifying on a per command basis what command data they see.
Adds CommandRegisteredEvent
- Allows manipulating the CommandNode to add more children/metadata for the client
diff --git a/build.gradle.kts b/build.gradle.kts
index 7cb7a4024f53c50d88652cb6c497d815ed67c148..7f005b9a41064f1929e8d9cb53c5ab207d3a333d 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -14,6 +14,7 @@ val alsoShade: Configuration by configurations.creating
dependencies {
implementation(project(":paper-api"))
+ implementation(project(":paper-mojangapi"))
// Paper start
implementation("org.jline:jline-terminal-jansi:3.21.0")
implementation("net.minecrell:terminalconsoleappender:1.3.0")
diff --git a/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java b/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java
index 3370731ee064d2693b972a0765c13dd4fd69f66a..614eba6cc55d1eb7755cac35c671cb6f6cacca13 100644
--- a/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java
+++ b/src/main/java/com/mojang/brigadier/exceptions/CommandSyntaxException.java
@@ -5,7 +5,7 @@ package com.mojang.brigadier.exceptions;
import com.mojang.brigadier.Message;
-public class CommandSyntaxException extends Exception {
+public class CommandSyntaxException extends Exception implements net.kyori.adventure.util.ComponentMessageThrowable { // Paper
public static final int CONTEXT_AMOUNT = 10;
public static boolean ENABLE_COMMAND_STACK_TRACES = true;
public static BuiltInExceptionProvider BUILT_IN_EXCEPTIONS = new BuiltInExceptions();
@@ -73,4 +73,11 @@ public class CommandSyntaxException extends Exception {
public int getCursor() {
return cursor;
}
+
+ // Paper start
+ @Override
+ public @org.jetbrains.annotations.Nullable net.kyori.adventure.text.Component componentMessage() {
+ return io.papermc.paper.brigadier.PaperBrigadier.componentFromMessage(this.message);
+ }
+ // Paper end
}
diff --git a/src/main/java/com/mojang/brigadier/tree/CommandNode.java b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
index da6250df1c5f3385b683cffde47754bca4606f5e..3384501f83d445f45aa8233e98c7597daa67b8ef 100644
--- a/src/main/java/com/mojang/brigadier/tree/CommandNode.java
+++ b/src/main/java/com/mojang/brigadier/tree/CommandNode.java
@@ -34,6 +34,7 @@ public abstract class CommandNode<S> implements Comparable<CommandNode<S>> {
private final RedirectModifier<S> modifier;
private final boolean forks;
private Command<S> command;
+ public LiteralCommandNode<CommandSourceStack> clientNode = null; // Paper
// CraftBukkit start
public void removeCommand(String name) {
this.children.remove(name);
diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java
index 902671a335da23d3945c363afc8abde6f5a1d444..e6bf044a0de278c568835e6150ce07ed0e39a44e 100644
--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java
+++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java
@@ -43,7 +43,7 @@ import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import com.mojang.brigadier.tree.CommandNode; // CraftBukkit
-public class CommandSourceStack implements SharedSuggestionProvider {
+public class CommandSourceStack implements SharedSuggestionProvider, com.destroystokyo.paper.brigadier.BukkitBrigadierCommandSource { // Paper
public static final SimpleCommandExceptionType ERROR_NOT_PLAYER = new SimpleCommandExceptionType(Component.translatable("permissions.requires.player"));
public static final SimpleCommandExceptionType ERROR_NOT_ENTITY = new SimpleCommandExceptionType(Component.translatable("permissions.requires.entity"));
@@ -176,6 +176,26 @@ public class CommandSourceStack implements SharedSuggestionProvider {
return this.textName;
}
+ // Paper start
+ @Override
+ public org.bukkit.entity.Entity getBukkitEntity() {
+ return getEntity() != null ? getEntity().getBukkitEntity() : null;
+ }
+
+ @Override
+ public org.bukkit.World getBukkitWorld() {
+ return getLevel() != null ? getLevel().getWorld() : null;
+ }
+
+ @Override
+ public org.bukkit.Location getBukkitLocation() {
+ Vec3 pos = getPosition();
+ org.bukkit.World world = getBukkitWorld();
+ Vec2 rot = getRotation();
+ return world != null && pos != null ? new org.bukkit.Location(world, pos.x, pos.y, pos.z, rot != null ? rot.y : 0, rot != null ? rot.x : 0) : null;
+ }
+ // Paper end
+
@Override
public boolean hasPermission(int level) {
// CraftBukkit start
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
index bbf4db5faf4694e78e01f4e74e277b46a02f7afd..148cfe9eac43cafee016d58a1344a97a2e69aa77 100644
--- a/src/main/java/net/minecraft/commands/Commands.java
+++ b/src/main/java/net/minecraft/commands/Commands.java
@@ -440,6 +440,7 @@ public class Commands {
bukkit.add(node.getName());
}
// Paper start - Async command map building
+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper
net.minecraft.server.MinecraftServer.getServer().execute(() -> {
runSync(player, bukkit, rootcommandnode);
});
@@ -447,6 +448,7 @@ public class Commands {
private void runSync(ServerPlayer player, Collection<String> bukkit, RootCommandNode<SharedSuggestionProvider> rootcommandnode) {
// Paper end - Async command map building
+ new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent<CommandSourceStack>(player.getBukkitEntity(), (RootCommandNode) rootcommandnode, false).callEvent(); // Paper
PlayerCommandSendEvent event = new PlayerCommandSendEvent(player.getBukkitEntity(), new LinkedHashSet<>(bukkit));
event.getPlayer().getServer().getPluginManager().callEvent(event);
@@ -465,6 +467,11 @@ public class Commands {
while (iterator.hasNext()) {
CommandNode<CommandSourceStack> commandnode2 = (CommandNode) iterator.next();
+ // Paper start
+ if (commandnode2.clientNode != null) {
+ commandnode2 = commandnode2.clientNode;
+ }
+ // Paper end
if ( !org.spigotmc.SpigotConfig.sendNamespaced && commandnode2.getName().contains( ":" ) ) continue; // Spigot
if (commandnode2.canUse(source)) {
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 08af9c32f8cb40e1e94cfb5c42ee113e8765db86..aed6d2420afeb404b5542f19d7424b6c14d0b636 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -732,8 +732,12 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
ParseResults<CommandSourceStack> parseresults = this.server.getCommands().getDispatcher().parse(stringreader, this.player.createCommandSourceStack());
this.server.getCommands().getDispatcher().getCompletionSuggestions(parseresults).thenAccept((suggestions) -> {
- if (suggestions.isEmpty()) return; // CraftBukkit - don't send through empty suggestions - prevents [<args>] from showing for plugins with nothing more to offer
- this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestions));
+ // Paper start - Brigadier API
+ com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, command);
+ suggestEvent.setCancelled(suggestions.isEmpty());
+ if (!suggestEvent.callEvent()) return;
+ this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestEvent.getSuggestions()));
+ // Paper end - Brigadier API
});
});
}
@@ -748,7 +752,13 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
builder.suggest(completion.suggestion(), PaperAdventure.asVanilla(completion.tooltip()));
}
});
- player.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), builder.buildFuture().join()));
+ // Paper start - Brigadier API
+ com.mojang.brigadier.suggestion.Suggestions suggestions = builder.buildFuture().join();
+ com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent suggestEvent = new com.destroystokyo.paper.event.brigadier.AsyncPlayerSendSuggestionsEvent(this.getCraftPlayer(), suggestions, command);
+ suggestEvent.setCancelled(suggestions.isEmpty());
+ if (!suggestEvent.callEvent()) return;
+ this.connection.send(new ClientboundCommandSuggestionsPacket(packet.getId(), suggestEvent.getSuggestions()));
+ // Paper end - Brigadier API
}
});
// Paper end - async tab completion
diff --git a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
index 83d81b9371902b0302d13e53b31c15fac4e67966..d113e54a30db16e2ad955170df6030d15de530d6 100644
--- a/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/BukkitCommandWrapper.java
@@ -20,7 +20,7 @@ import org.bukkit.command.CommandException;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.CraftServer;
-public class BukkitCommandWrapper implements com.mojang.brigadier.Command<CommandSourceStack>, Predicate<CommandSourceStack>, SuggestionProvider<CommandSourceStack> {
+public class BukkitCommandWrapper implements com.mojang.brigadier.Command<CommandSourceStack>, Predicate<CommandSourceStack>, SuggestionProvider<CommandSourceStack>, com.destroystokyo.paper.brigadier.BukkitBrigadierCommand<CommandSourceStack> { // Paper
private final CraftServer server;
private final Command command;
@@ -31,10 +31,24 @@ public class BukkitCommandWrapper implements com.mojang.brigadier.Command<Comman
}
public LiteralCommandNode<CommandSourceStack> register(CommandDispatcher<CommandSourceStack> dispatcher, String label) {
- return dispatcher.register(
- LiteralArgumentBuilder.<CommandSourceStack>literal(label).requires(this).executes(this)
- .then(RequiredArgumentBuilder.<CommandSourceStack, String>argument("args", StringArgumentType.greedyString()).suggests(this).executes(this))
- );
+ // Paper start - Expose Brigadier to Paper-MojangAPI
+ com.mojang.brigadier.tree.RootCommandNode<CommandSourceStack> root = dispatcher.getRoot();
+ LiteralCommandNode<CommandSourceStack> literal = LiteralArgumentBuilder.<CommandSourceStack>literal(label).requires(this).executes(this).build();
+ LiteralCommandNode<CommandSourceStack> defaultNode = literal;
+ com.mojang.brigadier.tree.ArgumentCommandNode<CommandSourceStack, String> defaultArgs = RequiredArgumentBuilder.<CommandSourceStack, String>argument("args", StringArgumentType.greedyString()).suggests(this).executes(this).build();
+ literal.addChild(defaultArgs);
+ com.destroystokyo.paper.event.brigadier.CommandRegisteredEvent<CommandSourceStack> event = new com.destroystokyo.paper.event.brigadier.CommandRegisteredEvent<>(label, this, this.command, root, literal, defaultArgs);
+ if (!event.callEvent()) {
+ return null;
+ }
+ literal = event.getLiteral();
+ if (event.isRawCommand()) {
+ defaultNode.clientNode = literal;
+ literal = defaultNode;
+ }
+ root.addChild(literal);
+ return literal;
+ // Paper end
}
@Override

View file

@ -0,0 +1,383 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 25 Jun 2023 23:10:14 -0700
Subject: [PATCH] Improve exact choice recipe ingredients
Fixes exact choices not working with recipe book clicks
and shapeless recipes.
== AT ==
public net.minecraft.world.item.ItemStackLinkedSet TYPE_AND_TAG
public net.minecraft.world.entity.player.StackedContents put(II)V
diff --git a/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java b/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java
new file mode 100644
index 0000000000000000000000000000000000000000..2a2f8327a5bd3983a3a13fd663beb98906f27312
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/recipe/RecipeBookExactChoiceRecipe.java
@@ -0,0 +1,30 @@
+package io.papermc.paper.inventory.recipe;
+
+import net.minecraft.world.Container;
+import net.minecraft.world.item.crafting.Ingredient;
+import net.minecraft.world.item.crafting.Recipe;
+
+public abstract class RecipeBookExactChoiceRecipe<C extends Container> implements Recipe<C> {
+
+ private boolean hasExactIngredients;
+
+ protected final void checkExactIngredients() {
+ // skip any special recipes
+ if (this.isSpecial()) {
+ this.hasExactIngredients = false;
+ return;
+ }
+ for (final Ingredient ingredient : this.getIngredients()) {
+ if (!ingredient.isEmpty() && ingredient.exact) {
+ this.hasExactIngredients = true;
+ return;
+ }
+ }
+ this.hasExactIngredients = false;
+ }
+
+ @Override
+ public final boolean hasExactIngredients() {
+ return this.hasExactIngredients;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..63db0b843c5bd11f979e613ba6cfac9d9da956bb
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/recipe/StackedContentsExtraMap.java
@@ -0,0 +1,79 @@
+package io.papermc.paper.inventory.recipe;
+
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntComparators;
+import it.unimi.dsi.fastutil.ints.IntList;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.world.entity.player.StackedContents;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.ItemStackLinkedSet;
+import net.minecraft.world.item.crafting.Ingredient;
+import net.minecraft.world.item.crafting.Recipe;
+
+public final class StackedContentsExtraMap {
+
+ private final AtomicInteger idCounter = new AtomicInteger(BuiltInRegistries.ITEM.size()); // start at max vanilla stacked contents idx
+ private final Object2IntMap<ItemStack> exactChoiceIds = new Object2IntOpenCustomHashMap<>(ItemStackLinkedSet.TYPE_AND_TAG);
+ private final Int2ObjectMap<ItemStack> idToExactChoice = new Int2ObjectOpenHashMap<>();
+ private final StackedContents contents;
+ public final Map<Ingredient, IntList> extraStackingIds = new IdentityHashMap<>();
+
+ public StackedContentsExtraMap(final StackedContents contents, final Recipe<?> recipe) {
+ this.exactChoiceIds.defaultReturnValue(-1);
+ this.contents = contents;
+ this.initialize(recipe);
+ }
+
+ private void initialize(final Recipe<?> recipe) {
+ if (recipe.hasExactIngredients()) {
+ for (final Ingredient ingredient : recipe.getIngredients()) {
+ if (!ingredient.isEmpty() && ingredient.exact) {
+ final net.minecraft.world.item.ItemStack[] items = ingredient.getItems();
+ final IntList idList = new IntArrayList(items.length);
+ for (final ItemStack item : items) {
+ idList.add(this.registerExact(item)); // I think not copying the stack here is safe because cb copies the stack when creating the ingredient
+ if (!item.hasTag()) {
+ // add regular index if it's a plain itemstack but still registered as exact
+ idList.add(StackedContents.getStackingIndex(item));
+ }
+ }
+ idList.sort(IntComparators.NATURAL_COMPARATOR);
+ this.extraStackingIds.put(ingredient, idList);
+ }
+ }
+ }
+ }
+
+ private int registerExact(final ItemStack exactChoice) {
+ final int existing = this.exactChoiceIds.getInt(exactChoice);
+ if (existing > -1) {
+ return existing;
+ }
+ final int id = this.idCounter.getAndIncrement();
+ this.exactChoiceIds.put(exactChoice, id);
+ this.idToExactChoice.put(id, exactChoice);
+ return id;
+ }
+
+ public ItemStack getById(int id) {
+ return this.idToExactChoice.get(id);
+ }
+
+ public boolean accountStack(final ItemStack stack, final int count) {
+ if (!this.exactChoiceIds.isEmpty()) {
+ final int id = this.exactChoiceIds.getInt(stack);
+ if (id >= 0) {
+ this.contents.put(id, count);
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/inventory/recipe/package-info.java b/src/main/java/io/papermc/paper/inventory/recipe/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..413dfa52760db393ad6a8b5341200ee704a864fc
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/recipe/package-info.java
@@ -0,0 +1,5 @@
+@DefaultQualifier(NonNull.class)
+package io.papermc.paper.inventory.recipe;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
diff --git a/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java b/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java
index 4303f2b4e55191f8a53170435c6d1263782d1c8d..f5440ae9bbce231de06cf184fd0a8490cebfafde 100644
--- a/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java
+++ b/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java
@@ -34,6 +34,7 @@ public class ServerPlaceRecipe<C extends Container> implements PlaceRecipe<Integ
this.inventory = entity.getInventory();
if (this.testClearGrid() || entity.isCreative()) {
this.stackedContents.clear();
+ this.stackedContents.initialize(recipe); // Paper - better exact choice recipes
entity.getInventory().fillStackedContents(this.stackedContents);
this.menu.fillCraftSlotsStackedContents(this.stackedContents);
if (this.stackedContents.canCraft(recipe.value(), (IntList)null)) {
@@ -80,7 +81,7 @@ public class ServerPlaceRecipe<C extends Container> implements PlaceRecipe<Integ
int l = k;
for(int m : intList) {
- int n = StackedContents.fromStackingIndex(m).getMaxStackSize();
+ int n = StackedContents.maxStackSizeFromStackingIndex(m, this.stackedContents); // Paper
if (n < l) {
l = n;
}
@@ -97,10 +98,21 @@ public class ServerPlaceRecipe<C extends Container> implements PlaceRecipe<Integ
@Override
public void addItemToSlot(Iterator<Integer> inputs, int slot, int amount, int gridX, int gridY) {
Slot slot2 = this.menu.getSlot(slot);
- ItemStack itemStack = StackedContents.fromStackingIndex(inputs.next());
+ // Paper start
+ final int itemId = inputs.next();
+ ItemStack itemStack = null;
+ boolean isExact = false;
+ if (this.stackedContents.extrasMap != null && itemId >= net.minecraft.core.registries.BuiltInRegistries.ITEM.size()) {
+ itemStack = StackedContents.fromStackingIndexExtras(itemId, this.stackedContents.extrasMap).copy();
+ isExact = true;
+ }
+ if (itemStack == null) {
+ itemStack = StackedContents.fromStackingIndex(itemId);
+ }
+ // Paper end
if (!itemStack.isEmpty()) {
for(int i = 0; i < amount; ++i) {
- this.moveItemToGrid(slot2, itemStack);
+ this.moveItemToGrid(slot2, itemStack, isExact); // Paper
}
}
@@ -130,8 +142,14 @@ public class ServerPlaceRecipe<C extends Container> implements PlaceRecipe<Integ
return i;
}
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper
protected void moveItemToGrid(Slot slot, ItemStack stack) {
- int i = this.inventory.findSlotMatchingUnusedItem(stack);
+ // Paper start
+ this.moveItemToGrid(slot, stack, false);
+ }
+ protected void moveItemToGrid(Slot slot, ItemStack stack, final boolean isExact) {
+ int i = isExact ? this.inventory.findSlotMatchingItem(stack) : this.inventory.findSlotMatchingUnusedItem(stack);
+ // Paper end
if (i != -1) {
ItemStack itemStack = this.inventory.getItem(i);
if (!itemStack.isEmpty()) {
diff --git a/src/main/java/net/minecraft/world/entity/player/StackedContents.java b/src/main/java/net/minecraft/world/entity/player/StackedContents.java
index 5cc5f67284ad01c9154876252450552ed37f24bf..26b236a764177ac16d53f5cbaf83d3e21d015ebc 100644
--- a/src/main/java/net/minecraft/world/entity/player/StackedContents.java
+++ b/src/main/java/net/minecraft/world/entity/player/StackedContents.java
@@ -21,8 +21,10 @@ import net.minecraft.world.item.crafting.RecipeHolder;
public class StackedContents {
private static final int EMPTY = 0;
public final Int2IntMap contents = new Int2IntOpenHashMap();
+ @Nullable public io.papermc.paper.inventory.recipe.StackedContentsExtraMap extrasMap = null; // Paper
public void accountSimpleStack(ItemStack stack) {
+ if (this.extrasMap != null && stack.hasTag() && this.extrasMap.accountStack(stack, Math.min(64, stack.getCount()))) return; // Paper - max of 64 due to accountStack method below
if (!stack.isDamaged() && !stack.isEnchanted() && !stack.hasCustomHoverName()) {
this.accountStack(stack);
}
@@ -37,6 +39,7 @@ public class StackedContents {
if (!stack.isEmpty()) {
int i = getStackingIndex(stack);
int j = Math.min(maxCount, stack.getCount());
+ if (this.extrasMap != null && stack.hasTag() && this.extrasMap.accountStack(stack, j)) return; // Paper - if an exact ingredient, don't include it
this.put(i, j);
}
@@ -84,6 +87,23 @@ public class StackedContents {
return itemId == 0 ? ItemStack.EMPTY : new ItemStack(Item.byId(itemId));
}
+ // Paper start
+ public void initialize(final Recipe<?> recipe) {
+ this.extrasMap = new io.papermc.paper.inventory.recipe.StackedContentsExtraMap(this, recipe);
+ }
+
+ public static int maxStackSizeFromStackingIndex(final int itemId, @Nullable final StackedContents contents) {
+ if (contents != null && contents.extrasMap != null && itemId >= BuiltInRegistries.ITEM.size()) {
+ return fromStackingIndexExtras(itemId, contents.extrasMap).getMaxStackSize();
+ }
+ return fromStackingIndex(itemId).getMaxStackSize();
+ }
+
+ public static ItemStack fromStackingIndexExtras(final int itemId, final io.papermc.paper.inventory.recipe.StackedContentsExtraMap extrasMap) {
+ return extrasMap.getById(itemId).copy();
+ }
+ // Paper end
+
public void clear() {
this.contents.clear();
}
@@ -107,7 +127,7 @@ public class StackedContents {
this.data = new BitSet(this.ingredientCount + this.itemCount + this.ingredientCount + this.ingredientCount * this.itemCount);
for(int i = 0; i < this.ingredients.size(); ++i) {
- IntList intList = this.ingredients.get(i).getStackingIds();
+ IntList intList = this.getStackingIds(this.ingredients.get(i)); // Paper
for(int j = 0; j < this.itemCount; ++j) {
if (intList.contains(this.items[j])) {
@@ -171,7 +191,7 @@ public class StackedContents {
IntCollection intCollection = new IntAVLTreeSet();
for(Ingredient ingredient : this.ingredients) {
- intCollection.addAll(ingredient.getStackingIds());
+ intCollection.addAll(this.getStackingIds(ingredient)); // Paper
}
IntIterator intIterator = intCollection.iterator();
@@ -294,7 +314,7 @@ public class StackedContents {
for(Ingredient ingredient : this.ingredients) {
int j = 0;
- for(int k : ingredient.getStackingIds()) {
+ for(int k : this.getStackingIds(ingredient)) { // Paper
j = Math.max(j, StackedContents.this.contents.get(k));
}
@@ -305,5 +325,17 @@ public class StackedContents {
return i;
}
+
+ // Paper start - improve exact recipe choices
+ private IntList getStackingIds(final Ingredient ingredient) {
+ if (StackedContents.this.extrasMap != null) {
+ final IntList ids = StackedContents.this.extrasMap.extraStackingIds.get(ingredient);
+ if (ids != null) {
+ return ids;
+ }
+ }
+ return ingredient.getStackingIds();
+ }
+ // Paper end - improve exact recipe choices
}
}
diff --git a/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java b/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java
index c864809c7d656cf8e3f1080c2c57aefd0a417240..760a429137350d8ae100e77273318f3ba1ccfcc5 100644
--- a/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java
+++ b/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java
@@ -6,7 +6,7 @@ import net.minecraft.world.Container;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
-public abstract class AbstractCookingRecipe implements Recipe<Container> {
+public abstract class AbstractCookingRecipe extends io.papermc.paper.inventory.recipe.RecipeBookExactChoiceRecipe<Container> implements Recipe<Container> { // Paper - improve exact recipe choices
protected final RecipeType<?> type;
protected final CookingBookCategory category;
protected final String group;
@@ -23,6 +23,7 @@ public abstract class AbstractCookingRecipe implements Recipe<Container> {
this.result = result;
this.experience = experience;
this.cookingTime = cookingTime;
+ this.checkExactIngredients(); // Paper - improve exact recipe choices
}
@Override
diff --git a/src/main/java/net/minecraft/world/item/crafting/Recipe.java b/src/main/java/net/minecraft/world/item/crafting/Recipe.java
index 75ae978b577b6b2399e1476328114e94abd7d6c4..80387cd1bee2bd4c024073cee74222828f9f2c17 100644
--- a/src/main/java/net/minecraft/world/item/crafting/Recipe.java
+++ b/src/main/java/net/minecraft/world/item/crafting/Recipe.java
@@ -65,4 +65,10 @@ public interface Recipe<C extends Container> {
}
org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id); // CraftBukkit
+
+ // Paper start - improved exact choice recipes
+ default boolean hasExactIngredients() {
+ return false;
+ }
+ // Paper end
}
diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
index 4430c1161c3316566158ab7286e21495edb38991..17bef91546fa85d401b263c3a69fbf464f290eca 100644
--- a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
+++ b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
@@ -26,7 +26,7 @@ import org.bukkit.craftbukkit.inventory.CraftShapedRecipe;
import org.bukkit.inventory.RecipeChoice;
// CraftBukkit end
-public class ShapedRecipe implements CraftingRecipe {
+public class ShapedRecipe extends io.papermc.paper.inventory.recipe.RecipeBookExactChoiceRecipe<CraftingContainer> implements CraftingRecipe { // Paper - improve exact recipe choices
final int width;
final int height;
@@ -44,6 +44,7 @@ public class ShapedRecipe implements CraftingRecipe {
this.recipeItems = ingredients;
this.result = result;
this.showNotification = showNotification;
+ this.checkExactIngredients(); // Paper - improve exact recipe choices
}
public ShapedRecipe(String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result) {
diff --git a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
index 7f78490ebf459595460ce656b9b9227e5d6b8de6..38f7d1ece27ec1a3deda21fb6a6f0e788c8ed718 100644
--- a/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
+++ b/src/main/java/net/minecraft/world/item/crafting/ShapelessRecipe.java
@@ -20,7 +20,7 @@ import org.bukkit.craftbukkit.inventory.CraftRecipe;
import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe;
// CraftBukkit end
-public class ShapelessRecipe implements CraftingRecipe {
+public class ShapelessRecipe extends io.papermc.paper.inventory.recipe.RecipeBookExactChoiceRecipe<CraftingContainer> implements CraftingRecipe { // Paper - improve exact recipe choices
final String group;
final CraftingBookCategory category;
@@ -32,6 +32,7 @@ public class ShapelessRecipe implements CraftingRecipe {
this.category = category;
this.result = result;
this.ingredients = ingredients;
+ this.checkExactIngredients(); // Paper - improve exact recipe choices
}
// CraftBukkit start
@@ -77,6 +78,7 @@ public class ShapelessRecipe implements CraftingRecipe {
public boolean matches(CraftingContainer inventory, Level world) {
StackedContents autorecipestackmanager = new StackedContents();
+ autorecipestackmanager.initialize(this); // Paper - better exact choice recipes
int i = 0;
for (int j = 0; j < inventory.getContainerSize(); ++j) {

View file

@ -0,0 +1,56 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 27 Feb 2019 22:18:40 -0500
Subject: [PATCH] Limit Client Sign length more
modified clients can send more data from the client
to the server and it would get stored on the sign as sent.
Mojang has a limit of 384 which is much higher than reasonable.
the client can barely render around 16 characters as-is, but formatting
codes can get it to be more than 16 actual length.
Set a limit of 80 which should give an average of 16 characters 2
sets of legacy formatting codes which should be plenty for all uses.
This does not strip any existing data from the NBT as plugins
may use this for storing data out of the rendered area.
it only impacts data sent from the client.
Set -DPaper.maxSignLength=XX to change limit or -1 to disable
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index aed6d2420afeb404b5542f19d7424b6c14d0b636..717f23a92017a589e639c09e15377e30f602d8fe 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -288,6 +288,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
private final MessageSignatureCache messageSignatureCache;
private final FutureChain chatMessageChain;
private boolean waitingForSwitchToConfig;
+ private static final int MAX_SIGN_LINE_LENGTH = Integer.getInteger("Paper.maxSignLength", 80); // Paper
public ServerGamePacketListenerImpl(MinecraftServer server, Connection connection, ServerPlayer player, CommonListenerCookie clientData) {
super(server, connection, clientData, player); // CraftBukkit
@@ -3044,7 +3045,19 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
@Override
public void handleSignUpdate(ServerboundSignUpdatePacket packet) {
- List<String> list = (List) Stream.of(packet.getLines()).map(ChatFormatting::stripFormatting).collect(Collectors.toList());
+ // Paper start - cap line length - modified clients can send longer data than normal
+ String[] lines = packet.getLines();
+ for (int i = 0; i < lines.length; ++i) {
+ if (MAX_SIGN_LINE_LENGTH > 0 && lines[i].length() > MAX_SIGN_LINE_LENGTH) {
+ // This handles multibyte characters as 1
+ int offset = lines[i].codePoints().limit(MAX_SIGN_LINE_LENGTH).map(Character::charCount).sum();
+ if (offset < lines[i].length()) {
+ lines[i] = lines[i].substring(0, offset); // this will break any filtering, but filtering is NYI as of 1.17
+ }
+ }
+ }
+ List<String> list = (List) Stream.of(lines).map(ChatFormatting::stripFormatting).collect(Collectors.toList());
+ // Paper end
this.filterTextPacket(list).thenAcceptAsync((list1) -> {
this.updateSignText(packet, list1);

View file

@ -0,0 +1,64 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 6 May 2020 05:00:57 -0400
Subject: [PATCH] Handle Oversized Tile Entities in chunks
Splits out Extra Packets if too many TE's are encountered to prevent
creating too large of a packet to sed.
Co authored by Spottedleaf
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
index f47eeb70661661610ef1a96dd9da67785825c126..0ef3e9b472e35bd2572b04722781abf7d4a1094b 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkPacketData.java
@@ -24,6 +24,14 @@ public class ClientboundLevelChunkPacketData {
private final CompoundTag heightmaps;
private final byte[] buffer;
private final List<ClientboundLevelChunkPacketData.BlockEntityInfo> blockEntitiesData;
+ // Paper start
+ private final java.util.List<net.minecraft.network.protocol.Packet> extraPackets = new java.util.ArrayList<>();
+ private static final int TE_LIMIT = Integer.getInteger("Paper.excessiveTELimit", 750);
+
+ public List<net.minecraft.network.protocol.Packet> getExtraPackets() {
+ return this.extraPackets;
+ }
+ // Paper end
public ClientboundLevelChunkPacketData(LevelChunk chunk) {
this.heightmaps = new CompoundTag();
@@ -37,8 +45,18 @@ public class ClientboundLevelChunkPacketData {
this.buffer = new byte[calculateChunkSize(chunk)];
extractChunkData(new FriendlyByteBuf(this.getWriteBuffer()), chunk);
this.blockEntitiesData = Lists.newArrayList();
+ int totalTileEntities = 0; // Paper
for(Map.Entry<BlockPos, BlockEntity> entry2 : chunk.getBlockEntities().entrySet()) {
+ // Paper start
+ if (++totalTileEntities > TE_LIMIT) {
+ var packet = entry2.getValue().getUpdatePacket();
+ if (packet != null) {
+ this.extraPackets.add(packet);
+ continue;
+ }
+ }
+ // Paper end
this.blockEntitiesData.add(ClientboundLevelChunkPacketData.BlockEntityInfo.create(entry2.getValue()));
}
diff --git a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
index 26e46d751c8f8162c2bafe2fc109fc91dc4b7c0f..ff42a3e76500ad6e087b7f1cd6ec45acde124180 100644
--- a/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
+++ b/src/main/java/net/minecraft/network/protocol/game/ClientboundLevelChunkWithLightPacket.java
@@ -57,4 +57,11 @@ public class ClientboundLevelChunkWithLightPacket implements Packet<ClientGamePa
public ClientboundLightUpdatePacketData getLightData() {
return this.lightData;
}
+
+ // Paper start - handle over-sized TE packets
+ @Override
+ public java.util.List<Packet> getExtraPackets() {
+ return this.chunkData.getExtraPackets();
+ }
+ // Paper end
}

View file

@ -0,0 +1,18 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mark Vainomaa <mikroskeem@mikroskeem.eu>
Date: Wed, 13 Mar 2019 20:08:09 +0200
Subject: [PATCH] Call WhitelistToggleEvent when whitelist is toggled
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 7b094b859fe4c865cd369108700d5b0d6573c019..b97ffb7746b2be70285addd091711fef0c2b1b87 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -1151,6 +1151,7 @@ public abstract class PlayerList {
}
public void setUsingWhiteList(boolean whitelistEnabled) {
+ new com.destroystokyo.paper.event.server.WhitelistToggleEvent(whitelistEnabled).callEvent();
this.doWhiteList = whitelistEnabled;
}

View file

@ -0,0 +1,136 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 24 Mar 2019 00:24:52 -0400
Subject: [PATCH] Entity#getEntitySpawnReason
Allows you to return the SpawnReason for why an Entity Spawned
Pre existing entities will return NATURAL if it was a non
persistenting Living Entity, SPAWNER for spawners,
or DEFAULT since data was not stored.
diff --git a/src/main/java/net/minecraft/server/commands/SummonCommand.java b/src/main/java/net/minecraft/server/commands/SummonCommand.java
index 2eddeb8d5239bbfeefbf4d3bd363f1ad083299b6..a7c89cdf20cb63792c76de81c1ff9f2cbbfcea84 100644
--- a/src/main/java/net/minecraft/server/commands/SummonCommand.java
+++ b/src/main/java/net/minecraft/server/commands/SummonCommand.java
@@ -57,6 +57,7 @@ public class SummonCommand {
ServerLevel worldserver = source.getLevel();
Entity entity = EntityType.loadEntityRecursive(nbttagcompound1, worldserver, (entity1) -> {
entity1.moveTo(pos.x, pos.y, pos.z, entity1.getYRot(), entity1.getXRot());
+ entity1.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.COMMAND; // Paper
return entity1;
});
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index af33e4be10c9d571eb6f7ba5730b7ed4d8647761..6978abb97da6c33099db2e4ead3f33b0ca87bed1 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1197,6 +1197,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
return true;
}
// Paper end
+ if (entity.spawnReason == null) entity.spawnReason = spawnReason; // Paper
if (entity.isRemoved()) {
// Paper start
if (DEBUG_ENTITIES) {
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index b97ffb7746b2be70285addd091711fef0c2b1b87..3831c724dfcdf97129c6fd8dab0c7a91fa3e7679 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -225,6 +225,11 @@ public abstract class PlayerList {
worldserver1 = worldserver;
}
+ // Paper start
+ if (nbttagcompound == null) {
+ player.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT; // set Player SpawnReason to DEFAULT on first login
+ }
+ // Paper end
player.setServerLevel(worldserver1);
String s1 = connection.getLoggableAddress(this.server.logIPs());
@@ -356,7 +361,7 @@ public abstract class PlayerList {
// CraftBukkit start
ServerLevel finalWorldServer = worldserver1;
Entity entity = EntityType.loadEntityRecursive(nbttagcompound1.getCompound("Entity"), finalWorldServer, (entity1) -> {
- return !finalWorldServer.addWithUUID(entity1) ? null : entity1;
+ return !finalWorldServer.addWithUUID(entity1, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.MOUNT) ? null : entity1; // Paper
// CraftBukkit end
});
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index d0779d38c56ca1c01abd154720d2865d14479a30..7c23f63fd642002cac597da2eafc5f0b4232d5a4 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -234,6 +234,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
}
// Paper end
+ public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper
public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper
private CraftEntity bukkitEntity;
@@ -2137,6 +2138,9 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
nbt.put("Paper.Origin", this.newDoubleList(origin.getX(), origin.getY(), origin.getZ()));
}
+ if (spawnReason != null) {
+ nbt.putString("Paper.SpawnReason", spawnReason.name());
+ }
// Save entity's from mob spawner status
if (spawnedViaMobSpawner) {
nbt.putBoolean("Paper.FromMobSpawner", true);
@@ -2283,6 +2287,26 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource {
}
spawnedViaMobSpawner = nbt.getBoolean("Paper.FromMobSpawner"); // Restore entity's from mob spawner status
+ if (nbt.contains("Paper.SpawnReason")) {
+ String spawnReasonName = nbt.getString("Paper.SpawnReason");
+ try {
+ spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.valueOf(spawnReasonName);
+ } catch (Exception ignored) {
+ LOGGER.error("Unknown SpawnReason " + spawnReasonName + " for " + this);
+ }
+ }
+ if (spawnReason == null) {
+ if (spawnedViaMobSpawner) {
+ spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER;
+ } else if (this instanceof Mob && (this instanceof net.minecraft.world.entity.animal.Animal || this instanceof net.minecraft.world.entity.animal.AbstractFish) && !((Mob) this).removeWhenFarAway(0.0)) {
+ if (!nbt.getBoolean("PersistenceRequired")) {
+ spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL;
+ }
+ }
+ }
+ if (spawnReason == null) {
+ spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT;
+ }
// Paper end
} catch (Throwable throwable) {
diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java
index a9be524edb03c51300bc45d424fcf87c7491a8c0..a08c2dee792da1a54005f0a65a9eefabc7bc7c60 100644
--- a/src/main/java/net/minecraft/world/level/BaseSpawner.java
+++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java
@@ -187,6 +187,7 @@ public abstract class BaseSpawner {
}
entity.spawnedViaMobSpawner = true; // Paper
+ entity.spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER; // Paper
flag = true; // Paper
// CraftBukkit start
if (org.bukkit.craftbukkit.event.CraftEventFactory.callSpawnerSpawnEvent(entity, pos).isCancelled()) {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 0fd9119c710afdd46a60d81ae3f9ce5da88d2541..c7cd18de51c5e7c9d194ba65902084194d4408ec 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -1307,5 +1307,10 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
public boolean fromMobSpawner() {
return getHandle().spawnedViaMobSpawner;
}
+
+ @Override
+ public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason getEntitySpawnReason() {
+ return getHandle().spawnReason;
+ }
// Paper end
}

View file

@ -0,0 +1,155 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mark Vainomaa <mikroskeem@mikroskeem.eu>
Date: Sun, 17 Mar 2019 21:46:56 +0200
Subject: [PATCH] Fire event on GS4 query
diff --git a/src/main/java/net/minecraft/server/rcon/NetworkDataOutputStream.java b/src/main/java/net/minecraft/server/rcon/NetworkDataOutputStream.java
index 51cb2644aa516a59e19fecb308d519dbc7e5fb11..e548aa0ca4e1e94ab628614b44fc11568ca3beff 100644
--- a/src/main/java/net/minecraft/server/rcon/NetworkDataOutputStream.java
+++ b/src/main/java/net/minecraft/server/rcon/NetworkDataOutputStream.java
@@ -22,6 +22,16 @@ public class NetworkDataOutputStream {
this.dataOutputStream.write(0);
}
+ // Paper start - unchecked exception variant to use in Stream API
+ public void writeStringUnchecked(String string) {
+ try {
+ writeString(string);
+ } catch (IOException e) {
+ com.destroystokyo.paper.util.SneakyThrow.sneaky(e);
+ }
+ }
+ // Paper end
+
public void write(int value) throws IOException {
this.dataOutputStream.write(value);
}
diff --git a/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java b/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java
index 88917d7a179739fc13c59831b15e7ddc711bed1b..1ef089dbf83de35d875c00efdf468c397be56978 100644
--- a/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java
+++ b/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java
@@ -106,13 +106,32 @@ public class QueryThreadGs4 extends GenericThread {
NetworkDataOutputStream networkDataOutputStream = new NetworkDataOutputStream(1460);
networkDataOutputStream.write(0);
networkDataOutputStream.writeBytes(this.getIdentBytes(packet.getSocketAddress()));
- networkDataOutputStream.writeString(this.serverName);
+
+ com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType queryType =
+ com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType.BASIC;
+ com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse queryResponse = com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.builder()
+ .motd(this.serverName)
+ .map(this.worldName)
+ .currentPlayers(this.serverInterface.getPlayerCount())
+ .maxPlayers(this.maxPlayers)
+ .port(this.serverPort)
+ .hostname(this.hostIp)
+ .gameVersion(this.serverInterface.getServerVersion())
+ .serverVersion(org.bukkit.Bukkit.getServer().getName() + " on " + org.bukkit.Bukkit.getServer().getBukkitVersion())
+ .build();
+ com.destroystokyo.paper.event.server.GS4QueryEvent queryEvent =
+ new com.destroystokyo.paper.event.server.GS4QueryEvent(queryType, packet.getAddress(), queryResponse);
+ queryEvent.callEvent();
+ queryResponse = queryEvent.getResponse();
+
+ networkDataOutputStream.writeString(queryResponse.getMotd());
networkDataOutputStream.writeString("SMP");
- networkDataOutputStream.writeString(this.worldName);
- networkDataOutputStream.writeString(Integer.toString(this.serverInterface.getPlayerCount()));
- networkDataOutputStream.writeString(Integer.toString(this.maxPlayers));
- networkDataOutputStream.writeShort((short)this.serverPort);
- networkDataOutputStream.writeString(this.hostIp);
+ networkDataOutputStream.writeString(queryResponse.getMap());
+ networkDataOutputStream.writeString(Integer.toString(queryResponse.getCurrentPlayers()));
+ networkDataOutputStream.writeString(Integer.toString(queryResponse.getMaxPlayers()));
+ networkDataOutputStream.writeShort((short) queryResponse.getPort());
+ networkDataOutputStream.writeString(queryResponse.getHostname());
+ // Paper end
this.sendTo(networkDataOutputStream.toByteArray(), packet);
LOGGER.debug("Status [{}]", (Object)socketAddress);
}
@@ -147,31 +166,75 @@ public class QueryThreadGs4 extends GenericThread {
this.rulesResponse.writeString("splitnum");
this.rulesResponse.write(128);
this.rulesResponse.write(0);
+ // Paper start
+ // Pack plugins
+ java.util.List<com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.PluginInformation> plugins = java.util.Collections.emptyList();
+ org.bukkit.plugin.Plugin[] bukkitPlugins;
+ if (((net.minecraft.server.dedicated.DedicatedServer) this.serverInterface).server.getQueryPlugins() && (bukkitPlugins = org.bukkit.Bukkit.getPluginManager().getPlugins()).length > 0) {
+ plugins = java.util.stream.Stream.of(bukkitPlugins)
+ .map(plugin -> com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.PluginInformation.of(plugin.getName(), plugin.getDescription().getVersion()))
+ .collect(java.util.stream.Collectors.toList());
+ }
+
+ com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse queryResponse = com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.builder()
+ .motd(this.serverName)
+ .map(this.worldName)
+ .currentPlayers(this.serverInterface.getPlayerCount())
+ .maxPlayers(this.maxPlayers)
+ .port(this.serverPort)
+ .hostname(this.hostIp)
+ .plugins(plugins)
+ .players(this.serverInterface.getPlayerNames())
+ .gameVersion(this.serverInterface.getServerVersion())
+ .serverVersion(org.bukkit.Bukkit.getServer().getName() + " on " + org.bukkit.Bukkit.getServer().getBukkitVersion())
+ .build();
+ com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType queryType =
+ com.destroystokyo.paper.event.server.GS4QueryEvent.QueryType.FULL;
+ com.destroystokyo.paper.event.server.GS4QueryEvent queryEvent =
+ new com.destroystokyo.paper.event.server.GS4QueryEvent(queryType, packet.getAddress(), queryResponse);
+ queryEvent.callEvent();
+ queryResponse = queryEvent.getResponse();
this.rulesResponse.writeString("hostname");
- this.rulesResponse.writeString(this.serverName);
+ this.rulesResponse.writeString(queryResponse.getMotd());
this.rulesResponse.writeString("gametype");
this.rulesResponse.writeString("SMP");
this.rulesResponse.writeString("game_id");
this.rulesResponse.writeString("MINECRAFT");
this.rulesResponse.writeString("version");
- this.rulesResponse.writeString(this.serverInterface.getServerVersion());
+ this.rulesResponse.writeString(queryResponse.getGameVersion());
this.rulesResponse.writeString("plugins");
- this.rulesResponse.writeString(this.serverInterface.getPluginNames());
+ java.lang.StringBuilder pluginsString = new java.lang.StringBuilder();
+ pluginsString.append(queryResponse.getServerVersion());
+ if (!queryResponse.getPlugins().isEmpty()) {
+ pluginsString.append(": ");
+ java.util.Iterator<com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.PluginInformation> iter = queryResponse.getPlugins().iterator();
+ while (iter.hasNext()) {
+ com.destroystokyo.paper.event.server.GS4QueryEvent.QueryResponse.PluginInformation info = iter.next();
+ pluginsString.append(info.getName());
+ if (info.getVersion() != null) {
+ pluginsString.append(' ').append(info.getVersion().replace(";", ","));
+ }
+ if (iter.hasNext()) {
+ pluginsString.append(';').append(' ');
+ }
+ }
+ }
+ this.rulesResponse.writeString(pluginsString.toString());
this.rulesResponse.writeString("map");
- this.rulesResponse.writeString(this.worldName);
+ this.rulesResponse.writeString(queryResponse.getMap());
this.rulesResponse.writeString("numplayers");
- this.rulesResponse.writeString("" + this.serverInterface.getPlayerCount());
+ this.rulesResponse.writeString(Integer.toString(queryResponse.getCurrentPlayers()));
this.rulesResponse.writeString("maxplayers");
- this.rulesResponse.writeString("" + this.maxPlayers);
+ this.rulesResponse.writeString(Integer.toString(queryResponse.getMaxPlayers()));
this.rulesResponse.writeString("hostport");
- this.rulesResponse.writeString("" + this.serverPort);
+ this.rulesResponse.writeString(Integer.toString(queryResponse.getPort()));
this.rulesResponse.writeString("hostip");
- this.rulesResponse.writeString(this.hostIp);
+ this.rulesResponse.writeString(queryResponse.getHostname());
this.rulesResponse.write(0);
this.rulesResponse.write(1);
this.rulesResponse.writeString("player_");
this.rulesResponse.write(0);
- String[] strings = this.serverInterface.getPlayerNames();
+ String[] strings = queryResponse.getPlayers().toArray(String[]::new);
for(String string : strings) {
this.rulesResponse.writeString(string);

View file

@ -0,0 +1,48 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MisterVector <whizkid3000@hotmail.com>
Date: Fri, 26 Oct 2018 21:31:00 -0700
Subject: [PATCH] Implement PlayerPostRespawnEvent
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 3831c724dfcdf97129c6fd8dab0c7a91fa3e7679..3c4224472ce59d7a2c1748d510e4fc8f58a7d5e1 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -753,9 +753,14 @@ public abstract class PlayerList {
boolean flag2 = false;
+ // Paper start
+ boolean isBedSpawn = false;
+ boolean isRespawn = false;
+ // Paper end
+
// CraftBukkit start - fire PlayerRespawnEvent
if (location == null) {
- boolean isBedSpawn = false;
+ // boolean isBedSpawn = false; // Paper - moved up
ServerLevel worldserver1 = this.server.getLevel(entityplayer.getRespawnDimension());
if (worldserver1 != null) {
Optional optional;
@@ -807,6 +812,7 @@ public abstract class PlayerList {
location = respawnEvent.getRespawnLocation();
if (!flag) entityplayer.reset(); // SPIGOT-4785
+ isRespawn = true; // Paper
} else {
location.setWorld(worldserver.getWorld());
}
@@ -865,6 +871,13 @@ public abstract class PlayerList {
if (entityplayer.connection.isDisconnected()) {
this.save(entityplayer);
}
+
+ // Paper start
+ if (isRespawn) {
+ cserver.getPluginManager().callEvent(new com.destroystokyo.paper.event.player.PlayerPostRespawnEvent(entityplayer.getBukkitEntity(), location, isBedSpawn));
+ }
+ // Paper end
+
// CraftBukkit end
return entityplayer1;
}

View file

@ -0,0 +1,27 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 24 Mar 2019 18:09:20 -0400
Subject: [PATCH] don't go below 0 for pickupDelay, breaks picking up items
vanilla checks for == 0
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
index 91eba1c571764c88489ea2598fb9b4736554d5cc..fda34d93a5b75919c840d3bc0efa0651e5eb4843 100644
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
@@ -122,6 +122,7 @@ public class ItemEntity extends Entity implements TraceableEntity {
// CraftBukkit start - Use wall time for pickup and despawn timers
int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
+ this.pickupDelay = Math.max(0, this.pickupDelay); // Paper - don't go below 0
if (this.age != -32768) this.age += elapsedTicks;
this.lastTick = MinecraftServer.currentTick;
// CraftBukkit end
@@ -208,6 +209,7 @@ public class ItemEntity extends Entity implements TraceableEntity {
// CraftBukkit start - Use wall time for pickup and despawn timers
int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
+ this.pickupDelay = Math.max(0, this.pickupDelay); // Paper - don't go below 0
if (this.age != -32768) this.age += elapsedTicks;
this.lastTick = MinecraftServer.currentTick;
// CraftBukkit end

View file

@ -0,0 +1,31 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 27 Mar 2019 22:48:45 -0400
Subject: [PATCH] Server Tick Events
Fires event at start and end of a server tick
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index bace1ff9e64bd8bec334c5e1d1fd1c621a63cd92..8ce9b9444c46956eeace333206eefd3ebca1e0c0 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1292,6 +1292,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
});
isOversleep = false;MinecraftTimings.serverOversleep.stopTiming();
// Paper end
+ new com.destroystokyo.paper.event.server.ServerTickStartEvent(this.tickCount+1).callEvent(); // Paper
++this.tickCount;
this.tickChildren(shouldKeepTicking);
@@ -1313,6 +1314,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
this.runAllTasks();
}
// Paper end
+ // Paper start
+ long endTime = System.nanoTime();
+ long remaining = (TICK_TIME - (endTime - lastTick)) - catchupTime;
+ new com.destroystokyo.paper.event.server.ServerTickEndEvent(this.tickCount, ((double)(endTime - lastTick) / 1000000D), remaining).callEvent();
+ // Paper end
this.profiler.push("tallying");
long j = this.tickTimes[this.tickCount % 100] = Util.getNanos() - i;

View file

@ -0,0 +1,77 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Wed, 27 Mar 2019 23:01:33 -0400
Subject: [PATCH] PlayerDeathEvent#getItemsToKeep
Exposes a mutable array on items a player should keep on death
Example Usage: https://gist.github.com/aikar/5bb202de6057a051a950ce1f29feb0b4
== AT ==
public net.minecraft.world.entity.player.Inventory compartments
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 9ad2e953a3b4a7a2f4ce375a1b209820cdef2d47..e66d24174d5834cb9526fa4a238d0765bef827b2 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -806,6 +806,46 @@ public class ServerPlayer extends Player {
});
}
+ // Paper start - process inventory
+ private static void processKeep(org.bukkit.event.entity.PlayerDeathEvent event, NonNullList<ItemStack> inv) {
+ List<org.bukkit.inventory.ItemStack> itemsToKeep = event.getItemsToKeep();
+ if (inv == null) {
+ // remainder of items left in toKeep - plugin added stuff on death that wasn't in the initial loot?
+ if (!itemsToKeep.isEmpty()) {
+ for (org.bukkit.inventory.ItemStack itemStack : itemsToKeep) {
+ event.getEntity().getInventory().addItem(itemStack);
+ }
+ }
+
+ return;
+ }
+
+ for (int i = 0; i < inv.size(); ++i) {
+ ItemStack item = inv.get(i);
+ if (EnchantmentHelper.hasVanishingCurse(item) || itemsToKeep.isEmpty() || item.isEmpty()) {
+ inv.set(i, ItemStack.EMPTY);
+ continue;
+ }
+
+ final org.bukkit.inventory.ItemStack bukkitStack = item.getBukkitStack();
+ boolean keep = false;
+ final Iterator<org.bukkit.inventory.ItemStack> iterator = itemsToKeep.iterator();
+ while (iterator.hasNext()) {
+ final org.bukkit.inventory.ItemStack itemStack = iterator.next();
+ if (bukkitStack.equals(itemStack)) {
+ iterator.remove();
+ keep = true;
+ break;
+ }
+ }
+
+ if (!keep) {
+ inv.set(i, ItemStack.EMPTY);
+ }
+ }
+ }
+ // Paper end
+
@Override
public void die(DamageSource damageSource) {
this.gameEvent(GameEvent.ENTITY_DIE);
@@ -889,7 +929,12 @@ public class ServerPlayer extends Player {
this.dropExperience();
// we clean the player's inventory after the EntityDeathEvent is called so plugins can get the exact state of the inventory.
if (!event.getKeepInventory()) {
- this.getInventory().clearContent();
+ // Paper start - replace logic
+ for (NonNullList<ItemStack> inv : this.getInventory().compartments) {
+ processKeep(event, inv);
+ }
+ processKeep(event, null);
+ // Paper end
}
this.setCamera(this); // Remove spectated target

View file

@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 6 Apr 2019 10:16:48 -0400
Subject: [PATCH] Optimize Captured TileEntity Lookup
upstream was doing a containsKey/get pattern, and always doing it at that.
that scenario is only even valid if were in the middle of a block place.
Optimize to check if the captured list even has values in it, and also to
just do a get call since the value can never be null.
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 9fa7dd67ebe5d7d4125e970dbe2e3b201b01f6aa..4968e036350631b79a8c7e70fdc32db4bcff2ade 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -977,9 +977,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@Nullable
public BlockEntity getBlockEntity(BlockPos blockposition, boolean validate) {
- if (this.capturedTileEntities.containsKey(blockposition)) {
- return this.capturedTileEntities.get(blockposition);
+ // Paper start - Optimize capturedTileEntities lookup
+ net.minecraft.world.level.block.entity.BlockEntity blockEntity;
+ if (!this.capturedTileEntities.isEmpty() && (blockEntity = this.capturedTileEntities.get(blockposition)) != null) {
+ return blockEntity;
}
+ // Paper end
// CraftBukkit end
return this.isOutsideBuildHeight(blockposition) ? null : (!this.isClientSide && Thread.currentThread() != this.thread ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE));
}

View file

@ -0,0 +1,40 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Tue, 1 Jan 2019 02:22:01 -0800
Subject: [PATCH] Add Heightmap API
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 7d9942004b52b4deae7ca09dafb43795d0d9c31d..00267800750ad2a4817b39c9c19a9336aff2299d 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -219,6 +219,29 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return CraftBlock.at(world, new BlockPos(x, y, z));
}
+ // Paper start - Implement heightmap api
+ @Override
+ public int getHighestBlockYAt(final int x, final int z, final com.destroystokyo.paper.HeightmapType heightmap) throws UnsupportedOperationException {
+ this.getChunkAt(x >> 4, z >> 4); // heightmap will ret 0 on unloaded areas
+
+ switch (heightmap) {
+ case LIGHT_BLOCKING:
+ throw new UnsupportedOperationException(); // TODO
+ //return this.world.getHighestBlockY(HeightMap.Type.LIGHT_BLOCKING, x, z);
+ case ANY:
+ return this.world.getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.WORLD_SURFACE, x, z);
+ case SOLID:
+ return this.world.getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.OCEAN_FLOOR, x, z);
+ case SOLID_OR_LIQUID:
+ return this.world.getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.MOTION_BLOCKING, x, z);
+ case SOLID_OR_LIQUID_NO_LEAVES:
+ return this.world.getHeight(net.minecraft.world.level.levelgen.Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z);
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+ // Paper end
+
@Override
public Location getSpawnLocation() {
BlockPos spawn = this.world.getSharedSpawnPos();

View file

@ -0,0 +1,103 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Fri, 19 Apr 2019 12:41:13 -0500
Subject: [PATCH] Mob Spawner API Enhancements
== AT ==
public net.minecraft.world.level.BaseSpawner isNearPlayer(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)Z
public net.minecraft.world.level.BaseSpawner delay(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;)V
public net.minecraft.world.level.BaseSpawner setNextSpawnData(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/SpawnData;)V
diff --git a/src/main/java/net/minecraft/world/level/BaseSpawner.java b/src/main/java/net/minecraft/world/level/BaseSpawner.java
index a08c2dee792da1a54005f0a65a9eefabc7bc7c60..369298dfd437c1c83801f3d4ba63484ee1b969fe 100644
--- a/src/main/java/net/minecraft/world/level/BaseSpawner.java
+++ b/src/main/java/net/minecraft/world/level/BaseSpawner.java
@@ -233,7 +233,13 @@ public abstract class BaseSpawner {
}
public void load(@Nullable Level world, BlockPos pos, CompoundTag nbt) {
+ // Paper start - use larger int if set
+ if (nbt.contains("Paper.Delay")) {
+ this.spawnDelay = nbt.getInt("Paper.Delay");
+ } else {
this.spawnDelay = nbt.getShort("Delay");
+ }
+ // Paper end
boolean flag = nbt.contains("SpawnData", 10);
if (flag) {
@@ -256,9 +262,15 @@ public abstract class BaseSpawner {
this.spawnPotentials = SimpleWeightedRandomList.single(this.nextSpawnData != null ? this.nextSpawnData : new SpawnData());
}
+ // Paper start - use ints if set
+ if (nbt.contains("Paper.MinSpawnDelay", 99)) {
+ this.minSpawnDelay = nbt.getInt("Paper.MinSpawnDelay");
+ this.maxSpawnDelay = nbt.getInt("Paper.MaxSpawnDelay");
+ this.spawnCount = nbt.getShort("SpawnCount");
+ } else // Paper end
if (nbt.contains("MinSpawnDelay", 99)) {
- this.minSpawnDelay = nbt.getShort("MinSpawnDelay");
- this.maxSpawnDelay = nbt.getShort("MaxSpawnDelay");
+ this.minSpawnDelay = nbt.getInt("MinSpawnDelay"); // Paper - short -> int
+ this.maxSpawnDelay = nbt.getInt("MaxSpawnDelay"); // Paper - short -> int
this.spawnCount = nbt.getShort("SpawnCount");
}
@@ -275,9 +287,20 @@ public abstract class BaseSpawner {
}
public CompoundTag save(CompoundTag nbt) {
- nbt.putShort("Delay", (short) this.spawnDelay);
- nbt.putShort("MinSpawnDelay", (short) this.minSpawnDelay);
- nbt.putShort("MaxSpawnDelay", (short) this.maxSpawnDelay);
+ // Paper start
+ if (spawnDelay > Short.MAX_VALUE) {
+ nbt.putInt("Paper.Delay", this.spawnDelay);
+ }
+ nbt.putShort("Delay", (short) Math.min(Short.MAX_VALUE, this.spawnDelay));
+
+ if (minSpawnDelay > Short.MAX_VALUE || maxSpawnDelay > Short.MAX_VALUE) {
+ nbt.putInt("Paper.MinSpawnDelay", this.minSpawnDelay);
+ nbt.putInt("Paper.MaxSpawnDelay", this.maxSpawnDelay);
+ }
+
+ nbt.putShort("MinSpawnDelay", (short) Math.min(Short.MAX_VALUE, this.minSpawnDelay));
+ nbt.putShort("MaxSpawnDelay", (short) Math.min(Short.MAX_VALUE, this.maxSpawnDelay));
+ // Paper nbt
nbt.putShort("SpawnCount", (short) this.spawnCount);
nbt.putShort("MaxNearbyEntities", (short) this.maxNearbyEntities);
nbt.putShort("RequiredPlayerRange", (short) this.requiredPlayerRange);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java b/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java
index f733df6d082561bf80a3fcfaaab1731985213324..e2d0937f8609c1f65eff94a1cdae8c80b6347cc3 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftCreatureSpawner.java
@@ -134,4 +134,28 @@ public class CraftCreatureSpawner extends CraftBlockEntityState<SpawnerBlockEnti
public void setSpawnRange(int spawnRange) {
this.getSnapshot().getSpawner().spawnRange = spawnRange;
}
+
+ // Paper start
+ @Override
+ public boolean isActivated() {
+ this.requirePlaced();
+ return this.getSnapshot().getSpawner().isNearPlayer(this.world.getHandle(), this.getPosition());
+ }
+
+ @Override
+ public void resetTimer() {
+ this.requirePlaced();
+ this.getSnapshot().getSpawner().delay(this.world.getHandle(), this.getPosition());
+ }
+
+ @Override
+ public void setSpawnedItem(org.bukkit.inventory.ItemStack itemStack) {
+ Preconditions.checkArgument(itemStack != null && !itemStack.getType().isAir(), "spawners cannot spawn air");
+ net.minecraft.world.item.ItemStack item = org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(itemStack);
+ net.minecraft.nbt.CompoundTag entity = new net.minecraft.nbt.CompoundTag();
+ entity.putString("id", net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.getKey(net.minecraft.world.entity.EntityType.ITEM).toString());
+ entity.put("Item", item.save(new net.minecraft.nbt.CompoundTag()));
+ this.getSnapshot().getSpawner().setNextSpawnData(this.isPlaced() ? this.world.getHandle() : null, this.getPosition(), new net.minecraft.world.level.SpawnData(entity, java.util.Optional.empty()));
+ }
+ // Paper end
}

View file

@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Fri, 10 May 2019 18:38:19 +0100
Subject: [PATCH] Fix CB call to changed postToMainThread method
diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
index 988820d6041f3784fec9321c7bc08e2e84a52ad5..d1808bf9dc19fad84da5eb3b4c3d549bc624b00a 100644
--- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
@@ -297,7 +297,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
Objects.requireNonNull(this.connection);
// CraftBukkit - Don't wait
- minecraftserver.wrapRunnable(networkmanager::handleDisconnection);
+ minecraftserver.scheduleOnMain(networkmanager::handleDisconnection); // Paper
}
protected boolean isSingleplayerOwner() {

View file

@ -0,0 +1,20 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Phoenix616 <mail@moep.tv>
Date: Sat, 27 Apr 2019 20:00:43 +0100
Subject: [PATCH] Fix sounds when item frames are modified (MC-123450)
This also fixes the adding sound playing when the item frame direction is changed.
diff --git a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
index a7ca700b9b1b3c09e9edd596d28e1123c211adc3..dc999068891bfdfd4873ca939b4c4389d63f4415 100644
--- a/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
+++ b/src/main/java/net/minecraft/world/entity/decoration/ItemFrame.java
@@ -331,7 +331,7 @@ public class ItemFrame extends HangingEntity {
this.onItemChanged(itemstack);
this.getEntityData().set(ItemFrame.DATA_ITEM, itemstack);
- if (!itemstack.isEmpty() && playSound) { // CraftBukkit
+ if (!itemstack.isEmpty() && flag && playSound) { // CraftBukkit // Paper - only play sound when update flag is set
this.playSound(this.getAddItemSound(), 1.0F, 1.0F);
}

View file

@ -0,0 +1,72 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: simpleauthority <jacob@algorithmjunkie.com>
Date: Tue, 28 May 2019 03:48:51 -0700
Subject: [PATCH] Implement CraftBlockSoundGroup
diff --git a/src/main/java/com/destroystokyo/paper/block/CraftBlockSoundGroup.java b/src/main/java/com/destroystokyo/paper/block/CraftBlockSoundGroup.java
new file mode 100644
index 0000000000000000000000000000000000000000..c5b07ec346105d1b95c1c938ffca12a21642040c
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/block/CraftBlockSoundGroup.java
@@ -0,0 +1,39 @@
+package com.destroystokyo.paper.block;
+
+import net.minecraft.world.level.block.SoundType;
+import org.bukkit.Sound;
+import org.bukkit.craftbukkit.CraftSound;
+
+@Deprecated(forRemoval = true)
+public class CraftBlockSoundGroup implements BlockSoundGroup {
+ private final SoundType soundEffectType;
+
+ public CraftBlockSoundGroup(SoundType soundEffectType) {
+ this.soundEffectType = soundEffectType;
+ }
+
+ @Override
+ public Sound getBreakSound() {
+ return CraftSound.getBukkit(soundEffectType.getBreakSound());
+ }
+
+ @Override
+ public Sound getStepSound() {
+ return CraftSound.getBukkit(soundEffectType.getStepSound());
+ }
+
+ @Override
+ public Sound getPlaceSound() {
+ return CraftSound.getBukkit(soundEffectType.getPlaceSound());
+ }
+
+ @Override
+ public Sound getHitSound() {
+ return CraftSound.getBukkit(soundEffectType.getHitSound());
+ }
+
+ @Override
+ public Sound getFallSound() {
+ return CraftSound.getBukkit(soundEffectType.getFallSound());
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
index 3205d86a63f8fcf3ccd13c6be0e0eefc27beb62a..93bf6b8e30e5e28fa9261b8b927081d85547e400 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
@@ -647,4 +647,16 @@ public class CraftBlock implements Block {
public String getTranslationKey() {
return this.getNMS().getBlock().getDescriptionId();
}
+
+ // Paper start
+ @Override
+ public com.destroystokyo.paper.block.BlockSoundGroup getSoundGroup() {
+ return new com.destroystokyo.paper.block.CraftBlockSoundGroup(getNMS().getBlock().defaultBlockState().getSoundType());
+ }
+
+ @Override
+ public org.bukkit.SoundGroup getBlockSoundGroup() {
+ return org.bukkit.craftbukkit.CraftSoundGroup.getSoundGroup(this.getNMS().getSoundType());
+ }
+ // Paper end
}

View file

@ -0,0 +1,222 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 13 Sep 2014 23:14:43 -0400
Subject: [PATCH] Configurable Keep Spawn Loaded range per world
This lets you disable it for some worlds and lower it for others.
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 8ce9b9444c46956eeace333206eefd3ebca1e0c0..5b274f766e8cf55151ac6499fa1a68b9a0816b57 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -722,30 +722,33 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// CraftBukkit start
public void prepareLevels(ChunkProgressListener worldloadlistener, ServerLevel worldserver) {
+ ServerChunkCache chunkproviderserver = worldserver.getChunkSource(); // Paper
// WorldServer worldserver = this.overworld();
this.forceTicks = true;
// CraftBukkit end
+ if (worldserver.getWorld().getKeepSpawnInMemory()) { // Paper
MinecraftServer.LOGGER.info("Preparing start region for dimension {}", worldserver.dimension().location());
BlockPos blockposition = worldserver.getSharedSpawnPos();
worldloadlistener.updateSpawnPos(new ChunkPos(blockposition));
- ServerChunkCache chunkproviderserver = worldserver.getChunkSource();
+ //ChunkProviderServer chunkproviderserver = worldserver.getChunkProvider(); // Paper - move up
this.nextTickTime = Util.getMillis();
- // CraftBukkit start
- if (worldserver.getWorld().getKeepSpawnInMemory()) {
- chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(blockposition), 11, Unit.INSTANCE);
-
- while (chunkproviderserver.getTickingGenerated() != 441) {
- // this.nextTickTime = SystemUtils.getMillis() + 10L;
- this.executeModerately();
- }
- }
+ // Paper start - configurable spawn reason
+ int radiusBlocks = worldserver.paperConfig().spawn.keepSpawnLoadedRange * 16;
+ int radiusChunks = radiusBlocks / 16 + ((radiusBlocks & 15) != 0 ? 1 : 0);
+ int totalChunks = ((radiusChunks) * 2 + 1);
+ totalChunks *= totalChunks;
+ worldloadlistener.setChunkRadius(radiusBlocks / 16);
+
+ worldserver.addTicketsForSpawn(radiusBlocks, blockposition);
+ // Paper end
// this.nextTickTime = SystemUtils.getMillis() + 10L;
this.executeModerately();
// Iterator iterator = this.levels.values().iterator();
+ }
if (true) {
ServerLevel worldserver1 = worldserver;
@@ -768,7 +771,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// this.nextTickTime = SystemUtils.getMillis() + 10L;
this.executeModerately();
// CraftBukkit end
- worldloadlistener.stop();
+ if (worldserver.getWorld().getKeepSpawnInMemory()) worldloadlistener.stop(); // Paper
// CraftBukkit start
// this.updateMobSpawningFlags();
worldserver.setSpawnSettings(this.isSpawningMonsters(), this.isSpawningAnimals());
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 6978abb97da6c33099db2e4ead3f33b0ca87bed1..74bddd7f37af06355549128bb0bfc1d7eedd2bb8 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1625,12 +1625,84 @@ public class ServerLevel extends Level implements WorldGenLevel {
return ((MapIndex) this.getServer().overworld().getDataStorage().computeIfAbsent(MapIndex.factory(), "idcounts")).getFreeAuxValueForMap();
}
+ // Paper start - helper function for configurable spawn radius
+ public void addTicketsForSpawn(int radiusInBlocks, BlockPos spawn) {
+ // In order to respect vanilla behavior, which is ensuring everything but the spawn border can tick, we add tickets
+ // with level 31 for the non-border spawn chunks
+ ServerChunkCache chunkproviderserver = this.getChunkSource();
+ int tickRadius = radiusInBlocks - 16;
+
+ // add ticking chunks
+ for (int x = -tickRadius; x <= tickRadius; x += 16) {
+ for (int z = -tickRadius; z <= tickRadius; z += 16) {
+ // radius of 2 will have the current chunk be level 31
+ chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, z)), 2, Unit.INSTANCE);
+ }
+ }
+
+ // add border chunks
+
+ // add border along x axis (including corner chunks)
+ for (int x = -radiusInBlocks; x <= radiusInBlocks; x += 16) {
+ // top
+ chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, radiusInBlocks)), 1, Unit.INSTANCE); // level 32
+ // bottom
+ chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, -radiusInBlocks)), 1, Unit.INSTANCE); // level 32
+ }
+
+ // add border along z axis (excluding corner chunks)
+ for (int z = -radiusInBlocks + 16; z < radiusInBlocks; z += 16) {
+ // right
+ chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(spawn.offset(radiusInBlocks, 0, z)), 1, Unit.INSTANCE); // level 32
+ // left
+ chunkproviderserver.addRegionTicket(TicketType.START, new ChunkPos(spawn.offset(-radiusInBlocks, 0, z)), 1, Unit.INSTANCE); // level 32
+ }
+ }
+ public void removeTicketsForSpawn(int radiusInBlocks, BlockPos spawn) {
+ // In order to respect vanilla behavior, which is ensuring everything but the spawn border can tick, we added tickets
+ // with level 31 for the non-border spawn chunks
+ ServerChunkCache chunkproviderserver = this.getChunkSource();
+ int tickRadius = radiusInBlocks - 16;
+
+ // remove ticking chunks
+ for (int x = -tickRadius; x <= tickRadius; x += 16) {
+ for (int z = -tickRadius; z <= tickRadius; z += 16) {
+ // radius of 2 will have the current chunk be level 31
+ chunkproviderserver.removeRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, z)), 2, Unit.INSTANCE);
+ }
+ }
+
+ // remove border chunks
+
+ // remove border along x axis (including corner chunks)
+ for (int x = -radiusInBlocks; x <= radiusInBlocks; x += 16) {
+ // top
+ chunkproviderserver.removeRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, radiusInBlocks)), 1, Unit.INSTANCE); // level 32
+ // bottom
+ chunkproviderserver.removeRegionTicket(TicketType.START, new ChunkPos(spawn.offset(x, 0, -radiusInBlocks)), 1, Unit.INSTANCE); // level 32
+ }
+
+ // remove border along z axis (excluding corner chunks)
+ for (int z = -radiusInBlocks + 16; z < radiusInBlocks; z += 16) {
+ // right
+ chunkproviderserver.removeRegionTicket(TicketType.START, new ChunkPos(spawn.offset(radiusInBlocks, 0, z)), 1, Unit.INSTANCE); // level 32
+ // left
+ chunkproviderserver.removeRegionTicket(TicketType.START, new ChunkPos(spawn.offset(-radiusInBlocks, 0, z)), 1, Unit.INSTANCE); // level 32
+ }
+ }
+ // Paper end
+
public void setDefaultSpawnPos(BlockPos pos, float angle) {
- ChunkPos chunkcoordintpair = new ChunkPos(new BlockPos(this.levelData.getXSpawn(), 0, this.levelData.getZSpawn()));
+ // Paper - configurable spawn radius
+ BlockPos prevSpawn = this.getSharedSpawnPos();
+ //ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(new BlockPosition(this.worldData.a(), 0, this.worldData.c()));
this.levelData.setSpawn(pos, angle);
- this.getChunkSource().removeRegionTicket(TicketType.START, chunkcoordintpair, 11, Unit.INSTANCE);
- this.getChunkSource().addRegionTicket(TicketType.START, new ChunkPos(pos), 11, Unit.INSTANCE);
+ if (this.keepSpawnInMemory) {
+ // if this keepSpawnInMemory is false a plugin has already removed our tickets, do not re-add
+ this.removeTicketsForSpawn(this.paperConfig().spawn.keepSpawnLoadedRange * 16, prevSpawn);
+ this.addTicketsForSpawn(this.paperConfig().spawn.keepSpawnLoadedRange * 16, pos);
+ }
this.getServer().getPlayerList().broadcastAll(new ClientboundSetDefaultSpawnPositionPacket(pos, angle));
}
diff --git a/src/main/java/net/minecraft/server/level/progress/ChunkProgressListener.java b/src/main/java/net/minecraft/server/level/progress/ChunkProgressListener.java
index 1b565b2809c2d367e21971c5154f35c9763995e6..b0f899835ded29aff108d1674bf4a1a6c89693db 100644
--- a/src/main/java/net/minecraft/server/level/progress/ChunkProgressListener.java
+++ b/src/main/java/net/minecraft/server/level/progress/ChunkProgressListener.java
@@ -12,4 +12,6 @@ public interface ChunkProgressListener {
void start();
void stop();
+
+ void setChunkRadius(int radius); // Paper - allow changing chunk radius
}
diff --git a/src/main/java/net/minecraft/server/level/progress/LoggerChunkProgressListener.java b/src/main/java/net/minecraft/server/level/progress/LoggerChunkProgressListener.java
index 4d2348df25410a0b5364eec066880326d6667dad..286aad3205ef8a9e21a47ef07893844fe857556a 100644
--- a/src/main/java/net/minecraft/server/level/progress/LoggerChunkProgressListener.java
+++ b/src/main/java/net/minecraft/server/level/progress/LoggerChunkProgressListener.java
@@ -11,12 +11,19 @@ import org.slf4j.Logger;
public class LoggerChunkProgressListener implements ChunkProgressListener {
private static final Logger LOGGER = LogUtils.getLogger();
- private final int maxCount;
+ private int maxCount;// Paper - remove final
private int count;
private long startTime;
private long nextTickTime = Long.MAX_VALUE;
public LoggerChunkProgressListener(int radius) {
+ // Paper start - Allow changing radius later for configurable spawn patch
+ this.setChunkRadius(radius); // Move to method
+ }
+
+ @Override
+ public void setChunkRadius(int radius) {
+ // Paper end
int i = radius * 2 + 1;
this.maxCount = i * i;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 00267800750ad2a4817b39c9c19a9336aff2299d..d9d327326f5fbf861f7b8ab6120ca2f2e8c8b360 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -1360,15 +1360,21 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public void setKeepSpawnInMemory(boolean keepLoaded) {
- world.keepSpawnInMemory = keepLoaded;
+ // Paper start - Configurable spawn radius
+ if (keepLoaded == world.keepSpawnInMemory) {
+ // do nothing, nothing has changed
+ return;
+ }
+ this.world.keepSpawnInMemory = keepLoaded;
// Grab the worlds spawn chunk
BlockPos chunkcoordinates = this.world.getSharedSpawnPos();
if (keepLoaded) {
- this.world.getChunkSource().addRegionTicket(TicketType.START, new ChunkPos(chunkcoordinates), 11, Unit.INSTANCE);
+ this.world.addTicketsForSpawn(this.world.paperConfig().spawn.keepSpawnLoadedRange * 16, chunkcoordinates);
} else {
- // TODO: doesn't work well if spawn changed....
- this.world.getChunkSource().removeRegionTicket(TicketType.START, new ChunkPos(chunkcoordinates), 11, Unit.INSTANCE);
+ // TODO: doesn't work well if spawn changed.... // Paper - resolved
+ this.world.removeTicketsForSpawn(this.world.paperConfig().spawn.keepSpawnLoadedRange * 16, chunkcoordinates);
}
+ // Paper end
}
@Override

View file

@ -0,0 +1,240 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Fri, 15 Feb 2019 01:08:19 -0500
Subject: [PATCH] Allow Saving of Oversized Chunks
Note 1.17 update: With 1.17, Entities are no longer stored in chunk slices, so this needs updating!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
The Minecraft World Region File format has a hard cap of 1MB per chunk.
This is due to the fact that the header of the file format only allocates
a single byte for sector count, meaning a maximum of 256 sectors, at 4k per sector.
This limit can be reached fairly easily with books, resulting in the chunk being unable
to save to the world. Worse off, is that nothing printed when this occured, and silently
performed a chunk rollback on next load.
This leads to security risk with duplication and is being actively exploited.
This patch catches the too large scenario, falls back and moves any large Entity
or Tile Entity into a new compound, and this compound is saved into a different file.
On Chunk Load, we check for oversized status, and if so, we load the extra file and
merge the Entities and Tile Entities from the oversized chunk back into the level to
then be loaded as normal.
Once a chunk is returned back to normal size, the oversized flag will clear, and no
extra data file will exist.
This fix maintains compatability with all existing Anvil Region Format tools as it
does not alter the save format. They will just not know about the extra entities.
This fix also maintains compatability if someone switches server jars to one without
this fix, as the data will remain in the oversized file. Once the server returns
to a jar with this fix, the data will be restored.
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
index 9e65b56fd2d5af6be305efa08e7c569e6598343a..bbf84e05d8c2ac5ad8e3837756421b6bda65f91c 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -18,8 +18,12 @@ import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
+import java.util.zip.InflaterInputStream; // Paper
+
import javax.annotation.Nullable;
import net.minecraft.Util;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.ChunkPos;
import org.slf4j.Logger;
@@ -44,6 +48,7 @@ public class RegionFile implements AutoCloseable {
private final IntBuffer timestamps;
@VisibleForTesting
protected final RegionBitmap usedSectors;
+ public final Path regionFile; // Paper
public RegionFile(Path file, Path directory, boolean dsync) throws IOException {
this(file, directory, RegionFileVersion.VERSION_DEFLATE, dsync);
@@ -51,6 +56,8 @@ public class RegionFile implements AutoCloseable {
public RegionFile(Path file, Path directory, RegionFileVersion outputChunkStreamVersion, boolean dsync) throws IOException {
this.header = ByteBuffer.allocateDirect(8192);
+ this.regionFile = file; // Paper
+ initOversizedState(); // Paper
this.usedSectors = new RegionBitmap();
this.version = outputChunkStreamVersion;
if (!Files.isDirectory(directory, new LinkOption[0])) {
@@ -420,6 +427,74 @@ public class RegionFile implements AutoCloseable {
}
+ // Paper start
+ private final byte[] oversized = new byte[1024];
+ private int oversizedCount = 0;
+
+ private synchronized void initOversizedState() throws IOException {
+ Path metaFile = getOversizedMetaFile();
+ if (Files.exists(metaFile)) {
+ final byte[] read = java.nio.file.Files.readAllBytes(metaFile);
+ System.arraycopy(read, 0, oversized, 0, oversized.length);
+ for (byte temp : oversized) {
+ oversizedCount += temp;
+ }
+ }
+ }
+
+ private static int getChunkIndex(int x, int z) {
+ return (x & 31) + (z & 31) * 32;
+ }
+ synchronized boolean isOversized(int x, int z) {
+ return this.oversized[getChunkIndex(x, z)] == 1;
+ }
+ synchronized void setOversized(int x, int z, boolean oversized) throws IOException {
+ final int offset = getChunkIndex(x, z);
+ boolean previous = this.oversized[offset] == 1;
+ this.oversized[offset] = (byte) (oversized ? 1 : 0);
+ if (!previous && oversized) {
+ oversizedCount++;
+ } else if (!oversized && previous) {
+ oversizedCount--;
+ }
+ if (previous && !oversized) {
+ Path oversizedFile = getOversizedFile(x, z);
+ if (Files.exists(oversizedFile)) {
+ Files.delete(oversizedFile);
+ }
+ }
+ if (oversizedCount > 0) {
+ if (previous != oversized) {
+ writeOversizedMeta();
+ }
+ } else if (previous) {
+ Path oversizedMetaFile = getOversizedMetaFile();
+ if (Files.exists(oversizedMetaFile)) {
+ Files.delete(oversizedMetaFile);
+ }
+ }
+ }
+
+ private void writeOversizedMeta() throws IOException {
+ java.nio.file.Files.write(getOversizedMetaFile(), oversized);
+ }
+
+ private Path getOversizedMetaFile() {
+ return this.regionFile.getParent().resolve(this.regionFile.getFileName().toString().replaceAll("\\.mca$", "") + ".oversized.nbt");
+ }
+
+ private Path getOversizedFile(int x, int z) {
+ return this.regionFile.getParent().resolve(this.regionFile.getFileName().toString().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt");
+ }
+
+ synchronized CompoundTag getOversizedData(int x, int z) throws IOException {
+ Path file = getOversizedFile(x, z);
+ try (DataInputStream out = new DataInputStream(new java.io.BufferedInputStream(new InflaterInputStream(Files.newInputStream(file))))) {
+ return NbtIo.read((java.io.DataInput) out);
+ }
+
+ }
+ // Paper end
private class ChunkBuffer extends ByteArrayOutputStream {
private final ChunkPos pos;
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index 81710e2853a75a47edd0abf3f5b30f8558b5abcd..e7aa20ec84fcbe74ca2a1a11697e083d713d6a1a 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -53,6 +53,71 @@ public final class RegionFileStorage implements AutoCloseable {
}
}
+ // Paper start
+ private static void printOversizedLog(String msg, Path file, int x, int z) {
+ org.apache.logging.log4j.LogManager.getLogger().fatal(msg + " (" + file.toString().replaceAll(".+[\\\\/]", "") + " - " + x + "," + z + ") Go clean it up to remove this message. /minecraft:tp " + (x<<4)+" 128 "+(z<<4) + " - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed.");
+ }
+
+ private static final int DEFAULT_SIZE_THRESHOLD = 1024 * 8;
+ private static final int OVERZEALOUS_TOTAL_THRESHOLD = 1024 * 64;
+ private static final int OVERZEALOUS_THRESHOLD = 1024;
+ private static int SIZE_THRESHOLD = DEFAULT_SIZE_THRESHOLD;
+ private static void resetFilterThresholds() {
+ SIZE_THRESHOLD = Math.max(1024 * 4, Integer.getInteger("Paper.FilterThreshhold", DEFAULT_SIZE_THRESHOLD));
+ }
+ static {
+ resetFilterThresholds();
+ }
+
+ static boolean isOverzealous() {
+ return SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD;
+ }
+
+
+ private static CompoundTag readOversizedChunk(RegionFile regionfile, ChunkPos chunkCoordinate) throws IOException {
+ synchronized (regionfile) {
+ try (DataInputStream datainputstream = regionfile.getChunkDataInputStream(chunkCoordinate)) {
+ CompoundTag oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z);
+ CompoundTag chunk = NbtIo.read((DataInput) datainputstream);
+ if (oversizedData == null) {
+ return chunk;
+ }
+ CompoundTag oversizedLevel = oversizedData.getCompound("Level");
+
+ mergeChunkList(chunk.getCompound("Level"), oversizedLevel, "Entities", "Entities");
+ mergeChunkList(chunk.getCompound("Level"), oversizedLevel, "TileEntities", "TileEntities");
+
+ return chunk;
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ throw throwable;
+ }
+ }
+ }
+
+ private static void mergeChunkList(CompoundTag level, CompoundTag oversizedLevel, String key, String oversizedKey) {
+ net.minecraft.nbt.ListTag levelList = level.getList(key, 10);
+ net.minecraft.nbt.ListTag oversizedList = oversizedLevel.getList(oversizedKey, 10);
+
+ if (!oversizedList.isEmpty()) {
+ levelList.addAll(oversizedList);
+ level.put(key, levelList);
+ }
+ }
+
+ private static int getNBTSize(net.minecraft.nbt.Tag nbtBase) {
+ DataOutputStream test = new DataOutputStream(new org.apache.commons.io.output.NullOutputStream());
+ try {
+ nbtBase.write(test);
+ return test.size();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return 0;
+ }
+ }
+
+ // Paper End
+
@Nullable
public CompoundTag read(ChunkPos pos) throws IOException {
// CraftBukkit start - SPIGOT-5680: There's no good reason to preemptively create files on read, save that for writing
@@ -63,6 +128,12 @@ public final class RegionFileStorage implements AutoCloseable {
// CraftBukkit end
DataInputStream datainputstream = regionfile.getChunkDataInputStream(pos);
+ // Paper start
+ if (regionfile.isOversized(pos.x, pos.z)) {
+ printOversizedLog("Loading Oversized Chunk!", regionfile.regionFile, pos.x, pos.z);
+ return readOversizedChunk(regionfile, pos);
+ }
+ // Paper end
CompoundTag nbttagcompound;
label43:
{
@@ -141,6 +212,7 @@ public final class RegionFileStorage implements AutoCloseable {
try {
NbtIo.write(nbt, (DataOutput) dataoutputstream);
+ regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
} catch (Throwable throwable) {
if (dataoutputstream != null) {
try {

View file

@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Sat, 20 Apr 2019 19:47:34 -0500
Subject: [PATCH] Expose the internal current tick
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 223e43758a009a9f5d97e9e89745bb12f2d72d2f..da2c850b1625758c0bb387930788b6e9f03555d9 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2749,5 +2749,10 @@ public final class CraftServer implements Server {
profile.getProperties().putAll(((CraftPlayer)player).getHandle().getGameProfile().getProperties());
return new com.destroystokyo.paper.profile.CraftPlayerProfile(profile);
}
+
+ @Override
+ public int getCurrentTick() {
+ return net.minecraft.server.MinecraftServer.currentTick;
+ }
// Paper end
}

View file

@ -0,0 +1,244 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 15 Jun 2019 08:54:33 -0700
Subject: [PATCH] Fix World#isChunkGenerated calls
Optimize World#loadChunk() too
This patch also adds a chunk status cache on region files (note that
its only purpose is to cache the status on DISK)
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 62c36060989acb54eb12a145909a68193c5e3035..a0fba2202b2060694485989094f7e6e4ab94caaa 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1172,9 +1172,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
private CompletableFuture<Optional<CompoundTag>> readChunk(ChunkPos chunkPos) {
- return this.read(chunkPos).thenApplyAsync((optional) -> {
- return optional.map((nbttagcompound) -> this.upgradeChunkTag(nbttagcompound, chunkPos)); // CraftBukkit
- }, Util.backgroundExecutor());
+ // Paper start - Cache chunk status on disk
+ try {
+ return CompletableFuture.completedFuture(Optional.ofNullable(this.readConvertChunkSync(chunkPos)));
+ } catch (Throwable thr) {
+ return CompletableFuture.failedFuture(thr);
+ }
+ // Paper end - Cache chunk status on disk
}
// CraftBukkit start
@@ -1183,6 +1187,63 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
// CraftBukkit end
}
+ // Paper start - Cache chunk status on disk
+ @Nullable
+ public CompoundTag readConvertChunkSync(ChunkPos pos) throws IOException {
+ CompoundTag nbttagcompound = this.readSync(pos);
+ // Paper start - Cache chunk status on disk
+ if (nbttagcompound == null) {
+ return null;
+ }
+
+ nbttagcompound = this.upgradeChunkTag(nbttagcompound, pos); // CraftBukkit
+ if (nbttagcompound == null) {
+ return null;
+ }
+
+ this.updateChunkStatusOnDisk(pos, nbttagcompound);
+
+ return nbttagcompound;
+ // Paper end
+ }
+
+ // Paper start - chunk status cache "api"
+ public ChunkStatus getChunkStatusOnDiskIfCached(ChunkPos chunkPos) {
+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFileIfLoaded(chunkPos);
+
+ return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
+ }
+
+ public ChunkStatus getChunkStatusOnDisk(ChunkPos chunkPos) throws IOException {
+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, true);
+
+ if (regionFile == null || !regionFileCache.chunkExists(chunkPos)) {
+ return null;
+ }
+
+ ChunkStatus status = regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
+
+ if (status != null) {
+ return status;
+ }
+
+ this.readChunk(chunkPos);
+
+ return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z);
+ }
+
+ public void updateChunkStatusOnDisk(ChunkPos chunkPos, @Nullable CompoundTag compound) throws IOException {
+ net.minecraft.world.level.chunk.storage.RegionFile regionFile = regionFileCache.getRegionFile(chunkPos, false);
+
+ regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkSerializer.getStatus(compound));
+ }
+
+ public ChunkAccess getUnloadingChunk(int chunkX, int chunkZ) {
+ ChunkHolder chunkHolder = io.papermc.paper.chunk.system.ChunkSystem.getUnloadingChunkHolder(this.level, chunkX, chunkZ);
+ return chunkHolder == null ? null : chunkHolder.getAvailableChunkNow();
+ }
+ // Paper end
+
boolean anyPlayerCloseEnoughForSpawning(ChunkPos pos) {
// Spigot start
return this.anyPlayerCloseEnoughForSpawning(pos, false);
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
index bbf84e05d8c2ac5ad8e3837756421b6bda65f91c..13ab2d2d8d0d3ab0facbae8285b09267765cc97d 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFile.java
@@ -50,6 +50,30 @@ public class RegionFile implements AutoCloseable {
protected final RegionBitmap usedSectors;
public final Path regionFile; // Paper
+ // Paper start - Cache chunk status
+ private final net.minecraft.world.level.chunk.ChunkStatus[] statuses = new net.minecraft.world.level.chunk.ChunkStatus[32 * 32];
+
+ private boolean closed;
+
+ // invoked on write/read
+ public void setStatus(int x, int z, net.minecraft.world.level.chunk.ChunkStatus status) {
+ if (this.closed) {
+ // We've used an invalid region file.
+ throw new IllegalStateException("RegionFile is closed");
+ }
+ this.statuses[getChunkLocation(x, z)] = status;
+ }
+
+ public net.minecraft.world.level.chunk.ChunkStatus getStatusIfCached(int x, int z) {
+ if (this.closed) {
+ // We've used an invalid region file.
+ throw new IllegalStateException("RegionFile is closed");
+ }
+ final int location = getChunkLocation(x, z);
+ return this.statuses[location];
+ }
+ // Paper end
+
public RegionFile(Path file, Path directory, boolean dsync) throws IOException {
this(file, directory, RegionFileVersion.VERSION_DEFLATE, dsync);
}
@@ -397,11 +421,13 @@ public class RegionFile implements AutoCloseable {
return this.getOffset(pos) != 0;
}
+ private static int getChunkLocation(int x, int z) { return (x & 31) + (z & 31) * 32; } // Paper - OBFHELPER - sort of, mirror of logic below
private static int getOffsetIndex(ChunkPos pos) {
return pos.getRegionLocalX() + pos.getRegionLocalZ() * 32;
}
public void close() throws IOException {
+ this.closed = true; // Paper
try {
this.padToFullSector();
} finally {
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
index e7aa20ec84fcbe74ca2a1a11697e083d713d6a1a..390569f89b21c375357fb71d7b7730f0ea789545 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/RegionFileStorage.java
@@ -212,6 +212,7 @@ public final class RegionFileStorage implements AutoCloseable {
try {
NbtIo.write(nbt, (DataOutput) dataoutputstream);
+ regionfile.setStatus(pos.x, pos.z, ChunkSerializer.getStatus(nbt)); // Paper - cache status on disk
regionfile.setOversized(pos.x, pos.z, false); // Paper - We don't do this anymore, mojang stores differently, but clear old meta flag if it exists to get rid of our own meta file once last oversized is gone
} catch (Throwable throwable) {
if (dataoutputstream != null) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index d9d327326f5fbf861f7b8ab6120ca2f2e8c8b360..b266cd21fec4c48d2fc4676eb30b1bfdaa4acd98 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -307,9 +307,23 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean isChunkGenerated(int x, int z) {
+ // Paper start - Fix this method
+ if (!Bukkit.isPrimaryThread()) {
+ return java.util.concurrent.CompletableFuture.supplyAsync(() -> {
+ return CraftWorld.this.isChunkGenerated(x, z);
+ }, world.getChunkSource().mainThreadProcessor).join();
+ }
+ ChunkAccess chunk = world.getChunkSource().getChunkAtImmediately(x, z);
+ if (chunk == null) {
+ chunk = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
+ }
+ if (chunk != null) {
+ return chunk instanceof ImposterProtoChunk || chunk instanceof net.minecraft.world.level.chunk.LevelChunk;
+ }
try {
- return this.isChunkLoaded(x, z) || this.world.getChunkSource().chunkMap.read(new ChunkPos(x, z)).get().isPresent();
- } catch (InterruptedException | ExecutionException ex) {
+ return world.getChunkSource().chunkMap.getChunkStatusOnDisk(new ChunkPos(x, z)) == ChunkStatus.FULL;
+ // Paper end
+ } catch (java.io.IOException ex) {
throw new RuntimeException(ex);
}
}
@@ -420,20 +434,48 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public boolean loadChunk(int x, int z, boolean generate) {
org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot
- ChunkAccess chunk = this.world.getChunkSource().getChunk(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper
+ // Paper start - Optimize this method
+ ChunkPos chunkPos = new ChunkPos(x, z);
- // If generate = false, but the chunk already exists, we will get this back.
- if (chunk instanceof ImposterProtoChunk) {
- // We then cycle through again to get the full chunk immediately, rather than after the ticket addition
- chunk = this.world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
- }
+ if (!generate) {
+ ChunkAccess immediate = world.getChunkSource().getChunkAtImmediately(x, z);
+ if (immediate == null) {
+ immediate = world.getChunkSource().chunkMap.getUnloadingChunk(x, z);
+ }
+ if (immediate != null) {
+ 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.getChunk(x, z); // make sure we're at ticket level 32 or lower
+ return true;
+ }
- if (chunk instanceof net.minecraft.world.level.chunk.LevelChunk) {
- this.world.getChunkSource().addRegionTicket(TicketType.PLUGIN, new ChunkPos(x, z), 1, Unit.INSTANCE);
- return true;
+ net.minecraft.world.level.chunk.storage.RegionFile file;
+ try {
+ file = world.getChunkSource().chunkMap.regionFileCache.getRegionFile(chunkPos, false);
+ } catch (java.io.IOException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ ChunkStatus status = file.getStatusIfCached(x, z);
+ if (!file.hasChunk(chunkPos) || (status != null && status != ChunkStatus.FULL)) {
+ return false;
+ }
+
+ ChunkAccess chunk = world.getChunkSource().getChunk(x, z, ChunkStatus.EMPTY, true);
+ if (!(chunk instanceof ImposterProtoChunk) && !(chunk instanceof net.minecraft.world.level.chunk.LevelChunk)) {
+ return false;
+ }
+
+ // fall through to load
+ // we do this so we do not re-read the chunk data on disk
}
- return false;
+ world.getChunkSource().addRegionTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE);
+ world.getChunkSource().getChunk(x, z, ChunkStatus.FULL, true);
+ return true;
+ // Paper end
}
@Override

View file

@ -0,0 +1,33 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 15 Jun 2019 10:28:25 -0700
Subject: [PATCH] Show blockstate location if we failed to read it
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
index 84550b6e4eaf62806a8ad83656d4051b09c9c97d..fbf2c487d7ea0e6530ab664ba706cbcf81fc60a3 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockEntityState.java
@@ -25,6 +25,7 @@ public class CraftBlockEntityState<T extends BlockEntity> extends CraftBlockStat
this.tileEntity = tileEntity;
+ try { // Paper - show location on failure
// Paper start
this.snapshotDisabled = DISABLE_SNAPSHOT;
if (DISABLE_SNAPSHOT) {
@@ -37,6 +38,14 @@ public class CraftBlockEntityState<T extends BlockEntity> extends CraftBlockStat
this.load(this.snapshot);
}
// Paper end
+ // Paper start - show location on failure
+ } catch (Throwable thr) {
+ if (thr instanceof ThreadDeath) {
+ throw (ThreadDeath)thr;
+ }
+ throw new RuntimeException("Failed to read BlockState at: world: " + this.getWorld().getName() + " location: (" + this.getX() + ", " + this.getY() + ", " + this.getZ() + ")", thr);
+ }
+ // Paper end
}
public void refreshSnapshot() {

View file

@ -0,0 +1,36 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 24 Mar 2019 01:01:32 -0400
Subject: [PATCH] Only count Natural Spawned mobs towards natural spawn mob
limit
This resolves the super common complaint about mobs not spawning.
This was ultimately a flaw in the vanilla count algorithim that allows
spawners and other misc mobs to count against the mob limit, which are
not bounded, and can prevent the entire world from spawning new.
I believe Bukkits changes around persistence may of actually made it
worse than vanilla.
This should fully solve all of the issues around it so that only natural
influences natural spawns.
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
index 9ed7abddf30d298a5abb04974efd1b470d015b09..fe38079d69f3e9987ad5ab077ae09d05017a681a 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
@@ -88,6 +88,13 @@ public final class NaturalSpawner {
MobCategory enumcreaturetype = entity.getType().getCategory();
if (enumcreaturetype != MobCategory.MISC) {
+ // Paper start - Only count natural spawns
+ if (!entity.level().paperConfig().entities.spawning.countAllMobsForSpawning &&
+ !(entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL ||
+ entity.spawnReason == org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
+ continue;
+ }
+ // Paper end
BlockPos blockposition = entity.blockPosition();
chunkSource.query(ChunkPos.asLong(blockposition), (chunk) -> {

View file

@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lucavon <lucavonlp@gmail.com>
Date: Tue, 23 Jul 2019 20:29:20 -0500
Subject: [PATCH] Configurable projectile relative velocity
This patch adds an option "disable relative projectile velocity", which, when
enabled, will cause projectiles to ignore the shooter's current velocity,
like they did in Minecraft 1.8 and prior.
If a player is falling, for example, their shooting range will be drastically
reduced, as a downwards velocity is applied to the projectile. This prevents
players from saving themselves from falling off floating islands, for example,
as a thrown ender pearl will not make it back to the island, while it would
have in 1.8.
While this could easily be done with plugins, too, there are multiple problems:
P1) If multiple plugins cancel the velocity by subtracting the shooter's velocity
from the projectile's velocity, the projectile's velocity would be different.
As there's no way to detect whether the projectile's velocity has already been
adjusted to ignore the player's velocity, plugins can't not do it if it's not
necessary.
P2) I've noticed some inconsistencies, e.g. weird velocity when shooting while
using an elytra. Checking for those inconsistencies is possible, but not as
efficient as just not applying the velocity in the first place.
P3) Solutions for 1) and especially 2) might not be future-proof, while this
server-internal fix makes this change future-proof.
diff --git a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
index e5da57b35cd82ee7b4e844cfe74289a71d38779a..24b549cb21926a02d736f0bbb991006b9453068d 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/Projectile.java
@@ -165,7 +165,7 @@ public abstract class Projectile extends Entity implements TraceableEntity {
this.shoot((double) f5, (double) f6, (double) f7, speed, divergence);
Vec3 vec3d = shooter.getDeltaMovement();
- this.setDeltaMovement(this.getDeltaMovement().add(vec3d.x, shooter.onGround() ? 0.0D : vec3d.y, vec3d.z));
+ if (!shooter.level().paperConfig().misc.disableRelativeProjectileVelocity) this.setDeltaMovement(this.getDeltaMovement().add(vec3d.x, shooter.onGround() ? 0.0D : vec3d.y, vec3d.z)); // Paper - allow disabling relative velocity
}
// CraftBukkit start - call projectile hit event

View file

@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kickash32 <kickash32@gmail.com>
Date: Tue, 30 Jul 2019 03:17:16 +0500
Subject: [PATCH] offset item frame ticking
diff --git a/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java b/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java
index 760e9e96cb567861f40a0b3debb58dc867be4026..66cf0a6cd1525ecf2615809210a26d55f445d07d 100644
--- a/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/decoration/HangingEntity.java
@@ -38,7 +38,7 @@ public abstract class HangingEntity extends Entity {
protected static final Predicate<Entity> HANGING_ENTITY = (entity) -> {
return entity instanceof HangingEntity;
};
- private int checkInterval;
+ private int checkInterval; { this.checkInterval = this.getId() % this.level().spigotConfig.hangingTickFrequency; } // Paper
public BlockPos pos;
protected Direction direction;

View file

@ -0,0 +1,25 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Tue, 13 Aug 2019 06:35:17 -0700
Subject: [PATCH] Fix MC-158900
The problem was we were checking isExpired() on the entry, but if it
was expired at that point, then it would be null.
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 3c4224472ce59d7a2c1748d510e4fc8f58a7d5e1..b4ded40387006821433db1de85ed1c2347757155 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -629,8 +629,10 @@ public abstract class PlayerList {
Player player = entity.getBukkitEntity();
PlayerLoginEvent event = new PlayerLoginEvent(player, loginlistener.connection.hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.connection.channel.remoteAddress()).getAddress());
- if (this.getBans().isBanned(gameprofile) && !this.getBans().get(gameprofile).hasExpired()) {
- UserBanListEntry gameprofilebanentry = (UserBanListEntry) this.bans.get(gameprofile);
+ // Paper start - Fix MC-158900
+ UserBanListEntry gameprofilebanentry;
+ if (getBans().isBanned(gameprofile) && (gameprofilebanentry = getBans().get(gameprofile)) != null) {
+ // Paper end
ichatmutablecomponent = Component.translatable("multiplayer.disconnect.banned.reason", gameprofilebanentry.getReason());
if (gameprofilebanentry.getExpires() != null) {

View file

@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: kickash32 <kickash32@gmail.com>
Date: Mon, 19 Aug 2019 19:42:35 +0500
Subject: [PATCH] Prevent consuming the wrong itemstack
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 0f7f90a6b358402c7a05217f59768448eabfce53..ac59a1e5ae4fe84af44220d721b60f2e157e0f1f 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -3710,9 +3710,14 @@ public abstract class LivingEntity extends Entity implements Attackable {
}
public void startUsingItem(InteractionHand hand) {
+ // Paper start - forwarder to method with forceUpdate parameter
+ this.startUsingItem(hand, false);
+ }
+ public void startUsingItem(InteractionHand hand, boolean forceUpdate) {
+ // Paper end
ItemStack itemstack = this.getItemInHand(hand);
- if (!itemstack.isEmpty() && !this.isUsingItem()) {
+ if (!itemstack.isEmpty() && !this.isUsingItem() || forceUpdate) { // Paper use override flag
this.useItem = itemstack;
this.useItemRemaining = itemstack.getUseDuration();
if (!this.level().isClientSide) {
@@ -3792,6 +3797,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
this.releaseUsingItem();
} else {
if (!this.useItem.isEmpty() && this.isUsingItem()) {
+ this.startUsingItem(this.getUsedItemHand(), true); // Paper
this.triggerItemUseEffects(this.useItem, 16);
// CraftBukkit start - fire PlayerItemConsumeEvent
ItemStack itemstack;
@@ -3826,8 +3832,8 @@ public abstract class LivingEntity extends Entity implements Attackable {
}
this.stopUsingItem();
- // Paper start - if the replacement is anything but the default, update the client inventory
- if (this instanceof ServerPlayer && !com.google.common.base.Objects.equal(defaultReplacement, itemstack)) {
+ // Paper start
+ if (this instanceof ServerPlayer) {
((ServerPlayer) this).getBukkitEntity().updateInventory();
}
// Paper end

View file

@ -0,0 +1,18 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Sat, 11 Sep 2021 11:56:51 +0200
Subject: [PATCH] Dont send unnecessary sign update
diff --git a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java
index 148ea4089c4f67894b28e21e961a661a2d291925..7e4fbf3cd57c74b61cec75c02eb35756a243de17 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/SignBlockEntity.java
@@ -186,6 +186,7 @@ public class SignBlockEntity extends BlockEntity implements CommandSource { // C
this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3);
} else {
SignBlockEntity.LOGGER.warn("Player {} just tried to change non-editable sign", player.getName().getString());
+ if (player.distanceToSqr(this.getBlockPos().getX(), this.getBlockPos().getY(), this.getBlockPos().getZ()) < 32 * 32) // Paper
((ServerPlayer) player).connection.send(this.getUpdatePacket()); // CraftBukkit
}
}

View file

@ -0,0 +1,18 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Wed, 9 Oct 2019 21:46:15 -0500
Subject: [PATCH] Add option to disable pillager patrols
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 65d9211b812995869e58900a2873583658122312..e5918fa3be107ac3a2fc8831fd78733a7506730a 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/PatrolSpawner.java
@@ -25,6 +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 (!spawnMonsters) {
return 0;
} else if (!world.getGameRules().getBoolean(GameRules.RULE_DO_PATROL_SPAWNING)) {

View file

@ -0,0 +1,210 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Byteflux <byte@byteflux.net>
Date: Wed, 2 Mar 2016 02:17:54 -0600
Subject: [PATCH] Flat bedrock generator settings
== AT ==
public net.minecraft.world.level.levelgen.SurfaceRules$Condition
public net.minecraft.world.level.levelgen.SurfaceRules$Context
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockX
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockY
public net.minecraft.world.level.levelgen.SurfaceRules$Context blockZ
public net.minecraft.world.level.levelgen.SurfaceRules$Context context
public net.minecraft.world.level.levelgen.SurfaceRules$Context randomState
public net.minecraft.world.level.levelgen.SurfaceRules$LazyYCondition
public net.minecraft.world.level.levelgen.SurfaceRules$LazyCondition
public net.minecraft.world.level.levelgen.SurfaceRules$VerticalGradientConditionSource
public net.minecraft.world.level.levelgen.SurfaceRules$SurfaceRule
public net.minecraft.world.level.levelgen.SurfaceSystem getOrCreateRandomFactory(Lnet/minecraft/resources/ResourceLocation;)Lnet/minecraft/world/level/levelgen/PositionalRandomFactory;
Co-authored-by: Noah van der Aa <ndvdaa@gmail.com>
diff --git a/src/main/java/net/minecraft/data/worldgen/SurfaceRuleData.java b/src/main/java/net/minecraft/data/worldgen/SurfaceRuleData.java
index 06e1774dfbb667aca69bc30c9675ed472cb5728c..1d5bc86516df3781aea894c3afd340421ba51a17 100644
--- a/src/main/java/net/minecraft/data/worldgen/SurfaceRuleData.java
+++ b/src/main/java/net/minecraft/data/worldgen/SurfaceRuleData.java
@@ -53,6 +53,66 @@ public class SurfaceRuleData {
return overworldLike(true, false, true);
}
+ // Paper start
+ // Taken from SurfaceRules$VerticalGradientConditionSource
+ // isRoof = true if roof, false if floor
+ public record PaperBedrockConditionSource(net.minecraft.resources.ResourceLocation randomName, VerticalAnchor trueAtAndBelow, VerticalAnchor falseAtAndAbove, boolean isRoof) implements SurfaceRules.ConditionSource {
+
+ public static final net.minecraft.util.KeyDispatchDataCodec<PaperBedrockConditionSource> CODEC = net.minecraft.util.KeyDispatchDataCodec.of(com.mojang.serialization.codecs.RecordCodecBuilder.<PaperBedrockConditionSource>mapCodec((instance) -> {
+ return instance.group(
+ net.minecraft.resources.ResourceLocation.CODEC.fieldOf("random_name").forGetter(PaperBedrockConditionSource::randomName),
+ VerticalAnchor.CODEC.fieldOf("true_at_and_below").forGetter(PaperBedrockConditionSource::trueAtAndBelow),
+ VerticalAnchor.CODEC.fieldOf("false_at_and_above").forGetter(PaperBedrockConditionSource::falseAtAndAbove),
+ com.mojang.serialization.Codec.BOOL.fieldOf("roof").forGetter(PaperBedrockConditionSource::isRoof)
+ ).apply(instance, PaperBedrockConditionSource::new);
+ }));
+
+ public PaperBedrockConditionSource(String randomName, net.minecraft.world.level.levelgen.VerticalAnchor trueAtAndBelow, net.minecraft.world.level.levelgen.VerticalAnchor falseAtAndAbove, boolean invert) {
+ this(new net.minecraft.resources.ResourceLocation(randomName), trueAtAndBelow, falseAtAndAbove, invert);
+ }
+
+ @Override
+ public net.minecraft.util.KeyDispatchDataCodec<PaperBedrockConditionSource>codec() {
+ return CODEC;
+ }
+
+ @Override
+ public SurfaceRules.Condition apply(SurfaceRules.Context context) {
+ boolean hasFlatBedrock = context.context.getWorld().paperConfig().environment.generateFlatBedrock;
+ int trueAtY = this.trueAtAndBelow().resolveY(context.context);
+ int falseAtY = this.falseAtAndAbove().resolveY(context.context);
+
+ int y = isRoof ? Math.max(falseAtY, trueAtY) - 1 : Math.min(falseAtY, trueAtY) ;
+ final int i = hasFlatBedrock ? y : trueAtY;
+ final int j = hasFlatBedrock ? y : falseAtY;
+ // TODO access transformer for randomState
+ final net.minecraft.world.level.levelgen.PositionalRandomFactory positionalRandomFactory = context.randomState.getOrCreateRandomFactory(this.randomName());
+
+ class VerticalGradientCondition extends SurfaceRules.LazyYCondition {
+ VerticalGradientCondition(SurfaceRules.Context context) {
+ super(context);
+ }
+
+ @Override
+ protected boolean compute() {
+ int y = this.context.blockY;
+ if (y <= i) {
+ return true;
+ } else if (y >= j) {
+ return false;
+ } else {
+ double d = net.minecraft.util.Mth.map((double) y, (double) i, (double) j, 1.0D, 0.0D);
+ net.minecraft.util.RandomSource randomSource = positionalRandomFactory.at(this.context.blockX, y, this.context.blockZ);
+ return (double) randomSource.nextFloat() < d;
+ }
+ }
+ }
+
+ return new VerticalGradientCondition(context);
+ }
+ }
+ // Paper end
+
public static SurfaceRules.RuleSource overworldLike(boolean surface, boolean bedrockRoof, boolean bedrockFloor) {
SurfaceRules.ConditionSource conditionSource = SurfaceRules.yBlockCheck(VerticalAnchor.absolute(97), 2);
SurfaceRules.ConditionSource conditionSource2 = SurfaceRules.yBlockCheck(VerticalAnchor.absolute(256), 0);
@@ -83,11 +143,11 @@ public class SurfaceRuleData {
SurfaceRules.RuleSource ruleSource9 = SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.WOODED_BADLANDS), SurfaceRules.ifTrue(conditionSource, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource16, COARSE_DIRT), SurfaceRules.ifTrue(conditionSource17, COARSE_DIRT), SurfaceRules.ifTrue(conditionSource18, COARSE_DIRT), ruleSource))), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.SWAMP), SurfaceRules.ifTrue(conditionSource6, SurfaceRules.ifTrue(SurfaceRules.not(conditionSource7), SurfaceRules.ifTrue(SurfaceRules.noiseCondition(Noises.SWAMP, 0.0D), WATER)))), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.MANGROVE_SWAMP), SurfaceRules.ifTrue(conditionSource5, SurfaceRules.ifTrue(SurfaceRules.not(conditionSource7), SurfaceRules.ifTrue(SurfaceRules.noiseCondition(Noises.SWAMP, 0.0D), WATER)))))), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.BADLANDS, Biomes.ERODED_BADLANDS, Biomes.WOODED_BADLANDS), SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource2, ORANGE_TERRACOTTA), SurfaceRules.ifTrue(conditionSource4, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource16, TERRACOTTA), SurfaceRules.ifTrue(conditionSource17, TERRACOTTA), SurfaceRules.ifTrue(conditionSource18, TERRACOTTA), SurfaceRules.bandlands())), SurfaceRules.ifTrue(conditionSource8, SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.ON_CEILING, RED_SANDSTONE), RED_SAND)), SurfaceRules.ifTrue(SurfaceRules.not(conditionSource11), ORANGE_TERRACOTTA), SurfaceRules.ifTrue(conditionSource10, WHITE_TERRACOTTA), ruleSource3)), SurfaceRules.ifTrue(conditionSource3, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource7, SurfaceRules.ifTrue(SurfaceRules.not(conditionSource4), ORANGE_TERRACOTTA)), SurfaceRules.bandlands())), SurfaceRules.ifTrue(SurfaceRules.UNDER_FLOOR, SurfaceRules.ifTrue(conditionSource10, WHITE_TERRACOTTA)))), SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, SurfaceRules.ifTrue(conditionSource8, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource12, SurfaceRules.ifTrue(conditionSource11, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource9, AIR), SurfaceRules.ifTrue(SurfaceRules.temperature(), ICE), WATER))), ruleSource8))), SurfaceRules.ifTrue(conditionSource10, SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, SurfaceRules.ifTrue(conditionSource12, SurfaceRules.ifTrue(conditionSource11, WATER))), SurfaceRules.ifTrue(SurfaceRules.UNDER_FLOOR, ruleSource7), SurfaceRules.ifTrue(conditionSource14, SurfaceRules.ifTrue(SurfaceRules.DEEP_UNDER_FLOOR, SANDSTONE)), SurfaceRules.ifTrue(conditionSource15, SurfaceRules.ifTrue(SurfaceRules.VERY_DEEP_UNDER_FLOOR, SANDSTONE)))), SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.FROZEN_PEAKS, Biomes.JAGGED_PEAKS), STONE), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.WARM_OCEAN, Biomes.LUKEWARM_OCEAN, Biomes.DEEP_LUKEWARM_OCEAN), ruleSource2), ruleSource3)));
ImmutableList.Builder<SurfaceRules.RuleSource> builder = ImmutableList.builder();
if (bedrockRoof) {
- builder.add(SurfaceRules.ifTrue(SurfaceRules.not(SurfaceRules.verticalGradient("bedrock_roof", VerticalAnchor.belowTop(5), VerticalAnchor.top())), BEDROCK));
+ builder.add(SurfaceRules.ifTrue(SurfaceRules.not(new PaperBedrockConditionSource("bedrock_roof", VerticalAnchor.belowTop(5), VerticalAnchor.top(), true)), BEDROCK)); // Paper
}
if (bedrockFloor) {
- builder.add(SurfaceRules.ifTrue(SurfaceRules.verticalGradient("bedrock_floor", VerticalAnchor.bottom(), VerticalAnchor.aboveBottom(5)), BEDROCK));
+ builder.add(SurfaceRules.ifTrue(new PaperBedrockConditionSource("bedrock_floor", VerticalAnchor.bottom(), VerticalAnchor.aboveBottom(5), false), BEDROCK)); // Paper
}
SurfaceRules.RuleSource ruleSource10 = SurfaceRules.ifTrue(SurfaceRules.abovePreliminarySurface(), ruleSource9);
@@ -112,7 +172,7 @@ public class SurfaceRuleData {
SurfaceRules.ConditionSource conditionSource11 = SurfaceRules.noiseCondition(Noises.NETHER_WART, 1.17D);
SurfaceRules.ConditionSource conditionSource12 = SurfaceRules.noiseCondition(Noises.NETHER_STATE_SELECTOR, 0.0D);
SurfaceRules.RuleSource ruleSource = SurfaceRules.ifTrue(conditionSource9, SurfaceRules.ifTrue(conditionSource3, SurfaceRules.ifTrue(conditionSource4, GRAVEL)));
- return SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.verticalGradient("bedrock_floor", VerticalAnchor.bottom(), VerticalAnchor.aboveBottom(5)), BEDROCK), SurfaceRules.ifTrue(SurfaceRules.not(SurfaceRules.verticalGradient("bedrock_roof", VerticalAnchor.belowTop(5), VerticalAnchor.top())), BEDROCK), SurfaceRules.ifTrue(conditionSource5, NETHERRACK), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.BASALT_DELTAS), SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.UNDER_CEILING, BASALT), SurfaceRules.ifTrue(SurfaceRules.UNDER_FLOOR, SurfaceRules.sequence(ruleSource, SurfaceRules.ifTrue(conditionSource12, BASALT), BLACKSTONE)))), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.SOUL_SAND_VALLEY), SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.UNDER_CEILING, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource12, SOUL_SAND), SOUL_SOIL)), SurfaceRules.ifTrue(SurfaceRules.UNDER_FLOOR, SurfaceRules.sequence(ruleSource, SurfaceRules.ifTrue(conditionSource12, SOUL_SAND), SOUL_SOIL)))), SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.not(conditionSource2), SurfaceRules.ifTrue(conditionSource6, LAVA)), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.WARPED_FOREST), SurfaceRules.ifTrue(SurfaceRules.not(conditionSource10), SurfaceRules.ifTrue(conditionSource, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource11, WARPED_WART_BLOCK), WARPED_NYLIUM)))), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.CRIMSON_FOREST), SurfaceRules.ifTrue(SurfaceRules.not(conditionSource10), SurfaceRules.ifTrue(conditionSource, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource11, NETHER_WART_BLOCK), CRIMSON_NYLIUM)))))), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.NETHER_WASTES), SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.UNDER_FLOOR, SurfaceRules.ifTrue(conditionSource7, SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.not(conditionSource6), SurfaceRules.ifTrue(conditionSource3, SurfaceRules.ifTrue(conditionSource4, SOUL_SAND))), NETHERRACK))), SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, SurfaceRules.ifTrue(conditionSource, SurfaceRules.ifTrue(conditionSource4, SurfaceRules.ifTrue(conditionSource8, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource2, GRAVEL), SurfaceRules.ifTrue(SurfaceRules.not(conditionSource6), GRAVEL)))))))), NETHERRACK);
+ return SurfaceRules.sequence(SurfaceRules.ifTrue(new PaperBedrockConditionSource("bedrock_floor", VerticalAnchor.bottom(), VerticalAnchor.aboveBottom(5), false), BEDROCK), SurfaceRules.ifTrue(SurfaceRules.not(new PaperBedrockConditionSource("bedrock_roof", VerticalAnchor.belowTop(5), VerticalAnchor.top(), true)), BEDROCK), SurfaceRules.ifTrue(conditionSource5, NETHERRACK), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.BASALT_DELTAS), SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.UNDER_CEILING, BASALT), SurfaceRules.ifTrue(SurfaceRules.UNDER_FLOOR, SurfaceRules.sequence(ruleSource, SurfaceRules.ifTrue(conditionSource12, BASALT), BLACKSTONE)))), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.SOUL_SAND_VALLEY), SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.UNDER_CEILING, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource12, SOUL_SAND), SOUL_SOIL)), SurfaceRules.ifTrue(SurfaceRules.UNDER_FLOOR, SurfaceRules.sequence(ruleSource, SurfaceRules.ifTrue(conditionSource12, SOUL_SAND), SOUL_SOIL)))), SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.not(conditionSource2), SurfaceRules.ifTrue(conditionSource6, LAVA)), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.WARPED_FOREST), SurfaceRules.ifTrue(SurfaceRules.not(conditionSource10), SurfaceRules.ifTrue(conditionSource, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource11, WARPED_WART_BLOCK), WARPED_NYLIUM)))), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.CRIMSON_FOREST), SurfaceRules.ifTrue(SurfaceRules.not(conditionSource10), SurfaceRules.ifTrue(conditionSource, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource11, NETHER_WART_BLOCK), CRIMSON_NYLIUM)))))), SurfaceRules.ifTrue(SurfaceRules.isBiome(Biomes.NETHER_WASTES), SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.UNDER_FLOOR, SurfaceRules.ifTrue(conditionSource7, SurfaceRules.sequence(SurfaceRules.ifTrue(SurfaceRules.not(conditionSource6), SurfaceRules.ifTrue(conditionSource3, SurfaceRules.ifTrue(conditionSource4, SOUL_SAND))), NETHERRACK))), SurfaceRules.ifTrue(SurfaceRules.ON_FLOOR, SurfaceRules.ifTrue(conditionSource, SurfaceRules.ifTrue(conditionSource4, SurfaceRules.ifTrue(conditionSource8, SurfaceRules.sequence(SurfaceRules.ifTrue(conditionSource2, GRAVEL), SurfaceRules.ifTrue(SurfaceRules.not(conditionSource6), GRAVEL)))))))), NETHERRACK);
}
public static SurfaceRules.RuleSource end() {
diff --git a/src/main/java/net/minecraft/server/Bootstrap.java b/src/main/java/net/minecraft/server/Bootstrap.java
index c887d34171f89c731d76c4ca92c70be2b1edc1e6..df6752b5c77bc88a4fcf1ffe918b24d41cd30ad4 100644
--- a/src/main/java/net/minecraft/server/Bootstrap.java
+++ b/src/main/java/net/minecraft/server/Bootstrap.java
@@ -78,6 +78,7 @@ public class Bootstrap {
CauldronInteraction.bootStrap();
// Paper start
BuiltInRegistries.bootStrap(() -> {
+ net.minecraft.core.Registry.register(net.minecraft.core.registries.BuiltInRegistries.MATERIAL_CONDITION, new net.minecraft.resources.ResourceLocation("paper", "bedrock_condition_source"), net.minecraft.data.worldgen.SurfaceRuleData.PaperBedrockConditionSource.CODEC.codec());
io.papermc.paper.plugin.entrypoint.LaunchEntryPointHandler.enterBootstrappers(); // Paper - Entrypoint for bootstrapping
});
// Paper end
diff --git a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
index 54308f1decc3982f30bf8b7a8a9d8865bfdbb9fd..0fca35ac82a1854260980b7c2664be82033e3bad 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java
@@ -207,7 +207,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
@Override
public void buildSurface(WorldGenRegion region, StructureManager structures, RandomState noiseConfig, ChunkAccess chunk) {
if (!SharedConstants.debugVoidTerrain(chunk.getPos())) {
- WorldGenerationContext worldgenerationcontext = new WorldGenerationContext(this, region);
+ WorldGenerationContext worldgenerationcontext = new WorldGenerationContext(this, region, region.getMinecraftWorld()); // Paper
this.buildSurface(chunk, worldgenerationcontext, noiseConfig, structures, region.getBiomeManager(), region.registryAccess().registryOrThrow(Registries.BIOME), Blender.of(region));
}
@@ -235,7 +235,7 @@ public final class NoiseBasedChunkGenerator extends ChunkGenerator {
return this.createNoiseChunk(ichunkaccess1, structureAccessor, Blender.of(chunkRegion), noiseConfig);
});
Aquifer aquifer = noisechunk.aquifer();
- CarvingContext carvingcontext = new CarvingContext(this, chunkRegion.registryAccess(), chunk.getHeightAccessorForGeneration(), noisechunk, noiseConfig, ((NoiseGeneratorSettings) this.settings.value()).surfaceRule());
+ CarvingContext carvingcontext = new CarvingContext(this, chunkRegion.registryAccess(), chunk.getHeightAccessorForGeneration(), noisechunk, noiseConfig, ((NoiseGeneratorSettings) this.settings.value()).surfaceRule(), chunkRegion.getMinecraftWorld()); // Paper
CarvingMask carvingmask = ((ProtoChunk) chunk).getOrCreateCarvingMask(carverStep);
for (int j = -8; j <= 8; ++j) {
diff --git a/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java b/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
index b99283c31193e2110f6e3f39c23dbfc2442bab2b..1c9d9ecdafb2bd04348045ba0404da052dcd6437 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/WorldGenerationContext.java
@@ -6,10 +6,13 @@ import net.minecraft.world.level.chunk.ChunkGenerator;
public class WorldGenerationContext {
private final int minY;
private final int height;
+ private final @javax.annotation.Nullable net.minecraft.world.level.Level level; // Paper
- public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world) {
+ public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world) { this(generator, world, null); } // Paper
+ public WorldGenerationContext(ChunkGenerator generator, LevelHeightAccessor world, @org.jetbrains.annotations.Nullable net.minecraft.world.level.Level level) { // Paper
this.minY = Math.max(world.getMinBuildHeight(), generator.getMinY());
this.height = Math.min(world.getHeight(), generator.getGenDepth());
+ this.level = level; // Paper
}
public int getMinGenY() {
@@ -19,4 +22,13 @@ public class WorldGenerationContext {
public int getGenDepth() {
return this.height;
}
+
+ // Paper start
+ public net.minecraft.world.level.Level getWorld() {
+ if (this.level == null) {
+ throw new NullPointerException("WorldGenerationContext was initialized without a Level, but WorldGenerationContext#getWorld was called");
+ }
+ return this.level;
+ }
+ // Paper end
}
diff --git a/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java b/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
index a745458ea3581ea91a68c863e3fd0a0292d73a61..f84ee8afe95f912a972e37fbae7a06ecdd3aba06 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/carver/CarvingContext.java
@@ -21,8 +21,8 @@ public class CarvingContext extends WorldGenerationContext {
private final RandomState randomState;
private final SurfaceRules.RuleSource surfaceRule;
- public CarvingContext(NoiseBasedChunkGenerator noiseChunkGenerator, RegistryAccess registryManager, LevelHeightAccessor heightLimitView, NoiseChunk chunkNoiseSampler, RandomState noiseConfig, SurfaceRules.RuleSource materialRule) {
- super(noiseChunkGenerator, heightLimitView);
+ public CarvingContext(NoiseBasedChunkGenerator noiseChunkGenerator, RegistryAccess registryManager, LevelHeightAccessor heightLimitView, NoiseChunk chunkNoiseSampler, RandomState noiseConfig, SurfaceRules.RuleSource materialRule, @javax.annotation.Nullable net.minecraft.world.level.Level level) { // Paper
+ super(noiseChunkGenerator, heightLimitView, level); // Paper
this.registryAccess = registryManager;
this.noiseChunk = chunkNoiseSampler;
this.randomState = noiseConfig;
diff --git a/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java b/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
index 640c2683c842655bbaee8f293f1c2613ef44844e..53d818b0cc602f827d0b907e293515f6810c6792 100644
--- a/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
+++ b/src/main/java/net/minecraft/world/level/levelgen/placement/PlacementContext.java
@@ -18,7 +18,7 @@ public class PlacementContext extends WorldGenerationContext {
private final Optional<PlacedFeature> topFeature;
public PlacementContext(WorldGenLevel world, ChunkGenerator generator, Optional<PlacedFeature> placedFeature) {
- super(generator, world);
+ super(generator, world, world.getLevel()); // Paper
this.level = world;
this.generator = generator;
this.topFeature = placedFeature;

View file

@ -0,0 +1,20 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Callahan <mr.callahhh@gmail.com>
Date: Mon, 13 Jan 2020 23:47:28 -0600
Subject: [PATCH] Prevent sync chunk loads when villagers try to find beds
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/SleepInBed.java b/src/main/java/net/minecraft/world/entity/ai/behavior/SleepInBed.java
index 964fc8615d071d33c821f0c54ac1ebd4884df0cd..dcfc668503c50927b4a4dbb3988d531215d513f8 100644
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/SleepInBed.java
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/SleepInBed.java
@@ -43,7 +43,8 @@ public class SleepInBed extends Behavior<LivingEntity> {
}
}
- BlockState blockState = world.getBlockState(globalPos.pos());
+ BlockState blockState = world.getBlockStateIfLoaded(globalPos.pos()); // Paper
+ if (blockState == null) { return false; } // Paper
return globalPos.pos().closerToCenterThan(entity.position(), 2.0D) && blockState.is(BlockTags.BEDS) && !blockState.getValue(BedBlock.OCCUPIED);
}
}

View file

@ -0,0 +1,50 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
Date: Wed, 18 Dec 2019 22:21:35 -0600
Subject: [PATCH] MC-145656 Fix Follow Range Initial Target
diff --git a/src/main/java/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java b/src/main/java/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
index 0dad5be671f990d0edf0a155e2534b3812438902..370e601ff5adccc3924055b69b42dd2f1970ae45 100644
--- a/src/main/java/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
+++ b/src/main/java/net/minecraft/world/entity/ai/goal/target/NearestAttackableTargetGoal.java
@@ -38,6 +38,7 @@ public class NearestAttackableTargetGoal<T extends LivingEntity> extends TargetG
this.randomInterval = reducedTickDelay(reciprocalChance);
this.setFlags(EnumSet.of(Goal.Flag.TARGET));
this.targetConditions = TargetingConditions.forCombat().range(this.getFollowDistance()).selector(targetPredicate);
+ if (mob.level().paperConfig().entities.entitiesTargetWithFollowRange) this.targetConditions.useFollowRange(); // Paper
}
@Override
diff --git a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
index f29823f2e8a54bd4e81e2940b5c505b152f23e88..58422f00c7d64dbd1cf6d7211c9838875cbe7778 100644
--- a/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
+++ b/src/main/java/net/minecraft/world/entity/ai/targeting/TargetingConditions.java
@@ -76,7 +76,7 @@ public class TargetingConditions {
if (this.range > 0.0D) {
double d = this.testInvisible ? targetEntity.getVisibilityPercent(baseEntity) : 1.0D;
- double e = Math.max(this.range * d, 2.0D);
+ double e = Math.max((this.useFollowRange ? this.getFollowRange(baseEntity) : this.range) * d, 2.0D); // Paper
double f = baseEntity.distanceToSqr(targetEntity.getX(), targetEntity.getY(), targetEntity.getZ());
if (f > e * e) {
return false;
@@ -94,4 +94,18 @@ public class TargetingConditions {
return true;
}
}
+
+ // Paper start
+ private boolean useFollowRange = false;
+
+ public TargetingConditions useFollowRange() {
+ this.useFollowRange = true;
+ return this;
+ }
+
+ private double getFollowRange(LivingEntity entityliving) {
+ net.minecraft.world.entity.ai.attributes.AttributeInstance attributeinstance = entityliving.getAttribute(net.minecraft.world.entity.ai.attributes.Attributes.FOLLOW_RANGE);
+ return attributeinstance == null ? 16.0D : attributeinstance.getValue();
+ }
+ // Paper end
}

View file

@ -0,0 +1,119 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sat, 21 Jul 2018 14:27:34 -0400
Subject: [PATCH] Duplicate UUID Resolve Option
Due to a bug in https://github.com/PaperMC/Paper/commit/2e29af3df05ec0a383f48be549d1c03200756d24
which was added all the way back in March of 2016, it was unknown (potentially not at the time)
that an entity might actually change the seed of the random object.
At some point, EntitySquid did start setting the seed. Due to this shared random, this caused
every entity to use a Random object with a predictable seed.
This has caused entities to potentially generate with the same UUID....
Over the years, servers have had entities disappear, but no sign of trouble
because CraftBukkit removed the log lines indicating that something was wrong.
We have fixed the root issue causing duplicate UUID's, however we now have chunk
files full of entities that have the same UUID as another entity!
When these chunks load, the 2nd entity will not be added to the world correctly.
If that chunk loads in a different order in the future, then it will reverse and the
missing one is now the one added to the world and not the other. This results in very
inconsistent entity behavior.
This change allows you to recover any duplicate entity by generating a new UUID for it.
This also lets you delete them instead if you don't want to risk having new entities added to
the world that you previously did not see.
But for those who are ok with leaving this inconsistent behavior, you may use WARN or NOTHING options.
It is recommended you regenerate the entities, as these were legit entities, and deserve your love.
diff --git a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
index 95eac2e12a16938d81ab512b00e90c5234b42834..9aea7de906103e4a8dcbcac397cc05ce1abb4aa2 100644
--- a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
+++ b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
@@ -224,7 +224,17 @@ public final class ChunkSystem {
}
public static void onEntityPreAdd(final ServerLevel level, final Entity entity) {
-
+ // Paper start - duplicate uuid resolving
+ if (net.minecraft.server.level.ChunkMap.checkDupeUUID(level, entity)) {
+ return;
+ }
+ if (net.minecraft.world.level.Level.DEBUG_ENTITIES && ((Entity) entity).level().paperConfig().entities.spawning.duplicateUuid.mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.NOTHING) {
+ if (((Entity) entity).addedToWorldStack != null) {
+ ((Entity) entity).addedToWorldStack.printStackTrace();
+ }
+ net.minecraft.server.level.ServerLevel.getAddToWorldStackTrace((Entity) entity).printStackTrace();
+ }
+ // Paper end - duplicate uuid resolving
}
public static void onChunkHolderCreate(final ServerLevel level, final ChunkHolder holder) {
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index a0fba2202b2060694485989094f7e6e4ab94caaa..66046c763872a031c65db17502b33199a40fd093 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -870,6 +870,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
entity.discard();
needsRemoval = true;
}
+ checkDupeUUID(world, entity); // Paper
return !needsRemoval;
}));
// CraftBukkit end
@@ -920,6 +921,49 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
});
}
+ // Paper start
+ // rets true if to prevent the entity from being added
+ public static boolean checkDupeUUID(ServerLevel level, Entity entity) {
+ io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode mode = level.paperConfig().entities.spawning.duplicateUuid.mode;
+ if (mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.WARN
+ && mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.DELETE
+ && mode != io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.SAFE_REGEN) {
+ return false;
+ }
+ Entity other = level.getEntity(entity.getUUID());
+
+ if (other == null || other == entity) {
+ return false;
+ }
+
+ if (mode == io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DuplicateUUID.DuplicateUUIDMode.SAFE_REGEN && other != null && !other.isRemoved()
+ && Objects.equals(other.getEncodeId(), entity.getEncodeId())
+ && entity.getBukkitEntity().getLocation().distance(other.getBukkitEntity().getLocation()) < level.paperConfig().entities.spawning.duplicateUuid.safeRegenDeleteRange
+ ) {
+ if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + " because it was near the duplicate and likely an actual duplicate. See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
+ entity.discard();
+ return true;
+ }
+ if (other != null && !other.isRemoved()) {
+ switch (mode) {
+ case SAFE_REGEN: {
+ entity.setUUID(java.util.UUID.randomUUID());
+ if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", regenerated UUID for " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
+ break;
+ }
+ case DELETE: {
+ if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
+ entity.discard();
+ return true;
+ }
+ default:
+ if (ServerLevel.DEBUG_ENTITIES) LOGGER.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", doing nothing to " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
+ break;
+ }
+ }
+ return false;
+ }
+ // Paper end
public CompletableFuture<Either<LevelChunk, ChunkHolder.ChunkLoadingFailure>> prepareTickingChunk(ChunkHolder holder) {
CompletableFuture<Either<List<ChunkAccess>, ChunkHolder.ChunkLoadingFailure>> completablefuture = this.getChunkRangeFuture(holder, 1, (i) -> {
return ChunkStatus.FULL;

View file

@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shane Freeder <theboyetronic@gmail.com>
Date: Tue, 24 Dec 2019 00:35:42 +0000
Subject: [PATCH] PlayerDeathEvent#shouldDropExperience
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index e66d24174d5834cb9526fa4a238d0765bef827b2..394f2b87010454ff2e4898f3ee5085c68d91952a 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -926,7 +926,7 @@ public class ServerPlayer extends Player {
this.tellNeutralMobsThatIDied();
}
// SPIGOT-5478 must be called manually now
- this.dropExperience();
+ if (event.shouldDropExperience()) this.dropExperience(); // Paper - tie to event
// we clean the player's inventory after the EntityDeathEvent is called so plugins can get the exact state of the inventory.
if (!event.getKeepInventory()) {
// Paper start - replace logic

View file

@ -0,0 +1,18 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <Blake.Galbreath@GMail.com>
Date: Sun, 5 Jan 2020 17:24:34 -0600
Subject: [PATCH] Prevent bees loading chunks checking hive position
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 407788ef7b0076e3bc5ec565af0470f1ab4865fa..4111ac5efa63c8063734646d0a8a8154f52f653d 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Bee.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Bee.java
@@ -502,6 +502,7 @@ public class Bee extends Animal implements NeutralMob, FlyingAnimal {
} else if (this.isTooFarAway(this.hivePos)) {
return false;
} else {
+ if (this.level().getChunkIfLoadedImmediately(this.hivePos.getX() >> 4, this.hivePos.getZ() >> 4) == null) return true; // Paper - just assume the hive is still there, no need to load the chunk(s)
BlockEntity tileentity = this.level().getBlockEntity(this.hivePos);
return tileentity != null && tileentity.getType() == BlockEntityType.BEEHIVE;

View file

@ -0,0 +1,32 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 3 Nov 2016 20:28:12 -0400
Subject: [PATCH] Don't load Chunks from Hoppers and other things
Hoppers call this to I guess "get the primary side" of a double sided chest.
If the double sided chest crosses chunk lines, it causes the chunk to load.
This will end up causing sync chunk loads, which will unload with Chunk GC,
only to be reloaded again the next tick.
This of course is undesirable, so just return the loaded side as "primary"
and treat it as a single chest if the other sides are unloaded
diff --git a/src/main/java/net/minecraft/world/level/block/DoubleBlockCombiner.java b/src/main/java/net/minecraft/world/level/block/DoubleBlockCombiner.java
index ff2a7b08fe70adaecdaa508baadcfe40416519e0..6c334703c816d2a04f97006c5796c658f34a12a4 100644
--- a/src/main/java/net/minecraft/world/level/block/DoubleBlockCombiner.java
+++ b/src/main/java/net/minecraft/world/level/block/DoubleBlockCombiner.java
@@ -25,7 +25,12 @@ public class DoubleBlockCombiner {
return new DoubleBlockCombiner.NeighborCombineResult.Single<>(blockEntity);
} else {
BlockPos blockPos = pos.relative(function.apply(state));
- BlockState blockState = world.getBlockState(blockPos);
+ // Paper start
+ BlockState blockState = world.getBlockStateIfLoaded(blockPos);
+ if (blockState == null) {
+ return new DoubleBlockCombiner.NeighborCombineResult.Single<>(blockEntity);
+ }
+ // Paper end
if (blockState.is(state.getBlock())) {
DoubleBlockCombiner.BlockType blockType2 = typeMapper.apply(blockState);
if (blockType2 != DoubleBlockCombiner.BlockType.SINGLE && blockType != blockType2 && blockState.getValue(directionProperty) == state.getValue(directionProperty)) {

View file

@ -0,0 +1,27 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 11 Jan 2020 21:50:56 -0800
Subject: [PATCH] Optimise EntityGetter#getPlayerByUUID
Use the PlayerList map instead of iterating over all players
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index 74bddd7f37af06355549128bb0bfc1d7eedd2bb8..0b928a1cf86de9c83fd50472226be48443ed65a6 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -326,6 +326,15 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
// Paper end
+ // Paper start - optimise getPlayerByUUID
+ @Nullable
+ @Override
+ public Player getPlayerByUUID(UUID uuid) {
+ final Player player = this.getServer().getPlayerList().getPlayer(uuid);
+ return player != null && player.level() == this ? player : null;
+ }
+ // Paper end
+
// Add env and gen to constructor, IWorldDataServer -> WorldDataServer
public ServerLevel(MinecraftServer minecraftserver, Executor executor, LevelStorageSource.LevelStorageAccess convertable_conversionsession, PrimaryLevelData iworlddataserver, ResourceKey<Level> resourcekey, LevelStem worlddimension, ChunkProgressListener worldloadlistener, boolean flag, long i, List<CustomSpawner> list, boolean flag1, @Nullable RandomSequences randomsequences, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider) {
// IRegistryCustom.Dimension iregistrycustom_dimension = minecraftserver.registryAccess(); // CraftBukkit - decompile error

View file

@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: AJMFactsheets <AJMFactsheets@gmail.com>
Date: Fri, 17 Jan 2020 17:17:54 -0600
Subject: [PATCH] Fix items not falling correctly
Since 1.14, Mojang has added an optimization which skips checking if
an item should fall every fourth tick.
However, Spigot's entity activation range class also has an
optimization which skips ticking active entities every fourth tick.
This can result in a state where an item will never properly fall
due to its move method never being called.
This patch resolves the conflict by offsetting checking Spigot's entity
activation range check from an item's move method.
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
index fda34d93a5b75919c840d3bc0efa0651e5eb4843..cecd7bd4a6dd66cfb2d632a232ff469e5fdcba44 100644
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
@@ -150,7 +150,7 @@ public class ItemEntity extends Entity implements TraceableEntity {
}
}
- if (!this.onGround() || this.getDeltaMovement().horizontalDistanceSqr() > 9.999999747378752E-6D || (this.tickCount + this.getId()) % 4 == 0) {
+ if (!this.onGround() || this.getDeltaMovement().horizontalDistanceSqr() > 9.999999747378752E-6D || (this.tickCount + this.getId()) % 4 == 0) { // Paper - Diff on change
this.move(MoverType.SELF, this.getDeltaMovement());
float f1 = 0.98F;
diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java
index d9149580408094784794bc0575a00bebe2e7b4d4..5d470b424d52ae402ec0e950263ad54cf6f7e3ef 100644
--- a/src/main/java/org/spigotmc/ActivationRange.java
+++ b/src/main/java/org/spigotmc/ActivationRange.java
@@ -251,7 +251,7 @@ public class ActivationRange
isActive = true;
}
// Add a little performance juice to active entities. Skip 1/4 if not immune.
- } else if ( !entity.defaultActivationState && entity.tickCount % 4 == 0 && !ActivationRange.checkEntityImmunities( entity ) )
+ } else if ( !entity.defaultActivationState && (entity.tickCount + entity.getId()) % 4 == 0 && !ActivationRange.checkEntityImmunities( entity ) ) // Paper - Ensure checking item movement is offset from Spigot's entity activation range check
{
isActive = false;
}