even more work
This commit is contained in:
parent
ef6a41195d
commit
7b29d1f4c5
46 changed files with 169 additions and 185 deletions
|
@ -1,208 +0,0 @@
|
|||
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 ae64cbbff21ab56d27fd55a4f21b6241d0a54e98..eecadeb81317834741b083fe8c5da77519d196a3 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 34fdef41d1eb3fe78bf688d69aae437d89a337bb..66bd75ee66840f17cc7d00ff89adcb88d83e4dc9 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"));
|
||||
@@ -180,6 +180,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 b7f1569c662df13f278fc704cabec0400ba7c382..87ce129e1d592bcf68169feb559f44d5cda7c486 100644
|
||||
--- a/src/main/java/net/minecraft/commands/Commands.java
|
||||
+++ b/src/main/java/net/minecraft/commands/Commands.java
|
||||
@@ -434,6 +434,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);
|
||||
});
|
||||
@@ -441,6 +442,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);
|
||||
|
||||
@@ -459,6 +461,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 c5b682d08ab270d153eb600afe9a21bfa1305072..6b49981d61e910d8d8fa09ff091359754e6d09a3 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -842,8 +842,12 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
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
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -858,7 +862,13 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
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
|
|
@ -1,383 +0,0 @@
|
|||
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 c8e9e85728d6b60bb829e69c015ab3c5172afd54..86cc516a6d4e8c64497479ec128d4e8d73667cfa 100644
|
||||
--- a/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java
|
||||
+++ b/src/main/java/net/minecraft/recipebook/ServerPlaceRecipe.java
|
||||
@@ -33,6 +33,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, (IntList)null)) {
|
||||
@@ -79,7 +80,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;
|
||||
}
|
||||
@@ -96,10 +97,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,8 +141,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 ba5cd22ec5a0853b48fbc2b0ba271fb79a8af4dc..68d272e98f9e54c9b150c75c27a9ae545be842f6 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/player/StackedContents.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/player/StackedContents.java
|
||||
@@ -20,8 +20,10 @@ import net.minecraft.world.item.crafting.Recipe;
|
||||
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);
|
||||
}
|
||||
@@ -36,6 +38,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);
|
||||
}
|
||||
|
||||
@@ -83,6 +86,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();
|
||||
}
|
||||
@@ -106,7 +126,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 f551651ce2d8b29c4a5cd787f48dedbbb411b2cb..e30af1e639db0c89066e76915884e1a0bef584a3 100644
|
||||
--- a/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java
|
||||
+++ b/src/main/java/net/minecraft/world/item/crafting/AbstractCookingRecipe.java
|
||||
@@ -7,7 +7,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 ResourceLocation id;
|
||||
private final CookingBookCategory category;
|
||||
@@ -26,6 +26,7 @@ public abstract class AbstractCookingRecipe implements Recipe<Container> {
|
||||
this.result = output;
|
||||
this.experience = experience;
|
||||
this.cookingTime = cookTime;
|
||||
+ 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 d1505698ce45bbc7f592a23111391542d01d2b28..f2cf1f30191e14c9e9b5b5e4a3f6d9346125e998 100644
|
||||
--- a/src/main/java/net/minecraft/world/item/crafting/Recipe.java
|
||||
+++ b/src/main/java/net/minecraft/world/item/crafting/Recipe.java
|
||||
@@ -68,4 +68,10 @@ public interface Recipe<C extends Container> {
|
||||
}
|
||||
|
||||
org.bukkit.inventory.Recipe toBukkitRecipe(); // 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 9c1285e31d947f92e0b00149e342e793898e0d7c..6693dd51440da3f0fc338c4e2cb67d3222eed182 100644
|
||||
--- a/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
|
||||
+++ b/src/main/java/net/minecraft/world/item/crafting/ShapedRecipe.java
|
||||
@@ -30,7 +30,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;
|
||||
@@ -50,6 +50,7 @@ public class ShapedRecipe implements CraftingRecipe {
|
||||
this.recipeItems = input;
|
||||
this.result = output;
|
||||
this.showNotification = showNotification;
|
||||
+ this.checkExactIngredients(); // Paper - improve exact recipe choices
|
||||
}
|
||||
|
||||
public ShapedRecipe(ResourceLocation id, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> input, ItemStack output) {
|
||||
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 1023dcb80dfb978dd496ed0f96ca5832fadf0f87..2e60bdc44c33d434bfd9ca5bf8f75de799c6768c 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
|
||||
|
||||
private final ResourceLocation id;
|
||||
final String group;
|
||||
@@ -34,6 +34,7 @@ public class ShapelessRecipe implements CraftingRecipe {
|
||||
this.category = category;
|
||||
this.result = output;
|
||||
this.ingredients = input;
|
||||
+ this.checkExactIngredients(); // Paper - improve exact recipe choices
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
@@ -83,6 +84,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) {
|
|
@ -1,56 +0,0 @@
|
|||
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 6b49981d61e910d8d8fa09ff091359754e6d09a3..593f017a43153c081f007073926fa4f520bded00 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -302,6 +302,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
private final MessageSignatureCache messageSignatureCache;
|
||||
private final FutureChain chatMessageChain;
|
||||
private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit
|
||||
+ private static final int MAX_SIGN_LINE_LENGTH = Integer.getInteger("Paper.maxSignLength", 80); // Paper
|
||||
|
||||
public ServerGamePacketListenerImpl(MinecraftServer server, Connection connection, ServerPlayer player) {
|
||||
this.lastChatTimeStamp = new AtomicReference(Instant.EPOCH);
|
||||
@@ -3189,7 +3190,19 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
|
||||
@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);
|
|
@ -1,359 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Aikar <aikar@aikar.co>
|
||||
Date: Wed, 6 May 2020 04:53:35 -0400
|
||||
Subject: [PATCH] Optimize Network Manager and add advanced packet support
|
||||
|
||||
Adds ability for 1 packet to bundle other packets to follow it
|
||||
Adds ability for a packet to delay sending more packets until a state is ready.
|
||||
|
||||
Removes synchronization from sending packets
|
||||
Removes processing packet queue off of main thread
|
||||
- for the few cases where it is allowed, order is not necessary nor
|
||||
should it even be happening concurrently in first place (handshaking/login/status)
|
||||
|
||||
Ensures packets sent asynchronously are dispatched on main thread
|
||||
|
||||
This helps ensure safety for ProtocolLib as packet listeners
|
||||
are commonly accessing world state. This will allow you to schedule
|
||||
a packet to be sent async, but itll be dispatched sync for packet
|
||||
listeners to process.
|
||||
|
||||
This should solve some deadlock risks
|
||||
|
||||
Also adds Netty Channel Flush Consolidation to reduce the amount of flushing
|
||||
|
||||
Also avoids spamming closed channel exception by rechecking closed state in dispatch
|
||||
and then catch exceptions and close if they fire.
|
||||
|
||||
Part of this commit was authored by: Spottedleaf, sandtechnology
|
||||
|
||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
||||
index b293ac99e436b02c9ef30994a394bcc53ebbf2f9..99d5fd192a4cf1ac2739520a111b8dd854ff2f57 100644
|
||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
||||
@@ -120,6 +120,10 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
public int protocolVersion;
|
||||
public java.net.InetSocketAddress virtualHost;
|
||||
private static boolean enableExplicitFlush = Boolean.getBoolean("paper.explicit-flush");
|
||||
+ // Optimize network
|
||||
+ public boolean isPending = true;
|
||||
+ public boolean queueImmunity = false;
|
||||
+ public ConnectionProtocol protocol;
|
||||
// Paper end
|
||||
|
||||
public Connection(PacketFlow side) {
|
||||
@@ -147,6 +151,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
|
||||
public void setProtocol(ConnectionProtocol state) {
|
||||
+ protocol = state; // Paper
|
||||
this.channel.attr(Connection.ATTRIBUTE_PROTOCOL).set(state);
|
||||
this.channel.attr(BundlerInfo.BUNDLER_PROVIDER).set(state);
|
||||
this.channel.config().setAutoRead(true);
|
||||
@@ -229,19 +234,88 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
Validate.notNull(listener, "packetListener", new Object[0]);
|
||||
this.packetListener = listener;
|
||||
}
|
||||
+ // Paper start
|
||||
+ public @Nullable net.minecraft.server.level.ServerPlayer getPlayer() {
|
||||
+ if (packetListener instanceof net.minecraft.server.network.ServerGamePacketListenerImpl serverGamePacketListener) {
|
||||
+ return serverGamePacketListener.player;
|
||||
+ } else {
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+ private static class InnerUtil { // Attempt to hide these methods from ProtocolLib so it doesn't accidently pick them up.
|
||||
+ private static java.util.List<Packet> buildExtraPackets(Packet packet) {
|
||||
+ java.util.List<Packet> extra = packet.getExtraPackets();
|
||||
+ if (extra == null || extra.isEmpty()) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ java.util.List<Packet> ret = new java.util.ArrayList<>(1 + extra.size());
|
||||
+ buildExtraPackets0(extra, ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ private static void buildExtraPackets0(java.util.List<Packet> extraPackets, java.util.List<Packet> into) {
|
||||
+ for (Packet extra : extraPackets) {
|
||||
+ into.add(extra);
|
||||
+ java.util.List<Packet> extraExtra = extra.getExtraPackets();
|
||||
+ if (extraExtra != null && !extraExtra.isEmpty()) {
|
||||
+ buildExtraPackets0(extraExtra, into);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Paper start
|
||||
+ private static boolean canSendImmediate(Connection networkManager, Packet<?> packet) {
|
||||
+ return networkManager.isPending || networkManager.protocol != ConnectionProtocol.PLAY ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundKeepAlivePacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundPlayerChatPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSystemChatPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundCommandSuggestionsPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetTitleTextPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetSubtitleTextPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetActionBarTextPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundSetTitlesAnimationPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundClearTitlesPacket ||
|
||||
+ packet instanceof net.minecraft.network.protocol.game.ClientboundBossEventPacket;
|
||||
+ }
|
||||
+ // Paper end
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
public void send(Packet<?> packet) {
|
||||
this.send(packet, (PacketSendListener) null);
|
||||
}
|
||||
|
||||
public void send(Packet<?> packet, @Nullable PacketSendListener callbacks) {
|
||||
- if (this.isConnected()) {
|
||||
- this.flushQueue();
|
||||
+ // Paper start - handle oversized packets better
|
||||
+ boolean connected = this.isConnected();
|
||||
+ if (!connected && !preparing) {
|
||||
+ return; // Do nothing
|
||||
+ }
|
||||
+ packet.onPacketDispatch(getPlayer());
|
||||
+ if (connected && (InnerUtil.canSendImmediate(this, packet) || (
|
||||
+ io.papermc.paper.util.MCUtil.isMainThread() && packet.isReady() && this.queue.isEmpty() &&
|
||||
+ (packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty())
|
||||
+ ))) {
|
||||
this.sendPacket(packet, callbacks);
|
||||
- } else {
|
||||
- this.queue.add(new Connection.PacketHolder(packet, callbacks));
|
||||
+ return;
|
||||
}
|
||||
+ // write the packets to the queue, then flush - antixray hooks there already
|
||||
+ java.util.List<Packet> extraPackets = InnerUtil.buildExtraPackets(packet);
|
||||
+ boolean hasExtraPackets = extraPackets != null && !extraPackets.isEmpty();
|
||||
+ if (!hasExtraPackets) {
|
||||
+ this.queue.add(new Connection.PacketHolder(packet, callbacks));
|
||||
+ } else {
|
||||
+ java.util.List<Connection.PacketHolder> packets = new java.util.ArrayList<>(1 + extraPackets.size());
|
||||
+ packets.add(new Connection.PacketHolder(packet, null)); // delay the future listener until the end of the extra packets
|
||||
|
||||
+ for (int i = 0, len = extraPackets.size(); i < len;) {
|
||||
+ Packet extra = extraPackets.get(i);
|
||||
+ boolean end = ++i == len;
|
||||
+ packets.add(new Connection.PacketHolder(extra, end ? callbacks : null)); // append listener to the end
|
||||
+ }
|
||||
+ this.queue.addAll(packets); // atomic
|
||||
+ }
|
||||
+ this.flushQueue();
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
private void sendPacket(Packet<?> packet, @Nullable PacketSendListener callbacks) {
|
||||
@@ -273,6 +347,15 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
this.setProtocol(packetState);
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ net.minecraft.server.level.ServerPlayer player = getPlayer();
|
||||
+ if (!isConnected()) {
|
||||
+ packet.onPacketDispatchFinish(player, null);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ try {
|
||||
+ // Paper end
|
||||
ChannelFuture channelfuture = this.channel.writeAndFlush(packet);
|
||||
|
||||
if (callbacks != null) {
|
||||
@@ -291,28 +374,72 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
});
|
||||
}
|
||||
+ // Paper start
|
||||
+ if (packet.hasFinishListener()) {
|
||||
+ channelfuture.addListener((ChannelFutureListener) channelFuture -> packet.onPacketDispatchFinish(player, channelFuture));
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
+ // Paper start
|
||||
+ } catch (Exception e) {
|
||||
+ LOGGER.error("NetworkException: " + player, e);
|
||||
+ disconnect(Component.translatable("disconnect.genericReason", "Internal Exception: " + e.getMessage()));
|
||||
+ packet.onPacketDispatchFinish(player, null);
|
||||
+ }
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
private ConnectionProtocol getCurrentProtocol() {
|
||||
return (ConnectionProtocol) this.channel.attr(Connection.ATTRIBUTE_PROTOCOL).get();
|
||||
}
|
||||
|
||||
- private void flushQueue() {
|
||||
+ // Paper start - rewrite this to be safer if ran off main thread
|
||||
+ private boolean flushQueue() { // void -> boolean
|
||||
+ if (!isConnected()) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ if (io.papermc.paper.util.MCUtil.isMainThread()) {
|
||||
+ return processQueue();
|
||||
+ } else if (isPending) {
|
||||
+ // Should only happen during login/status stages
|
||||
+ synchronized (this.queue) {
|
||||
+ return this.processQueue();
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+ private boolean processQueue() {
|
||||
try { // Paper - add pending task queue
|
||||
- if (this.channel != null && this.channel.isOpen()) {
|
||||
- Queue queue = this.queue;
|
||||
+ if (this.queue.isEmpty()) return true;
|
||||
+ // If we are on main, we are safe here in that nothing else should be processing queue off main anymore
|
||||
+ // But if we are not on main due to login/status, the parent is synchronized on packetQueue
|
||||
+ java.util.Iterator<PacketHolder> iterator = this.queue.iterator();
|
||||
+ while (iterator.hasNext()) {
|
||||
+ PacketHolder queued = iterator.next(); // poll -> peek
|
||||
+
|
||||
+ // Fix NPE (Spigot bug caused by handleDisconnection())
|
||||
+ if (queued == null) {
|
||||
+ return true;
|
||||
+ }
|
||||
|
||||
- synchronized (this.queue) {
|
||||
- Connection.PacketHolder networkmanager_queuedpacket;
|
||||
+ // Paper start - checking isConsumed flag and skipping packet sending
|
||||
+ if (queued.isConsumed()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ // Paper end - checking isConsumed flag and skipping packet sending
|
||||
|
||||
- while ((networkmanager_queuedpacket = (Connection.PacketHolder) this.queue.poll()) != null) {
|
||||
- this.sendPacket(networkmanager_queuedpacket.packet, networkmanager_queuedpacket.listener);
|
||||
+ Packet<?> packet = queued.packet;
|
||||
+ if (!packet.isReady()) {
|
||||
+ return false;
|
||||
+ } else {
|
||||
+ iterator.remove();
|
||||
+ if (queued.tryMarkConsumed()) { // Paper - try to mark isConsumed flag for de-duplicating packet
|
||||
+ this.sendPacket(packet, queued.listener);
|
||||
}
|
||||
-
|
||||
}
|
||||
}
|
||||
+ return true;
|
||||
} finally { // Paper start - add pending task queue
|
||||
Runnable r;
|
||||
while ((r = this.pendingTasks.poll()) != null) {
|
||||
@@ -320,6 +447,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
}
|
||||
} // Paper end - add pending task queue
|
||||
}
|
||||
+ // Paper end
|
||||
|
||||
public void tick() {
|
||||
this.flushQueue();
|
||||
@@ -356,9 +484,22 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
return this.address;
|
||||
}
|
||||
|
||||
+ // Paper start
|
||||
+ public void clearPacketQueue() {
|
||||
+ net.minecraft.server.level.ServerPlayer player = getPlayer();
|
||||
+ queue.forEach(queuedPacket -> {
|
||||
+ Packet<?> packet = queuedPacket.packet;
|
||||
+ if (packet.hasFinishListener()) {
|
||||
+ packet.onPacketDispatchFinish(player, null);
|
||||
+ }
|
||||
+ });
|
||||
+ queue.clear();
|
||||
+ }
|
||||
+ // Paper end
|
||||
public void disconnect(Component disconnectReason) {
|
||||
// Spigot Start
|
||||
this.preparing = false;
|
||||
+ clearPacketQueue(); // Paper
|
||||
// Spigot End
|
||||
if (this.channel == null) {
|
||||
this.delayedDisconnect = disconnectReason;
|
||||
@@ -500,7 +641,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
public void handleDisconnection() {
|
||||
if (this.channel != null && !this.channel.isOpen()) {
|
||||
if (this.disconnectionHandled) {
|
||||
- Connection.LOGGER.warn("handleDisconnection() called twice");
|
||||
+ //Connection.LOGGER.warn("handleDisconnection() called twice"); // Paper - Do not log useless message
|
||||
} else {
|
||||
this.disconnectionHandled = true;
|
||||
if (this.getDisconnectedReason() != null) {
|
||||
@@ -508,7 +649,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
} else if (this.getPacketListener() != null) {
|
||||
this.getPacketListener().onDisconnect(Component.translatable("multiplayer.disconnect.generic"));
|
||||
}
|
||||
- this.queue.clear(); // Free up packet queue.
|
||||
+ clearPacketQueue(); // Paper
|
||||
// Paper start - Add PlayerConnectionCloseEvent
|
||||
final PacketListener packetListener = this.getPacketListener();
|
||||
if (packetListener instanceof net.minecraft.server.network.ServerGamePacketListenerImpl) {
|
||||
@@ -548,6 +689,18 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
@Nullable
|
||||
final PacketSendListener listener;
|
||||
|
||||
+ // Paper start - isConsumed flag for the connection
|
||||
+ private java.util.concurrent.atomic.AtomicBoolean isConsumed = new java.util.concurrent.atomic.AtomicBoolean(false);
|
||||
+
|
||||
+ public boolean tryMarkConsumed() {
|
||||
+ return isConsumed.compareAndSet(false, true);
|
||||
+ }
|
||||
+
|
||||
+ public boolean isConsumed() {
|
||||
+ return isConsumed.get();
|
||||
+ }
|
||||
+ // Paper end - isConsumed flag for the connection
|
||||
+
|
||||
public PacketHolder(Packet<?> packet, @Nullable PacketSendListener callbacks) {
|
||||
this.packet = packet;
|
||||
this.listener = callbacks;
|
||||
diff --git a/src/main/java/net/minecraft/network/protocol/Packet.java b/src/main/java/net/minecraft/network/protocol/Packet.java
|
||||
index 74bfe0d3942259c45702b099efdc4e101a4e3022..e8fcd56906d26f6dc87959e32c4c7c78cfea9658 100644
|
||||
--- a/src/main/java/net/minecraft/network/protocol/Packet.java
|
||||
+++ b/src/main/java/net/minecraft/network/protocol/Packet.java
|
||||
@@ -9,6 +9,19 @@ public interface Packet<T extends PacketListener> {
|
||||
void handle(T listener);
|
||||
|
||||
// Paper start
|
||||
+ /**
|
||||
+ * @param player Null if not at PLAY stage yet
|
||||
+ */
|
||||
+ default void onPacketDispatch(@javax.annotation.Nullable net.minecraft.server.level.ServerPlayer player) {}
|
||||
+
|
||||
+ /**
|
||||
+ * @param player Null if not at PLAY stage yet
|
||||
+ * @param future Can be null if packet was cancelled
|
||||
+ */
|
||||
+ default void onPacketDispatchFinish(@javax.annotation.Nullable net.minecraft.server.level.ServerPlayer player, @javax.annotation.Nullable io.netty.channel.ChannelFuture future) {}
|
||||
+ default boolean hasFinishListener() { return false; }
|
||||
+ default boolean isReady() { return true; }
|
||||
+ default java.util.List<Packet> getExtraPackets() { return null; }
|
||||
default boolean packetTooLarge(net.minecraft.network.Connection manager) {
|
||||
return false;
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
index 776528e50a5abc0e02d9de99231fb47352aa4f43..fbf375534e2b8bd6ef052c4625764f4f8feb2ed6 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
@@ -62,10 +62,12 @@ public class ServerConnectionListener {
|
||||
final List<Connection> connections = Collections.synchronizedList(Lists.newArrayList());
|
||||
// Paper start - prevent blocking on adding a new network manager while the server is ticking
|
||||
private final java.util.Queue<Connection> pending = new java.util.concurrent.ConcurrentLinkedQueue<>();
|
||||
+ private static final boolean disableFlushConsolidation = Boolean.getBoolean("Paper.disableFlushConsolidate"); // Paper
|
||||
private final void addPending() {
|
||||
Connection manager = null;
|
||||
while ((manager = pending.poll()) != null) {
|
||||
connections.add(manager);
|
||||
+ manager.isPending = false;
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
@@ -100,6 +102,7 @@ public class ServerConnectionListener {
|
||||
;
|
||||
}
|
||||
|
||||
+ if (!disableFlushConsolidation) channel.pipeline().addFirst(new io.netty.handler.flush.FlushConsolidationHandler()); // Paper
|
||||
ChannelPipeline channelpipeline = channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30)).addLast("legacy_query", new LegacyQueryHandler(ServerConnectionListener.this));
|
||||
|
||||
Connection.configureSerialization(channelpipeline, PacketFlow.SERVERBOUND);
|
|
@ -1,64 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
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 5657493e215a17f3132f9d411424875160fda6d9..e038240042366e1c491c04016982c91c91ee86cd 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -1157,6 +1157,7 @@ public abstract class PlayerList {
|
||||
}
|
||||
|
||||
public void setUsingWhiteList(boolean whitelistEnabled) {
|
||||
+ new com.destroystokyo.paper.event.server.WhitelistToggleEvent(whitelistEnabled).callEvent();
|
||||
this.doWhiteList = whitelistEnabled;
|
||||
}
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
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 6b157b362cffedae26133fc0f0af1094655ee11f..986a509998d217228eb1dc2b5815787599e02d6b 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1429,6 +1429,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 e038240042366e1c491c04016982c91c91ee86cd..cccaf594392a0283f00986f182cc89d56181bc40 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -233,6 +233,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 = "local";
|
||||
|
||||
@@ -373,7 +378,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 dee47ff97e4f08154866d7628c3588a42f9892b9..0f40361ebfe64fae828f577529200e40e94b0e05 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;
|
||||
@@ -2181,6 +2182,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);
|
||||
@@ -2327,6 +2331,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 185b3af59bf72244bbbfc46c3336e4f7f14e73a8..4dc676f29f04ba8d52b2fe3778347d2c2f46eebe 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
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
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);
|
|
@ -1,48 +0,0 @@
|
|||
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 cccaf594392a0283f00986f182cc89d56181bc40..94a0d17a0339249c1c97e36d6e13b5958cfa2e49 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -758,9 +758,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;
|
||||
@@ -812,6 +817,7 @@ public abstract class PlayerList {
|
||||
|
||||
location = respawnEvent.getRespawnLocation();
|
||||
if (!flag) entityplayer.reset(); // SPIGOT-4785
|
||||
+ isRespawn = true; // Paper
|
||||
} else {
|
||||
location.setWorld(worldserver.getWorld());
|
||||
}
|
||||
@@ -871,6 +877,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;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
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
|
|
@ -1,31 +0,0 @@
|
|||
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 d84ed437e91a620c294533ddcb098cc11bc16c1f..78465f82b2f3ae2a932b787a489f3d01cc51f5f9 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -1304,6 +1304,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);
|
||||
@@ -1325,6 +1326,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;
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
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 d29e13aa7bbfbab54f8a3d73279891d65e957d7e..3ebb5cd3a0068cf6716ebe6fa6f4f4f4a1d58d08 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -847,6 +847,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);
|
||||
@@ -930,7 +970,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
|
|
@ -1,30 +0,0 @@
|
|||
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 b3a0f4dd651dda29d2064b629da67378c00a6a78..b1111e644f860f40a944fd43ac900db4615a1c5e 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/Level.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/Level.java
|
||||
@@ -982,9 +982,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 && !io.papermc.paper.util.TickThread.isTickThread() ? null : this.getChunkAt(blockposition).getBlockEntity(blockposition, LevelChunk.EntityCreationType.IMMEDIATE)); // Paper - rewrite chunk system
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
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 be17e1a4afc2fd5490a59aa7bcb199fb61643d35..537e63ff97afc2ae73e25d3699d249a66f13ba16 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();
|
|
@ -1,103 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
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/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 593f017a43153c081f007073926fa4f520bded00..732238a60dacd83b6572f6f79763f112e5fe9611 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -517,7 +517,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
|
||||
|
||||
Objects.requireNonNull(this.connection);
|
||||
// CraftBukkit - Don't wait
|
||||
- minecraftserver.wrapRunnable(networkmanager::handleDisconnection);
|
||||
+ minecraftserver.scheduleOnMain(networkmanager::handleDisconnection); // Paper
|
||||
}
|
||||
|
||||
private <T, R> CompletableFuture<R> filterTextPacket(T text, BiFunction<TextFilter, T, CompletableFuture<R>> filterer) {
|
|
@ -1,20 +0,0 @@
|
|||
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);
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,222 +0,0 @@
|
|||
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 78465f82b2f3ae2a932b787a489f3d01cc51f5f9..21d7196cdc694a581c8a3232a39e7454c0b30f56 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -737,30 +737,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;
|
||||
@@ -783,7 +786,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 986a509998d217228eb1dc2b5815787599e02d6b..773fea9c2c4bef931439b5663471c010d9a1297c 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -1866,12 +1866,84 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
return ((MapIndex) this.getServer().overworld().getDataStorage().computeIfAbsent(MapIndex::load, MapIndex::new, "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 537e63ff97afc2ae73e25d3699d249a66f13ba16..e0f38ef295e1958b45fc05395cd4c57194928338 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
|
||||
@@ -1350,15 +1350,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
|
|
@ -1,251 +0,0 @@
|
|||
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 ddcc212ba83d9365adb842b3d3ced64e3d7dd155..584985272a02eb5b61a22cf2404fbd97a55a3358 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;
|
||||
|
||||
@@ -45,6 +49,7 @@ public class RegionFile implements AutoCloseable {
|
||||
@VisibleForTesting
|
||||
protected final RegionBitmap usedSectors;
|
||||
public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(true); // Paper
|
||||
+ public final Path regionFile; // Paper
|
||||
|
||||
public RegionFile(Path file, Path directory, boolean dsync) throws IOException {
|
||||
this(file, directory, RegionFileVersion.VERSION_DEFLATE, dsync);
|
||||
@@ -52,6 +57,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])) {
|
||||
@@ -430,6 +437,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 4c1b1ac36ef09a9cc8da6e8ee71f82d4a67478df..96f129cb13642dc9667464b58c025fa0ed700cfd 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
|
||||
@@ -11,8 +11,10 @@ import java.nio.file.Path;
|
||||
import javax.annotation.Nullable;
|
||||
import net.minecraft.FileUtil;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
+import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.nbt.StreamTagVisitor;
|
||||
+import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.util.ExceptionCollector;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
|
||||
@@ -120,6 +122,71 @@ public 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) {
|
||||
+ ListTag levelList = level.getList(key, 10);
|
||||
+ ListTag oversizedList = oversizedLevel.getList(oversizedKey, 10);
|
||||
+
|
||||
+ if (!oversizedList.isEmpty()) {
|
||||
+ levelList.addAll(oversizedList);
|
||||
+ level.put(key, levelList);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static int getNBTSize(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
|
||||
@@ -131,6 +198,12 @@ public class RegionFileStorage implements AutoCloseable {
|
||||
try { // Paper
|
||||
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:
|
||||
{
|
||||
@@ -217,6 +290,7 @@ public 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 {
|
|
@ -1,21 +0,0 @@
|
|||
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 4926f9f5eaf13b339ce2c2385faf9f4afba3d029..535880a9dc3ccbf8d6f1b185a695011a347bdd88 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -2746,5 +2746,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
|
||||
}
|
|
@ -1,246 +0,0 @@
|
|||
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 af92411006c3d281815b3f4c3de5f0280d3a5901..50a201c08f143117a050305b0dde6873a04efb8b 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -687,9 +687,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
// Paper end
|
||||
|
||||
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
|
||||
@@ -698,6 +702,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 584985272a02eb5b61a22cf2404fbd97a55a3358..cda87a66fe80bf910f629c64e36c1fecbad81d77 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
|
||||
@@ -51,6 +51,30 @@ public class RegionFile implements AutoCloseable {
|
||||
public final java.util.concurrent.locks.ReentrantLock fileLock = new java.util.concurrent.locks.ReentrantLock(true); // Paper
|
||||
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);
|
||||
}
|
||||
@@ -398,6 +422,7 @@ 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;
|
||||
}
|
||||
@@ -408,6 +433,7 @@ public class RegionFile implements AutoCloseable {
|
||||
synchronized (this) {
|
||||
try {
|
||||
// Paper end
|
||||
+ 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 96f129cb13642dc9667464b58c025fa0ed700cfd..29da08c58200c24fd03003937d30eb41234cabc9 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
|
||||
@@ -290,6 +290,7 @@ public 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 e0f38ef295e1958b45fc05395cd4c57194928338..f259f6609cd87a210451ddf4ea00a72718d1efd0 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);
|
||||
}
|
||||
}
|
||||
@@ -423,20 +437,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
|
|
@ -1,33 +0,0 @@
|
|||
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() {
|
|
@ -1,36 +0,0 @@
|
|||
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 2503d4691e47083a9d99a38c4ed3f50a2374821a..23d53f3fd524cc4d827dc95ab95367702a110a05 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) -> {
|
|
@ -1,39 +0,0 @@
|
|||
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
|
|
@ -1,19 +0,0 @@
|
|||
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;
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
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 94a0d17a0339249c1c97e36d6e13b5958cfa2e49..2848e657209a699b12fc0e1fd2bde54d661f07f0 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -642,8 +642,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) {
|
|
@ -1,45 +0,0 @@
|
|||
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 965c5724eb4927f017123c791c4a721c59b6db15..53abf88143206eee03f372cf6471fbfbe519496d 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -3651,9 +3651,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) {
|
||||
@@ -3733,6 +3738,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;
|
||||
@@ -3767,8 +3773,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
|
|
@ -1,18 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
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)) {
|
|
@ -1,210 +0,0 @@
|
|||
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 902156477bdfc9917105f1229f760c26e5af302a..58609a0911c4e32b6f80f050cd3d23f70ad75b1b 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;
|
|
@ -1,20 +0,0 @@
|
|||
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 9012a6347b2f061e88c42d3c237c4b465883e941..6b2b34cb129f807af8042e26a5e180d18e195459 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);
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
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 8f7bf1f0400aeab8b7801d113d244d0716c5eb84..fccb8d7a99bef076838ebefa233f2f00a1364c30 100644
|
||||
--- a/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
|
||||
+++ b/src/main/java/io/papermc/paper/chunk/system/ChunkSystem.java
|
||||
@@ -74,7 +74,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 50a201c08f143117a050305b0dde6873a04efb8b..562e45954cc72a253f20e9a9fddf0f179baf3e7b 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -535,6 +535,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
entity.discard();
|
||||
needsRemoval = true;
|
||||
}
|
||||
+ checkDupeUUID(world, entity); // Paper
|
||||
return !needsRemoval;
|
||||
}));
|
||||
// CraftBukkit end
|
||||
@@ -546,6 +547,49 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
throw new UnsupportedOperationException(); // Paper - rewrite chunk system
|
||||
}
|
||||
|
||||
+ // 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) {
|
||||
throw new UnsupportedOperationException(); // Paper - rewrite chunk system
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
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 3ebb5cd3a0068cf6716ebe6fa6f4f4f4a1d58d08..4aea5b2e36f5cd6f9b076f9a225a391211ec0c25 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -967,7 +967,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
|
|
@ -1,18 +0,0 @@
|
|||
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;
|
|
@ -1,32 +0,0 @@
|
|||
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)) {
|
|
@ -1,27 +0,0 @@
|
|||
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 773fea9c2c4bef931439b5663471c010d9a1297c..0efc377743e93a0120843cab192753d037e88a73 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -550,6 +550,15 @@ public class ServerLevel extends Level implements WorldGenLevel {
|
||||
});
|
||||
}
|
||||
|
||||
+ // 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
|
|
@ -1,42 +0,0 @@
|
|||
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 1d9ce6dae17ff572d4528971c69c63d0f85b313c..305d9772f2af22e8bdf73235cdb15ea01ac2c3b3 100644
|
||||
--- a/src/main/java/org/spigotmc/ActivationRange.java
|
||||
+++ b/src/main/java/org/spigotmc/ActivationRange.java
|
||||
@@ -257,7 +257,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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue