p a t c h e s

This commit is contained in:
Jason Penilla 2023-12-05 21:49:31 -07:00
parent 2053d6ace7
commit 29e137669e
No known key found for this signature in database
GPG key ID: 0E75A301420E48F8
24 changed files with 68 additions and 68 deletions

View file

@ -0,0 +1,60 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jakub Zacek <dawon@dawon.eu>
Date: Mon, 4 Oct 2021 10:16:44 +0200
Subject: [PATCH] Add methods to find targets for lightning strikes
== AT ==
public net.minecraft.server.level.ServerLevel findLightningRod(Lnet/minecraft/core/BlockPos;)Ljava/util/Optional;
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index d2e173c644ca005afd124b30566a01c22bcb7e0e..d5709319497a5589665388afd61307dc7755332d 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -994,6 +994,11 @@ public class ServerLevel extends Level implements WorldGenLevel {
}
protected BlockPos findLightningTargetAround(BlockPos pos) {
+ // Paper start
+ return this.findLightningTargetAround(pos, false);
+ }
+ public BlockPos findLightningTargetAround(BlockPos pos, boolean returnNullWhenNoTarget) {
+ // Paper end
BlockPos blockposition1 = this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos);
Optional<BlockPos> optional = this.findLightningRod(blockposition1);
@@ -1008,6 +1013,7 @@ public class ServerLevel extends Level implements WorldGenLevel {
if (!list.isEmpty()) {
return ((LivingEntity) list.get(this.random.nextInt(list.size()))).blockPosition();
} else {
+ if (returnNullWhenNoTarget) return null; // Paper
if (blockposition1.getY() == this.getMinBuildHeight() - 1) {
blockposition1 = blockposition1.above(2);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index a5a6a1a946451e3f3f4f88eccb30eb3e870c58de..19db9c2447ffc2321e2f8a3b86f561d29b9eb6de 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -702,6 +702,23 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return (LightningStrike) lightning.getBukkitEntity();
}
+ // Paper start
+ @Override
+ public Location findLightningRod(Location location) {
+ return this.world.findLightningRod(io.papermc.paper.util.MCUtil.toBlockPosition(location))
+ .map(blockPos -> io.papermc.paper.util.MCUtil.toLocation(this.world, blockPos)
+ // get the actual rod pos
+ .subtract(0, 1, 0))
+ .orElse(null);
+ }
+
+ @Override
+ public Location findLightningTarget(Location location) {
+ final BlockPos pos = this.world.findLightningTargetAround(io.papermc.paper.util.MCUtil.toBlockPosition(location), true);
+ return pos == null ? null : io.papermc.paper.util.MCUtil.toLocation(this.world, pos);
+ }
+ // Paper end
+
@Override
public boolean generateTree(Location loc, TreeType type) {
return this.generateTree(loc, CraftWorld.rand, type);

View file

@ -0,0 +1,150 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Fri, 20 Aug 2021 13:03:21 -0700
Subject: [PATCH] Get entity default attributes
== AT ==
public net.minecraft.world.entity.ai.attributes.AttributeSupplier getAttributeInstance(Lnet/minecraft/world/entity/ai/attributes/Attribute;)Lnet/minecraft/world/entity/ai/attributes/AttributeInstance;
diff --git a/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeInstance.java b/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeInstance.java
new file mode 100644
index 0000000000000000000000000000000000000000..12135ffeacd648f6bc4d7d327059ea1a7e8c79c4
--- /dev/null
+++ b/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeInstance.java
@@ -0,0 +1,30 @@
+package io.papermc.paper.attribute;
+
+import net.minecraft.world.entity.ai.attributes.AttributeInstance;
+import org.bukkit.attribute.Attribute;
+import org.bukkit.attribute.AttributeModifier;
+import org.bukkit.craftbukkit.attribute.CraftAttributeInstance;
+
+import java.util.Collection;
+
+public class UnmodifiableAttributeInstance extends CraftAttributeInstance {
+
+ public UnmodifiableAttributeInstance(AttributeInstance handle, Attribute attribute) {
+ super(handle, attribute);
+ }
+
+ @Override
+ public void setBaseValue(double d) {
+ throw new UnsupportedOperationException("Cannot modify default attributes");
+ }
+
+ @Override
+ public void addModifier(AttributeModifier modifier) {
+ throw new UnsupportedOperationException("Cannot modify default attributes");
+ }
+
+ @Override
+ public void removeModifier(AttributeModifier modifier) {
+ throw new UnsupportedOperationException("Cannot modify default attributes");
+ }
+}
diff --git a/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeMap.java b/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeMap.java
new file mode 100644
index 0000000000000000000000000000000000000000..68044b8439c302114240d0ae4da93ab3e0789cd2
--- /dev/null
+++ b/src/main/java/io/papermc/paper/attribute/UnmodifiableAttributeMap.java
@@ -0,0 +1,32 @@
+package io.papermc.paper.attribute;
+
+import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
+import org.bukkit.attribute.Attributable;
+import org.bukkit.attribute.Attribute;
+import org.bukkit.attribute.AttributeInstance;
+import org.bukkit.craftbukkit.attribute.CraftAttribute;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class UnmodifiableAttributeMap implements Attributable {
+
+ private final AttributeSupplier handle;
+
+ public UnmodifiableAttributeMap(@NotNull AttributeSupplier handle) {
+ this.handle = handle;
+ }
+
+ @Override
+ public @Nullable AttributeInstance getAttribute(@NotNull Attribute attribute) {
+ net.minecraft.world.entity.ai.attributes.Attribute nmsAttribute = CraftAttribute.bukkitToMinecraft(attribute);
+ if (!this.handle.hasAttribute(nmsAttribute)) {
+ return null;
+ }
+ return new UnmodifiableAttributeInstance(this.handle.getAttributeInstance(nmsAttribute), attribute);
+ }
+
+ @Override
+ public void registerAttribute(@NotNull Attribute attribute) {
+ throw new UnsupportedOperationException("Cannot register new attributes here");
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index 5045af60302e1929d9f904cb74214c3c15cfe60d..33988ab25aba60c75bffbd39da5562333875d0c1 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -560,6 +560,18 @@ public final class CraftMagicNumbers implements UnsafeValues {
public int getProtocolVersion() {
return net.minecraft.SharedConstants.getCurrentVersion().getProtocolVersion();
}
+
+ @Override
+ public boolean hasDefaultEntityAttributes(NamespacedKey bukkitEntityKey) {
+ return net.minecraft.world.entity.ai.attributes.DefaultAttributes.hasSupplier(net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(bukkitEntityKey)));
+ }
+
+ @Override
+ public org.bukkit.attribute.Attributable getDefaultEntityAttributes(NamespacedKey bukkitEntityKey) {
+ Preconditions.checkArgument(hasDefaultEntityAttributes(bukkitEntityKey), bukkitEntityKey + " doesn't have default attributes");
+ var supplier = net.minecraft.world.entity.ai.attributes.DefaultAttributes.getSupplier((net.minecraft.world.entity.EntityType<? extends net.minecraft.world.entity.LivingEntity>) net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(bukkitEntityKey)));
+ return new io.papermc.paper.attribute.UnmodifiableAttributeMap(supplier);
+ }
// Paper end
/**
diff --git a/src/test/java/io/papermc/paper/attribute/EntityTypeAttributesTest.java b/src/test/java/io/papermc/paper/attribute/EntityTypeAttributesTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e8cdfa385230d3de202122e4df5e07f61f80ce75
--- /dev/null
+++ b/src/test/java/io/papermc/paper/attribute/EntityTypeAttributesTest.java
@@ -0,0 +1,39 @@
+package io.papermc.paper.attribute;
+
+import org.bukkit.attribute.Attributable;
+import org.bukkit.attribute.Attribute;
+import org.bukkit.attribute.AttributeInstance;
+import org.bukkit.attribute.AttributeModifier;
+import org.bukkit.entity.EntityType;
+import org.bukkit.support.AbstractTestingBase;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class EntityTypeAttributesTest extends AbstractTestingBase {
+
+ @Test
+ public void testIllegalEntity() {
+ assertFalse(EntityType.EGG.hasDefaultAttributes());
+ assertThrows(IllegalArgumentException.class, () -> EntityType.EGG.getDefaultAttributes());
+ }
+
+ @Test
+ public void testLegalEntity() {
+ assertTrue(EntityType.ZOMBIE.hasDefaultAttributes());
+ EntityType.ZOMBIE.getDefaultAttributes();
+ }
+
+ @Test
+ public void testUnmodifiabilityOfAttributable() {
+ Attributable attributable = EntityType.ZOMBIE.getDefaultAttributes();
+ assertThrows(UnsupportedOperationException.class, () -> attributable.registerAttribute(Attribute.GENERIC_ATTACK_DAMAGE));
+ AttributeInstance instance = attributable.getAttribute(Attribute.GENERIC_FOLLOW_RANGE);
+ assertNotNull(instance);
+ assertThrows(UnsupportedOperationException.class, () -> instance.addModifier(new AttributeModifier("test", 3, AttributeModifier.Operation.ADD_NUMBER)));
+ assertThrows(UnsupportedOperationException.class, () -> instance.setBaseValue(3.2));
+ }
+}

View file

@ -0,0 +1,27 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Thu, 14 Oct 2021 12:36:58 -0500
Subject: [PATCH] Left handed API
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java
index a5d3845acc607f640dace05a85f0089611c7d23a..b8d16e2e91e3bf1c004f887bf37e5502938fdd1d 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java
@@ -147,6 +147,16 @@ public abstract class CraftMob extends CraftLivingEntity implements Mob {
public int getMaxHeadPitch() {
return getHandle().getMaxHeadXRot();
}
+
+ @Override
+ public boolean isLeftHanded() {
+ return getHandle().isLeftHanded();
+ }
+
+ @Override
+ public void setLeftHanded(boolean leftHanded) {
+ getHandle().setLeftHanded(leftHanded);
+ }
// Paper end
// Paper start

View file

@ -0,0 +1,219 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: syldium <syldium@mailo.com>
Date: Fri, 9 Jul 2021 18:50:40 +0200
Subject: [PATCH] Add more advancement API
== AT ==
public net.minecraft.advancements.Advancement decorateName(Lnet/minecraft/advancements/DisplayInfo;)Lnet/minecraft/network/chat/Component;
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
diff --git a/src/main/java/io/papermc/paper/advancement/PaperAdvancementDisplay.java b/src/main/java/io/papermc/paper/advancement/PaperAdvancementDisplay.java
new file mode 100644
index 0000000000000000000000000000000000000000..1705f65d897114dd0813f6d366124cefafcb450e
--- /dev/null
+++ b/src/main/java/io/papermc/paper/advancement/PaperAdvancementDisplay.java
@@ -0,0 +1,69 @@
+package io.papermc.paper.advancement;
+
+import io.papermc.paper.adventure.PaperAdventure;
+import net.kyori.adventure.text.Component;
+import net.minecraft.advancements.Advancement;
+import net.minecraft.advancements.DisplayInfo;
+import net.minecraft.advancements.FrameType;
+import org.bukkit.NamespacedKey;
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public record PaperAdvancementDisplay(DisplayInfo handle) implements AdvancementDisplay {
+
+ @Override
+ public @NotNull Frame frame() {
+ return asPaperFrame(this.handle.getFrame());
+ }
+
+ @Override
+ public @NotNull Component title() {
+ return PaperAdventure.asAdventure(this.handle.getTitle());
+ }
+
+ @Override
+ public @NotNull Component description() {
+ return PaperAdventure.asAdventure(this.handle.getDescription());
+ }
+
+ @Override
+ public @NotNull ItemStack icon() {
+ return CraftItemStack.asBukkitCopy(this.handle.getIcon());
+ }
+
+ @Override
+ public boolean doesShowToast() {
+ return this.handle.shouldShowToast();
+ }
+
+ @Override
+ public boolean doesAnnounceToChat() {
+ return this.handle.shouldAnnounceChat();
+ }
+
+ @Override
+ public boolean isHidden() {
+ return this.handle.isHidden();
+ }
+
+ @Override
+ public @Nullable NamespacedKey backgroundPath() {
+ return this.handle.getBackground() == null ? null : CraftNamespacedKey.fromMinecraft(this.handle.getBackground());
+ }
+
+ @Override
+ public @NotNull Component displayName() {
+ return PaperAdventure.asAdventure(Advancement.decorateName(java.util.Objects.requireNonNull(this.handle, "cannot build display name for null handle, invalid state")));
+ }
+
+ public static @NotNull Frame asPaperFrame(@NotNull FrameType frameType) {
+ return switch (frameType) {
+ case TASK -> Frame.TASK;
+ case CHALLENGE -> Frame.CHALLENGE;
+ case GOAL -> Frame.GOAL;
+ };
+ }
+}
diff --git a/src/main/java/net/minecraft/advancements/DisplayInfo.java b/src/main/java/net/minecraft/advancements/DisplayInfo.java
index d357deb8a9e1d4043f5fb3302b957b20ffc0cc32..d83acd5eac3d7d1893b1b97ab0b0764c06da016b 100644
--- a/src/main/java/net/minecraft/advancements/DisplayInfo.java
+++ b/src/main/java/net/minecraft/advancements/DisplayInfo.java
@@ -24,6 +24,7 @@ public class DisplayInfo {
private final boolean hidden;
private float x;
private float y;
+ public final io.papermc.paper.advancement.AdvancementDisplay paper = new io.papermc.paper.advancement.PaperAdvancementDisplay(this); // Paper
public DisplayInfo(ItemStack icon, Component title, Component description, Optional<ResourceLocation> background, AdvancementType frame, boolean showToast, boolean announceToChat, boolean hidden) {
this.title = title;
diff --git a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java
index 52baf818579a6841b77ff80e42f4f1b9f635ea08..bd640e0d8d796ee114ff787def7e07edbeffc0a5 100644
--- a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java
+++ b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancement.java
@@ -29,12 +29,47 @@ public class CraftAdvancement implements org.bukkit.advancement.Advancement {
return Collections.unmodifiableCollection(this.handle.value().criteria().keySet());
}
+ // Paper start
@Override
- public AdvancementDisplay getDisplay() {
- if (this.handle.value().display().isEmpty()) {
- return null;
+ public io.papermc.paper.advancement.AdvancementDisplay getDisplay() {
+ return this.handle.value().display().map(d -> d.paper).orElse(null);
+ }
+
+ @Deprecated
+ @io.papermc.paper.annotation.DoNotUse
+ public AdvancementDisplay getDisplay0() { // May be called by plugins via Commodore
+ return this.handle.value().display().map(CraftAdvancementDisplay::new).orElse(null);
+ }
+
+ @Override
+ public net.kyori.adventure.text.Component displayName() {
+ return io.papermc.paper.adventure.PaperAdventure.asAdventure(net.minecraft.advancements.Advancement.name(this.handle));
+ }
+
+ @Override
+ public org.bukkit.advancement.Advancement getParent() {
+ return this.handle.value().parent()
+ .map(net.minecraft.server.MinecraftServer.getServer().getAdvancements()::get)
+ .map(AdvancementHolder::toBukkit)
+ .orElse(null);
+ }
+
+ @Override
+ public Collection<org.bukkit.advancement.Advancement> getChildren() {
+ final com.google.common.collect.ImmutableList.Builder<org.bukkit.advancement.Advancement> children = com.google.common.collect.ImmutableList.<org.bukkit.advancement.Advancement>builder();
+ final net.minecraft.advancements.AdvancementNode advancementNode = net.minecraft.server.MinecraftServer.getServer().getAdvancements().tree().get(this.handle);
+ if (advancementNode != null) {
+ for (final net.minecraft.advancements.AdvancementNode child : advancementNode.children()) {
+ children.add(child.holder().toBukkit());
+ }
}
+ return children.build();
+ }
- return new CraftAdvancementDisplay(this.handle.value().display().get());
+ @Override
+ public org.bukkit.advancement.Advancement getRoot() {
+ final net.minecraft.advancements.AdvancementNode advancementNode = net.minecraft.server.MinecraftServer.getServer().getAdvancements().tree().get(this.handle);
+ return java.util.Objects.requireNonNull(advancementNode, "could not find internal advancement node for advancement " + this.handle.id()).root().holder().toBukkit();
}
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java
index 8ca86852319d7463f60832bc98b825b0b4325995..62ada73302c6b3ce3fb2dcc8c31a1d9c0ac4fd09 100644
--- a/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java
+++ b/src/main/java/org/bukkit/craftbukkit/advancement/CraftAdvancementDisplay.java
@@ -6,6 +6,7 @@ import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.inventory.ItemStack;
+@Deprecated // Paper
public class CraftAdvancementDisplay implements org.bukkit.advancement.AdvancementDisplay {
private final DisplayInfo handle;
diff --git a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
index 812819e814cfbdb542051a7dbfe123d3c59e66bd..61d00421b295103a6964b22fe0dfaf097bd7a671 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
@@ -56,6 +56,7 @@ public class Commodore
) );
// Paper start - Plugin rewrites
+ private static final String CB_PACKAGE = org.bukkit.Bukkit.getServer().getClass().getPackageName().replace('.', '/');
private static final Map<String, String> SEARCH_AND_REMOVE = initReplacementsMap();
private static Map<String, String> initReplacementsMap()
{
@@ -457,6 +458,11 @@ public class Commodore
super.visitMethodInsn(opcode, owner, name, "()Lcom/destroystokyo/paper/profile/PlayerProfile;", itf);
return;
}
+ if (owner.equals("org/bukkit/advancement/Advancement") && name.equals("getDisplay") && desc.endsWith(")Lorg/bukkit/advancement/AdvancementDisplay;")) {
+ super.visitTypeInsn(Opcodes.CHECKCAST, CB_PACKAGE + "/advancement/CraftAdvancement");
+ super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CB_PACKAGE + "/advancement/CraftAdvancement", "getDisplay0", desc, false);
+ return;
+ }
// Paper end
if ( modern )
{
diff --git a/src/test/java/io/papermc/paper/advancement/AdvancementFrameTest.java b/src/test/java/io/papermc/paper/advancement/AdvancementFrameTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1a4b930ecf10dac8336dd2eceb4ee2bf9ec780d5
--- /dev/null
+++ b/src/test/java/io/papermc/paper/advancement/AdvancementFrameTest.java
@@ -0,0 +1,24 @@
+package io.papermc.paper.advancement;
+
+import io.papermc.paper.adventure.PaperAdventure;
+import net.kyori.adventure.text.format.TextColor;
+import net.minecraft.advancements.FrameType;
+import net.minecraft.network.chat.contents.TranslatableContents;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class AdvancementFrameTest {
+
+ @Test
+ public void test() {
+ for (FrameType nmsFrameType : FrameType.values()) {
+ final TextColor expectedColor = PaperAdventure.asAdventure(nmsFrameType.getChatColor());
+ final String expectedTranslationKey = ((TranslatableContents) nmsFrameType.getDisplayName().getContents()).getKey();
+ final var frame = PaperAdvancementDisplay.asPaperFrame(nmsFrameType);
+ assertEquals(expectedTranslationKey, frame.translationKey(), "The translation keys should be the same");
+ assertEquals(expectedColor, frame.color(), "The frame colors should be the same");
+ assertEquals(nmsFrameType.getName(), AdvancementDisplay.Frame.NAMES.key(frame));
+ }
+ }
+}

View file

@ -0,0 +1,51 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Thu, 14 Oct 2021 12:09:39 -0500
Subject: [PATCH] Add ItemFactory#getSpawnEgg API
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
index 866106d0d773e407a0cdd8614818cba4ab910040..259489c7d0f4b5ce3e8f4294f4f853e9b51ded0b 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
@@ -570,4 +570,19 @@ public final class CraftItemFactory implements ItemFactory {
new net.md_5.bungee.api.chat.TextComponent(customName));
}
// Paper end - bungee hover events
+
+ // Paper start - old getSpawnEgg API
+ // @Override // used to override, upstream added conflicting method, is called via Commodore now
+ @Deprecated
+ public ItemStack getSpawnEgg0(org.bukkit.entity.EntityType type) {
+ if (type == null) {
+ return null;
+ }
+ String typeId = type.getKey().toString();
+ net.minecraft.resources.ResourceLocation typeKey = new net.minecraft.resources.ResourceLocation(typeId);
+ net.minecraft.world.entity.EntityType<?> nmsType = net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.get(typeKey);
+ net.minecraft.world.item.SpawnEggItem eggItem = net.minecraft.world.item.SpawnEggItem.byId(nmsType);
+ return eggItem == null ? null : new net.minecraft.world.item.ItemStack(eggItem).asBukkitMirror();
+ }
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
index 61d00421b295103a6964b22fe0dfaf097bd7a671..2a75bd263dce91bc64601f96b622ed6d4fb18fe6 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
@@ -464,6 +464,16 @@ public class Commodore
return;
}
// Paper end
+
+ // Paper start - ItemFactory#getSpawnEgg (paper had original method that returned ItemStack, upstream added identical but returned Material)
+ if (owner.equals("org/bukkit/inventory/ItemFactory") && name.equals("getSpawnEgg") && desc.equals("(Lorg/bukkit/entity/EntityType;)Lorg/bukkit/inventory/ItemStack;")) {
+ super.visitInsn(Opcodes.SWAP); // has 1 param, this moves the owner instance to the top for the checkcast
+ super.visitTypeInsn(Opcodes.CHECKCAST, CB_PACKAGE + "/inventory/CraftItemFactory");
+ super.visitInsn(Opcodes.SWAP); // moves param back to the the top of stack
+ super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, CB_PACKAGE + "/inventory/CraftItemFactory", "getSpawnEgg0", desc, false);
+ return;
+ }
+ // Paper end - ItemFactory#getSpawnEgg
if ( modern )
{
if ( owner.equals( "org/bukkit/Material" ) )

View file

@ -0,0 +1,134 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: dodison <kacpik@mapik.eu>
Date: Mon, 26 Jul 2021 17:32:36 +0200
Subject: [PATCH] Add critical damage API
diff --git a/src/main/java/net/minecraft/world/damagesource/DamageSource.java b/src/main/java/net/minecraft/world/damagesource/DamageSource.java
index df8c88bfa749e02f633350446101dcce05db7ac1..1a0f86b5a632469942e33c237c247d2d1dee4a3d 100644
--- a/src/main/java/net/minecraft/world/damagesource/DamageSource.java
+++ b/src/main/java/net/minecraft/world/damagesource/DamageSource.java
@@ -191,4 +191,18 @@ public class DamageSource {
public Holder<DamageType> typeHolder() {
return this.type;
}
+
+ // Paper start - add critical damage API
+ private boolean critical;
+ public boolean isCritical() {
+ return this.critical;
+ }
+ public DamageSource critical() {
+ return this.critical(true);
+ }
+ public DamageSource critical(boolean critical) {
+ this.critical = critical;
+ return this;
+ }
+ // Paper end
}
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
index e77a2d3a321313e8476068d895dfb39cb152f7e6..9193e0fb5c2a545907c084322b548722312a5583 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -1263,7 +1263,7 @@ public abstract class Player extends LivingEntity {
flag1 = true;
}
- boolean flag2 = flag && this.fallDistance > 0.0F && !this.onGround() && !this.onClimbable() && !this.isInWater() && !this.hasEffect(MobEffects.BLINDNESS) && !this.isPassenger() && target instanceof LivingEntity;
+ boolean flag2 = flag && this.fallDistance > 0.0F && !this.onGround() && !this.onClimbable() && !this.isInWater() && !this.hasEffect(MobEffects.BLINDNESS) && !this.isPassenger() && target instanceof LivingEntity; // Paper - Add critical damage API - conflict on change
flag2 = flag2 && !this.level().paperConfig().entities.behavior.disablePlayerCrits; // Paper
flag2 = flag2 && !this.isSprinting();
@@ -1303,7 +1303,7 @@ public abstract class Player extends LivingEntity {
}
Vec3 vec3d = target.getDeltaMovement();
- boolean flag5 = target.hurt(this.damageSources().playerAttack(this), f);
+ boolean flag5 = target.hurt(this.damageSources().playerAttack(this).critical(flag2), f); // Paper - add critical damage API
if (flag5) {
if (i > 0) {
@@ -1331,7 +1331,7 @@ public abstract class Player extends LivingEntity {
if (entityliving != this && entityliving != target && !this.isAlliedTo((Entity) entityliving) && (!(entityliving instanceof ArmorStand) || !((ArmorStand) entityliving).isMarker()) && this.distanceToSqr((Entity) entityliving) < 9.0D) {
// CraftBukkit start - Only apply knockback if the damage hits
- if (entityliving.hurt(this.damageSources().playerAttack(this).sweep(), f4)) {
+ if (entityliving.hurt(this.damageSources().playerAttack(this).sweep().critical(flag2), f4)) { // Paper - add critical damage API
entityliving.knockback(0.4000000059604645D, (double) Mth.sin(this.getYRot() * 0.017453292F), (double) (-Mth.cos(this.getYRot() * 0.017453292F)), this); // Pa
}
// CraftBukkit end
diff --git a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java
index 004c130fc03dc01ef75fabdb4ef1ef711e33cb95..ae4319c2532855315bc45995a39d7eb98961b939 100644
--- a/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java
+++ b/src/main/java/net/minecraft/world/entity/projectile/AbstractArrow.java
@@ -379,6 +379,7 @@ public abstract class AbstractArrow extends Projectile {
}
}
+ if (this.isCritArrow()) damagesource = damagesource.critical(); // Paper - add critical damage API
boolean flag = entity.getType() == EntityType.ENDERMAN;
int k = entity.getRemainingFireTicks();
boolean flag1 = entity.getType().is(EntityTypeTags.DEFLECTS_ARROWS);
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index 9bb9be0c73386c3f1c49f8831402789e92e23c0f..cc679e7042c5a5fba2ce34598f2c1eaf49bb065f 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -1046,7 +1046,7 @@ public class CraftEventFactory {
} else {
damageCause = DamageCause.ENTITY_EXPLOSION;
}
- event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), entity.getBukkitEntity(), damageCause, modifiers, modifierFunctions);
+ event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), entity.getBukkitEntity(), damageCause, modifiers, modifierFunctions, source.isCritical()); // Paper - add critical damage API
}
event.setCancelled(cancelled);
@@ -1078,7 +1078,7 @@ public class CraftEventFactory {
cause = DamageCause.SONIC_BOOM;
}
- return CraftEventFactory.callEntityDamageEvent(damager, entity, cause, modifiers, modifierFunctions, cancelled);
+ return CraftEventFactory.callEntityDamageEvent(damager, entity, cause, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API
} else if (source.is(DamageTypes.FELL_OUT_OF_WORLD)) {
EntityDamageEvent event = new EntityDamageByBlockEvent(null, entity.getBukkitEntity(), DamageCause.VOID, modifiers, modifierFunctions);
event.setCancelled(cancelled);
@@ -1148,7 +1148,7 @@ public class CraftEventFactory {
} else {
throw new IllegalStateException(String.format("Unhandled damage of %s by %s from %s", entity, damager.getHandle(), source.getMsgId()));
}
- EntityDamageEvent event = new EntityDamageByEntityEvent(damager, entity.getBukkitEntity(), cause, modifiers, modifierFunctions);
+ EntityDamageEvent event = new EntityDamageByEntityEvent(damager, entity.getBukkitEntity(), cause, modifiers, modifierFunctions, source.isCritical()); // Paper - add critical damage API
event.setCancelled(cancelled);
CraftEventFactory.callEvent(event);
if (!event.isCancelled()) {
@@ -1197,20 +1197,28 @@ public class CraftEventFactory {
}
if (cause != null) {
- return CraftEventFactory.callEntityDamageEvent(null, entity, cause, modifiers, modifierFunctions, cancelled);
+ return CraftEventFactory.callEntityDamageEvent(null, entity, cause, modifiers, modifierFunctions, cancelled, source.isCritical()); // Paper - add critical damage API
}
throw new IllegalStateException(String.format("Unhandled damage of %s from %s", entity, source.getMsgId()));
}
+ @Deprecated // Paper - Add critical damage API
private static EntityDamageEvent callEntityDamageEvent(Entity damager, Entity damagee, DamageCause cause, Map<DamageModifier, Double> modifiers, Map<DamageModifier, Function<? super Double, Double>> modifierFunctions) {
return CraftEventFactory.callEntityDamageEvent(damager, damagee, cause, modifiers, modifierFunctions, false);
}
+ // Paper start - Add critical damage API
+ @Deprecated
private static EntityDamageEvent callEntityDamageEvent(Entity damager, Entity damagee, DamageCause cause, Map<DamageModifier, Double> modifiers, Map<DamageModifier, Function<? super Double, Double>> modifierFunctions, boolean cancelled) {
+ return CraftEventFactory.callEntityDamageEvent(damager, damagee, cause, modifiers, modifierFunctions, false, false);
+ }
+
+ private static EntityDamageEvent callEntityDamageEvent(Entity damager, Entity damagee, DamageCause cause, Map<DamageModifier, Double> modifiers, Map<DamageModifier, Function<? super Double, Double>> modifierFunctions, boolean cancelled, boolean critical) {
+ // Paper end
EntityDamageEvent event;
if (damager != null) {
- event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, modifiers, modifierFunctions);
+ event = new EntityDamageByEntityEvent(damager.getBukkitEntity(), damagee.getBukkitEntity(), cause, modifiers, modifierFunctions, critical); // Paper - add critical damage API
} else {
event = new EntityDamageEvent(damagee.getBukkitEntity(), cause, modifiers, modifierFunctions);
}

View file

@ -0,0 +1,59 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 24 Oct 2021 20:29:45 -0700
Subject: [PATCH] Fix issues with mob conversion
diff --git a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
index e88af2dcc0f7fc5190654e2640f67d706e6c8c81..8b818a7cb835512c4bd2ea9641d4bfd904150332 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Skeleton.java
@@ -90,10 +90,15 @@ public class Skeleton extends AbstractSkeleton {
}
protected void doFreezeConversion() {
- this.convertTo(EntityType.STRAY, true, org.bukkit.event.entity.EntityTransformEvent.TransformReason.FROZEN, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.FROZEN); // CraftBukkit - add spawn and transform reasons
+ Stray stray = this.convertTo(EntityType.STRAY, true, org.bukkit.event.entity.EntityTransformEvent.TransformReason.FROZEN, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.FROZEN); // CraftBukkit - add spawn and transform reasons // Paper - track result of conversion
if (!this.isSilent()) {
this.level().levelEvent((Player) null, 1048, this.blockPosition(), 0);
}
+ // Paper start - reset conversion time to prevent event spam
+ if (stray == null) {
+ this.conversionTime = 300;
+ }
+ // Paper end
}
diff --git a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
index 7bcd5498c734873b74bee503992ec4806ae61df7..4257f2282152aee09533c9a2e53018d3e49effa4 100644
--- a/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
+++ b/src/main/java/net/minecraft/world/entity/monster/hoglin/Hoglin.java
@@ -241,6 +241,11 @@ public class Hoglin extends Animal implements Enemy, HoglinBase {
if (zoglin != null) {
zoglin.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 200, 0));
}
+ // Paper start - reset to prevent event spam
+ else {
+ this.timeInOverworld = 0;
+ }
+ // Paper end
}
diff --git a/src/main/java/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java b/src/main/java/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java
index 22c8d6233be5e4f7fb4f03176e83dbee02256b1f..4384cbf9c53b220128cd278f126466b143fab2f2 100644
--- a/src/main/java/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java
+++ b/src/main/java/net/minecraft/world/entity/monster/piglin/AbstractPiglin.java
@@ -120,6 +120,11 @@ public abstract class AbstractPiglin extends Monster {
if (entitypigzombie != null) {
entitypigzombie.addEffect(new MobEffectInstance(MobEffects.CONFUSION, 200, 0));
}
+ // Paper start - reset to prevent event spam
+ else {
+ this.timeInOverworld = 0;
+ }
+ // Paper end
}

View file

@ -0,0 +1,57 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 4 Nov 2021 11:50:40 -0700
Subject: [PATCH] Add isCollidable methods to various places
== AT ==
public net.minecraft.world.level.block.state.BlockBehaviour hasCollision
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
index ce297420f695404356655b1df2847a32fb98ec59..068b3735b6c50a7a2053c7dc39856f728fb7218a 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java
@@ -457,6 +457,11 @@ public class CraftBlock implements Block {
public boolean isSolid() {
return this.getNMS().blocksMotion();
}
+
+ @Override
+ public boolean isCollidable() {
+ return getNMS().getBlock().hasCollision;
+ }
// Paper end
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
index aca63719790429d3d7c7c59a1931a98221c70fc0..31bb92c026a4a2de0e8d3500f6ecf35b60d61fb9 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java
@@ -331,4 +331,11 @@ public class CraftBlockState implements BlockState {
public CraftBlockState copy() {
return new CraftBlockState(this);
}
+
+ // Paper start
+ @Override
+ public boolean isCollidable() {
+ return this.data.getBlock().hasCollision;
+ }
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index 33988ab25aba60c75bffbd39da5562333875d0c1..da0d44cf18888945ed24d941af0f1a3562156e50 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -572,6 +572,12 @@ public final class CraftMagicNumbers implements UnsafeValues {
var supplier = net.minecraft.world.entity.ai.attributes.DefaultAttributes.getSupplier((net.minecraft.world.entity.EntityType<? extends net.minecraft.world.entity.LivingEntity>) net.minecraft.core.registries.BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(bukkitEntityKey)));
return new io.papermc.paper.attribute.UnmodifiableAttributeMap(supplier);
}
+
+ @Override
+ public boolean isCollidable(Material material) {
+ Preconditions.checkArgument(material.isBlock(), material + " is not a block");
+ return getBlock(material).hasCollision;
+ }
// Paper end
/**

View file

@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Seggan <segganew@gmail.com>
Date: Thu, 5 Aug 2021 13:10:27 -0400
Subject: [PATCH] Goat ram API
diff --git a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
index 52f3f679568955b632a60d44de687c6db0e2b38a..3ec2f590dfe9410f1a9d2afb530eebfcce917798 100644
--- a/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
+++ b/src/main/java/net/minecraft/world/entity/animal/goat/Goat.java
@@ -392,4 +392,15 @@ public class Goat extends Animal {
protected Vector3f getPassengerAttachmentPoint(Entity passenger, EntityDimensions dimensions, float scaleFactor) {
return new Vector3f(0.0F, dimensions.height - 0.1875F * scaleFactor, 0.0F);
}
+
+ // Paper start - Goat ram API
+ public void ram(net.minecraft.world.entity.LivingEntity entity) {
+ Brain<Goat> brain = this.getBrain();
+ brain.setMemory(MemoryModuleType.RAM_TARGET, entity.position());
+ brain.eraseMemory(MemoryModuleType.RAM_COOLDOWN_TICKS);
+ brain.eraseMemory(MemoryModuleType.BREED_TARGET);
+ brain.eraseMemory(MemoryModuleType.TEMPTING_PLAYER);
+ brain.setActiveActivityIfPossible(net.minecraft.world.entity.schedule.Activity.RAM);
+ }
+ // Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftGoat.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftGoat.java
index 65fcb36e849da6949c123a6f3672b485036f368e..2c21de478bff9cdf13ba46cd041831d54c11e924 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftGoat.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftGoat.java
@@ -48,4 +48,11 @@ public class CraftGoat extends CraftAnimals implements Goat {
public void setScreaming(boolean screaming) {
this.getHandle().setScreamingGoat(screaming);
}
+
+ // Paper start - Goat ram API
+ @Override
+ public void ram(@org.jetbrains.annotations.NotNull org.bukkit.entity.LivingEntity entity) {
+ this.getHandle().ram(((CraftLivingEntity) entity).getHandle());
+ }
+ // Paper end
}

View file

@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: booky10 <boooky10@gmail.com>
Date: Fri, 5 Nov 2021 21:01:36 +0100
Subject: [PATCH] Add API for resetting a single score
It was only possible to reset all scores for a specific entry, instead of resetting only specific scores.
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java
index a7f53c135bae2a464e7cd28bf8e990d692c63d63..426a99e839986eb9c25cf4e65191f5a5a1efab6c 100644
--- a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java
@@ -68,4 +68,12 @@ final class CraftScore implements Score {
public CraftScoreboard getScoreboard() {
return this.objective.getScoreboard();
}
+
+ // Paper start
+ @Override
+ public void resetScore() {
+ Scoreboard board = this.objective.checkState().board;
+ board.resetPlayerScore(entry, this.objective.getHandle());
+ }
+ // Paper end
}

View file

@ -0,0 +1,82 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mariell Hoversholm <proximyst@proximyst.com>
Date: Sun, 24 Oct 2021 16:20:31 -0400
Subject: [PATCH] Add Raw Byte Entity Serialization
== AT ==
public net.minecraft.world.entity.Entity setLevel(Lnet/minecraft/world/level/Level;)V
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 5f642ba64877a3ba53f69b8cffd99ed8c56a3d3f..430f36da1e40d8427f24994ab16e68da79f575d2 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -2157,6 +2157,15 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, S
}
}
+ // Paper start - Entity serialization api
+ public boolean serializeEntity(CompoundTag compound) {
+ List<Entity> pass = new java.util.ArrayList<>(this.getPassengers());
+ this.passengers = ImmutableList.of();
+ boolean result = save(compound);
+ this.passengers = ImmutableList.copyOf(pass);
+ return result;
+ }
+ // Paper end
public boolean save(CompoundTag nbt) {
return this.isPassenger() ? false : this.saveAsPassenger(nbt);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 6065336cd30e6400bd8875768f06c44a4be0a1e1..32fd3b7067894bc442bc5a74dbcf60164a8fd70f 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -1431,5 +1431,15 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
}
return set;
}
+
+ @Override
+ public boolean spawnAt(Location location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
+ Preconditions.checkNotNull(location, "location cannot be null");
+ Preconditions.checkNotNull(reason, "reason cannot be null");
+ this.entity.setLevel(((CraftWorld) location.getWorld()).getHandle());
+ this.entity.setPos(location.getX(), location.getY(), location.getZ());
+ this.entity.setRot(location.getYaw(), location.getPitch());
+ return !this.entity.valid && this.entity.level().addFreshEntity(this.entity, reason);
+ }
// Paper end
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index da0d44cf18888945ed24d941af0f1a3562156e50..ef59fcd13f8835001fcc6fba6165ffd6c35784fa 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -483,6 +483,29 @@ public final class CraftMagicNumbers implements UnsafeValues {
return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.of(ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.ITEM_STACK, compound, dataVersion, getDataVersion())));
}
+ @Override
+ public byte[] serializeEntity(org.bukkit.entity.Entity entity) {
+ Preconditions.checkNotNull(entity, "null cannot be serialized");
+ Preconditions.checkArgument(entity instanceof org.bukkit.craftbukkit.entity.CraftEntity, "only CraftEntities can be serialized");
+
+ CompoundTag compound = new CompoundTag();
+ ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().serializeEntity(compound);
+ return serializeNbtToBytes(compound);
+ }
+
+ @Override
+ public org.bukkit.entity.Entity deserializeEntity(byte[] data, org.bukkit.World world, boolean preserveUUID) {
+ Preconditions.checkNotNull(data, "null cannot be deserialized");
+ Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing");
+
+ CompoundTag compound = deserializeNbtFromBytes(data);
+ int dataVersion = compound.getInt("DataVersion");
+ compound = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertTag(ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.ENTITY, compound, dataVersion, getDataVersion());
+ if (!preserveUUID) compound.remove("UUID"); // Generate a new UUID so we don't have to worry about deserializing the same entity twice
+ return net.minecraft.world.entity.EntityType.create(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle())
+ .orElseThrow(() -> new IllegalArgumentException("An ID was not found for the data. Did you downgrade?")).getBukkitEntity();
+ }
+
private byte[] serializeNbtToBytes(CompoundTag compound) {
compound.putInt("DataVersion", getDataVersion());
java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream();

View file

@ -0,0 +1,81 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Wed, 25 Aug 2021 13:19:53 -0700
Subject: [PATCH] Vanilla command permission fixes
Fixes permission checks for vanilla commands which don't have a
requirement, as well as for namespaced vanilla commands.
== AT ==
public-f com.mojang.brigadier.tree.CommandNode requirement
diff --git a/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java b/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java
index 899008b2980d13f1be6280cd8cb959c53a29bebf..f875507241ac6769545e91cd3285232b75b892f0 100644
--- a/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java
+++ b/src/main/java/com/mojang/brigadier/builder/ArgumentBuilder.java
@@ -14,9 +14,17 @@ import java.util.Collections;
import java.util.function.Predicate;
public abstract class ArgumentBuilder<S, T extends ArgumentBuilder<S, T>> {
+ // Paper start
+ private static final Predicate<Object> DEFAULT_REQUIREMENT = s -> true;
+
+ @SuppressWarnings("unchecked")
+ public static <S> Predicate<S> defaultRequirement() {
+ return (Predicate<S>) DEFAULT_REQUIREMENT;
+ }
+ // Paper end
private final RootCommandNode<S> arguments = new RootCommandNode<>();
private Command<S> command;
- private Predicate<S> requirement = s -> true;
+ private Predicate<S> requirement = defaultRequirement(); // Paper
private CommandNode<S> target;
private RedirectModifier<S> modifier = null;
private boolean forks;
diff --git a/src/main/java/net/minecraft/commands/Commands.java b/src/main/java/net/minecraft/commands/Commands.java
index 094eb2d3aff11c861490e04cc054918bff5e2d87..154cbcab900f4697f280a88d6307682a26f46bfc 100644
--- a/src/main/java/net/minecraft/commands/Commands.java
+++ b/src/main/java/net/minecraft/commands/Commands.java
@@ -257,6 +257,13 @@ public class Commands {
}
this.vanillaCommandNodes.addAll(this.dispatcher.getRoot().getChildren()); // Paper
+ // Paper start
+ for (final CommandNode<CommandSourceStack> node : this.dispatcher.getRoot().getChildren()) {
+ if (node.getRequirement() == com.mojang.brigadier.builder.ArgumentBuilder.<CommandSourceStack>defaultRequirement()) {
+ node.requirement = stack -> stack.source == CommandSource.NULL || stack.getBukkitSender().hasPermission(org.bukkit.craftbukkit.command.VanillaCommandWrapper.getPermission(node));
+ }
+ }
+ // Paper end
// CraftBukkit start
}
diff --git a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
index 35746f4fc45568c32497677b63a2a746c7a9ba94..13804bb81a13d51d2e9e55d8a477369ba20fc301 100644
--- a/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
+++ b/src/main/java/org/bukkit/craftbukkit/command/VanillaCommandWrapper.java
@@ -88,7 +88,23 @@ public final class VanillaCommandWrapper extends BukkitCommand {
}
public static String getPermission(CommandNode<CommandSourceStack> vanillaCommand) {
- return "minecraft.command." + ((vanillaCommand.getRedirect() == null) ? vanillaCommand.getName() : vanillaCommand.getRedirect().getName());
+ // Paper start
+ final String commandName;
+ if (vanillaCommand.getRedirect() == null) {
+ commandName = vanillaCommand.getName();
+ } else {
+ commandName = vanillaCommand.getRedirect().getName();
+ }
+ return "minecraft.command." + stripDefaultNamespace(commandName);
+ }
+
+ private static String stripDefaultNamespace(final String maybeNamespaced) {
+ final String prefix = "minecraft:";
+ if (maybeNamespaced.startsWith(prefix)) {
+ return maybeNamespaced.substring(prefix.length());
+ }
+ return maybeNamespaced;
+ // Paper end
}
private String toDispatcher(String[] args, String name) {

View file

@ -0,0 +1,68 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Thu, 11 Mar 2021 03:03:32 -0800
Subject: [PATCH] Do not run close logic for inventories on chunk unload
Still call the event and change the active container though. We
want to avoid close logic because it's possible to load the
chunk through it. This should also be OK from a leak prevention/
state desync POV because the TE is getting unloaded anyways.
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
index d5709319497a5589665388afd61307dc7755332d..938a07d2e90ea2516b064d930b38e8908be91055 100644
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
+++ b/src/main/java/net/minecraft/server/level/ServerLevel.java
@@ -1574,9 +1574,13 @@ public class ServerLevel extends Level implements WorldGenLevel {
// Spigot Start
for (net.minecraft.world.level.block.entity.BlockEntity tileentity : chunk.getBlockEntities().values()) {
if (tileentity instanceof net.minecraft.world.Container) {
+ // Paper start - this area looks like it can load chunks, change the behavior
+ // chests for example can apply physics to the world
+ // so instead we just change the active container and call the event
for (org.bukkit.entity.HumanEntity h : Lists.newArrayList(((net.minecraft.world.Container) tileentity).getViewers())) {
- h.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper
+ ((org.bukkit.craftbukkit.entity.CraftHumanEntity)h).getHandle().closeUnloadedInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper
}
+ // Paper end
}
}
// Spigot End
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index b1be9c5d7346841ea4c0f9b8aec5e7c4d9367dc8..5035609b0a4160c2671ee9939e2d8bbf17749370 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -1629,6 +1629,18 @@ public class ServerPlayer extends Player {
this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId));
this.doCloseContainer();
}
+ // Paper start - special close for unloaded inventory
+ @Override
+ public void closeUnloadedInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) {
+ // copied from above
+ CraftEventFactory.handleInventoryCloseEvent(this, reason); // CraftBukkit
+ // Paper end
+ // copied from below
+ this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId));
+ this.containerMenu = this.inventoryMenu;
+ // do not run close logic
+ }
+ // Paper end - special close for unloaded inventory
@Override
public void doCloseContainer() {
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
index 9193e0fb5c2a545907c084322b548722312a5583..bce494bb7bc1ce20809ac7d355f04aa7aad78308 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -508,6 +508,11 @@ public abstract class Player extends LivingEntity {
this.containerMenu = this.inventoryMenu;
}
// Paper end
+ // Paper start - special close for unloaded inventory
+ public void closeUnloadedInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) {
+ this.containerMenu = this.inventoryMenu;
+ }
+ // Paper end - special close for unloaded inventory
public void closeContainer() {
this.containerMenu = this.inventoryMenu;

View file

@ -0,0 +1,126 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sat, 11 Jul 2020 05:09:28 -0700
Subject: [PATCH] Fix GameProfileCache concurrency
Separate lookup and state access locks prevent lookups
from stalling simple state access/write calls
diff --git a/src/main/java/net/minecraft/server/players/GameProfileCache.java b/src/main/java/net/minecraft/server/players/GameProfileCache.java
index 4dc08b0abf0a1edb51cc586d1a89444ba790ca7f..13e05a9e42ba89d37c423d819da33f2f67fa9eac 100644
--- a/src/main/java/net/minecraft/server/players/GameProfileCache.java
+++ b/src/main/java/net/minecraft/server/players/GameProfileCache.java
@@ -61,6 +61,11 @@ public class GameProfileCache {
@Nullable
private Executor executor;
+ // Paper start
+ protected final java.util.concurrent.locks.ReentrantLock stateLock = new java.util.concurrent.locks.ReentrantLock();
+ protected final java.util.concurrent.locks.ReentrantLock lookupLock = new java.util.concurrent.locks.ReentrantLock();
+ // Paper end
+
public GameProfileCache(GameProfileRepository profileRepository, File cacheFile) {
this.profileRepository = profileRepository;
this.file = cacheFile;
@@ -68,11 +73,13 @@ public class GameProfileCache {
}
private void safeAdd(GameProfileCache.GameProfileInfo entry) {
+ try { this.stateLock.lock(); // Paper - allow better concurrency
GameProfile gameprofile = entry.getProfile();
entry.setLastAccess(this.getNextOperation());
this.profilesByName.put(gameprofile.getName().toLowerCase(Locale.ROOT), entry);
this.profilesByUUID.put(gameprofile.getId(), entry);
+ } finally { this.stateLock.unlock(); } // Paper - allow better concurrency
}
private static Optional<GameProfile> lookupGameProfile(GameProfileRepository repository, String name) {
@@ -129,17 +136,20 @@ public class GameProfileCache {
// Paper start
public @Nullable GameProfile getProfileIfCached(String name) {
+ try { this.stateLock.lock(); // Paper - allow better concurrency
GameProfileCache.GameProfileInfo entry = this.profilesByName.get(name.toLowerCase(Locale.ROOT));
if (entry == null) {
return null;
}
entry.setLastAccess(this.getNextOperation());
return entry.getProfile();
+ } finally { this.stateLock.unlock(); } // Paper - allow better concurrency
}
// Paper end
public Optional<GameProfile> get(String name) {
String s1 = name.toLowerCase(Locale.ROOT);
+ boolean stateLocked = true; try { this.stateLock.lock(); // Paper - allow better concurrency
GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByName.get(s1);
boolean flag = false;
@@ -155,8 +165,12 @@ public class GameProfileCache {
if (usercache_usercacheentry != null) {
usercache_usercacheentry.setLastAccess(this.getNextOperation());
optional = Optional.of(usercache_usercacheentry.getProfile());
+ stateLocked = false; this.stateLock.unlock(); // Paper - allow better concurrency
} else {
+ stateLocked = false; this.stateLock.unlock(); // Paper - allow better concurrency
+ try { this.lookupLock.lock(); // Paper - allow better concurrency
optional = GameProfileCache.lookupGameProfile(this.profileRepository, name); // Spigot - use correct case for offline players
+ } finally { this.lookupLock.unlock(); } // Paper - allow better concurrency
if (optional.isPresent()) {
this.add((GameProfile) optional.get());
flag = false;
@@ -168,6 +182,7 @@ public class GameProfileCache {
}
return optional;
+ } finally { if (stateLocked) { this.stateLock.unlock(); } } // Paper - allow better concurrency
}
public CompletableFuture<Optional<GameProfile>> getAsync(String username) {
@@ -192,6 +207,7 @@ public class GameProfileCache {
}
public Optional<GameProfile> get(UUID uuid) {
+ try { this.stateLock.lock(); // Paper - allow better concurrency
GameProfileCache.GameProfileInfo usercache_usercacheentry = (GameProfileCache.GameProfileInfo) this.profilesByUUID.get(uuid);
if (usercache_usercacheentry == null) {
@@ -200,6 +216,7 @@ public class GameProfileCache {
usercache_usercacheentry.setLastAccess(this.getNextOperation());
return Optional.of(usercache_usercacheentry.getProfile());
}
+ } finally { this.stateLock.unlock(); } // Paper - allow better concurrency
}
public void setExecutor(Executor executor) {
@@ -280,7 +297,7 @@ public class GameProfileCache {
JsonArray jsonarray = new JsonArray();
DateFormat dateformat = GameProfileCache.createDateFormat();
- this.getTopMRUProfiles(org.spigotmc.SpigotConfig.userCacheCap).forEach((usercache_usercacheentry) -> { // Spigot
+ this.listTopMRUProfiles(org.spigotmc.SpigotConfig.userCacheCap).forEach((usercache_usercacheentry) -> { // Spigot // Paper - allow better concurrency
jsonarray.add(GameProfileCache.writeGameProfile(usercache_usercacheentry, dateformat));
});
String s = this.gson.toJson(jsonarray);
@@ -321,8 +338,19 @@ public class GameProfileCache {
}
private Stream<GameProfileCache.GameProfileInfo> getTopMRUProfiles(int limit) {
- return ImmutableList.copyOf(this.profilesByUUID.values()).stream().sorted(Comparator.comparing(GameProfileCache.GameProfileInfo::getLastAccess).reversed()).limit((long) limit);
+ // Paper start - allow better concurrency
+ return this.listTopMRUProfiles(limit).stream();
+ }
+
+ private List<GameProfileCache.GameProfileInfo> listTopMRUProfiles(int limit) {
+ try {
+ this.stateLock.lock();
+ return this.profilesByUUID.values().stream().sorted(Comparator.comparing(GameProfileCache.GameProfileInfo::getLastAccess).reversed()).limit(limit).toList();
+ } finally {
+ this.stateLock.unlock();
+ }
}
+ // Paper end
private static JsonElement writeGameProfile(GameProfileCache.GameProfileInfo entry, DateFormat dateFormat) {
JsonObject jsonobject = new JsonObject();

View file

@ -0,0 +1,191 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Wed, 25 Aug 2021 20:17:12 -0700
Subject: [PATCH] Improve and expand AsyncCatcher
Log when the async catcher is tripped
The chunk system can swallow the exception given it's all
built with completablefuture, so ensure it is at least printed.
Add/move several async catchers
Async catch modifications to critical entity state
These used to be here from Spigot, but were dropped with 1.17.
Now in 1.17, this state is _even more_ critical than it was before,
so these must exist to catch stupid plugins.
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 711842dda404b5e0ecbb753ce08dfdf70b556c20..ef031bf9df047534e154fb3b288d76df3bed89cc 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -1571,6 +1571,7 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
public void internalTeleport(double d0, double d1, double d2, float f, float f1, Set<RelativeMovement> set) { // Paper
+ org.spigotmc.AsyncCatcher.catchOp("teleport"); // Paper
// Paper start
if (player.isRemoved()) {
LOGGER.info("Attempt to teleport removed player {} restricted", player.getScoreboardName());
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index b0e8a2f4a223cfe3652ad45c26bcf96f21bb7853..46e3f01c2158ec327a0daa232be1e7fb6d40a90e 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -1118,7 +1118,7 @@ public abstract class LivingEntity extends Entity implements Attackable {
}
public boolean addEffect(MobEffectInstance mobeffect, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause) {
- org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot
+ // org.spigotmc.AsyncCatcher.catchOp("effect add"); // Spigot // Paper - move to API
if (this.isTickingEffects) {
this.effectsToProcess.add(new ProcessableEffect(mobeffect, cause));
return true;
diff --git a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
index bbbf6dd8e566ecdca8794e3b03765fe7e426a2bd..66ab901956ca394c251c420338643d39817a1b7e 100644
--- a/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
+++ b/src/main/java/net/minecraft/world/level/entity/PersistentEntitySectionManager.java
@@ -77,6 +77,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private boolean addEntityUuid(T entity) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity add by UUID"); // Paper
if (!this.knownUuids.add(entity.getUUID())) {
PersistentEntitySectionManager.LOGGER.warn("UUID of added entity already exists: {}", entity);
return false;
@@ -90,6 +91,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private boolean addEntity(T entity, boolean existing) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity add"); // Paper
// Paper start - chunk system hooks
if (existing) {
// I don't want to know why this is a generic type.
@@ -145,19 +147,23 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
void startTicking(T entity) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity start ticking"); // Paper
this.callbacks.onTickingStart(entity);
}
void stopTicking(T entity) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity stop ticking"); // Paper
this.callbacks.onTickingEnd(entity);
}
void startTracking(T entity) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity start tracking"); // Paper
this.visibleEntityStorage.add(entity);
this.callbacks.onTrackingStart(entity);
}
void stopTracking(T entity) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity stop tracking"); // Paper
this.callbacks.onTrackingEnd(entity);
this.visibleEntityStorage.remove(entity);
}
@@ -169,6 +175,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void updateChunkStatus(ChunkPos chunkPos, Visibility trackingStatus) {
+ org.spigotmc.AsyncCatcher.catchOp("Update chunk status"); // Paper
long i = chunkPos.toLong();
if (trackingStatus == Visibility.HIDDEN) {
@@ -213,6 +220,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void ensureChunkQueuedForLoad(long chunkPos) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk save"); // Paper
PersistentEntitySectionManager.ChunkLoadStatus persistententitysectionmanager_b = (PersistentEntitySectionManager.ChunkLoadStatus) this.chunkLoadStatuses.get(chunkPos);
if (persistententitysectionmanager_b == PersistentEntitySectionManager.ChunkLoadStatus.FRESH) {
@@ -257,6 +265,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private void requestChunkLoad(long chunkPos) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk load request"); // Paper
this.chunkLoadStatuses.put(chunkPos, PersistentEntitySectionManager.ChunkLoadStatus.PENDING);
ChunkPos chunkcoordintpair = new ChunkPos(chunkPos);
CompletableFuture completablefuture = this.permanentStorage.loadEntities(chunkcoordintpair);
@@ -270,6 +279,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private boolean processChunkUnload(long chunkPos) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk unload process"); // Paper
boolean flag = this.storeChunkSections(chunkPos, (entityaccess) -> {
entityaccess.getPassengersAndSelf().forEach(this::unloadEntity);
}, true); // CraftBukkit - add boolean for event call
@@ -294,6 +304,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
private void processPendingLoads() {
+ org.spigotmc.AsyncCatcher.catchOp("Entity chunk process pending loads"); // Paper
ChunkEntities<T> chunkentities; // CraftBukkit - decompile error
while ((chunkentities = (ChunkEntities) this.loadingInbox.poll()) != null) {
@@ -310,6 +321,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void tick() {
+ org.spigotmc.AsyncCatcher.catchOp("Entity manager tick"); // Paper
this.processPendingLoads();
this.processUnloads();
}
@@ -330,6 +342,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void autoSave() {
+ org.spigotmc.AsyncCatcher.catchOp("Entity manager autosave"); // Paper
this.getAllChunksToSave().forEach((java.util.function.LongConsumer) (i) -> { // CraftBukkit - decompile error
boolean flag = this.chunkVisibility.get(i) == Visibility.HIDDEN;
@@ -344,6 +357,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
}
public void saveAll() {
+ org.spigotmc.AsyncCatcher.catchOp("Entity manager save"); // Paper
LongSet longset = this.getAllChunksToSave();
while (!longset.isEmpty()) {
@@ -451,6 +465,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
long i = SectionPos.asLong(blockposition);
if (i != this.currentSectionKey) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity move"); // Paper
Visibility visibility = this.currentSection.getStatus();
if (!this.currentSection.remove(this.entity)) {
@@ -505,6 +520,7 @@ public class PersistentEntitySectionManager<T extends EntityAccess> implements A
@Override
public void onRemove(Entity.RemovalReason reason) {
+ org.spigotmc.AsyncCatcher.catchOp("Entity remove"); // Paper
if (!this.currentSection.remove(this.entity)) {
PersistentEntitySectionManager.LOGGER.warn("Entity {} wasn't found in section {} (destroying due to {})", new Object[]{this.entity, SectionPos.of(this.currentSectionKey), reason});
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
index faf32ed224f5471d8994ad09e493c1fb724904a0..68d827b95a67f2fb65ed806221bb4e7e906a05f4 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java
@@ -465,6 +465,7 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity {
@Override
public boolean addPotionEffect(PotionEffect effect, boolean force) {
+ org.spigotmc.AsyncCatcher.catchOp("effect add"); // Paper
this.getHandle().addEffect(CraftPotionUtil.fromBukkit(effect), EntityPotionEffectEvent.Cause.PLUGIN); // Paper - Don't ignore icon
return true;
}
diff --git a/src/main/java/org/spigotmc/AsyncCatcher.java b/src/main/java/org/spigotmc/AsyncCatcher.java
index 05e94702e42b8f5c35d2a112c486d57948a3acba..d678ca116f9b81ab9d7d99a698e0ce066c132f34 100644
--- a/src/main/java/org/spigotmc/AsyncCatcher.java
+++ b/src/main/java/org/spigotmc/AsyncCatcher.java
@@ -11,6 +11,7 @@ public class AsyncCatcher
{
if ( !io.papermc.paper.util.TickThread.isTickThread() ) // Paper // Paper - rewrite chunk system
{
+ MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); // Paper
throw new IllegalStateException( "Asynchronous " + reason + "!" );
}
}

View file

@ -0,0 +1,341 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
Date: Mon, 16 Aug 2021 01:31:54 -0500
Subject: [PATCH] Add '/paper mobcaps' and '/paper playermobcaps'
Add commands to get the mobcaps for a world, as well as the mobcaps for
each player when per-player mob spawning is enabled.
Also has a hover text on each mob category listing what entity types are
in said category
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
index a8b41d6a42e23a7cd839cc3d5e5cae84860f802c..b7fc337ab2bbe3c6d80ed70834e294ab3a7f9dc2 100644
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
@@ -42,6 +42,7 @@ public final class PaperCommand extends Command {
commands.put(Set.of("debug", "chunkinfo", "holderinfo"), new ChunkDebugCommand());
commands.put(Set.of("syncloadinfo"), new SyncLoadInfoCommand());
commands.put(Set.of("dumpitem"), new DumpItemCommand());
+ commands.put(Set.of("mobcaps", "playermobcaps"), new MobcapsCommand());
return commands.entrySet().stream()
.flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue())))
diff --git a/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java b/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..d3b39d88a72ca25057fd8574d32f28db0d420818
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java
@@ -0,0 +1,229 @@
+package io.papermc.paper.command.subcommands;
+
+import com.google.common.collect.ImmutableMap;
+import io.papermc.paper.command.CommandUtil;
+import io.papermc.paper.command.PaperSubcommand;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.ToIntFunction;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.ComponentLike;
+import net.kyori.adventure.text.JoinConfiguration;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.TextColor;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.entity.MobCategory;
+import net.minecraft.world.level.NaturalSpawner;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.command.CommandSender;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public final class MobcapsCommand implements PaperSubcommand {
+ static final Map<MobCategory, TextColor> MOB_CATEGORY_COLORS = ImmutableMap.<MobCategory, TextColor>builder()
+ .put(MobCategory.MONSTER, NamedTextColor.RED)
+ .put(MobCategory.CREATURE, NamedTextColor.GREEN)
+ .put(MobCategory.AMBIENT, NamedTextColor.GRAY)
+ .put(MobCategory.AXOLOTLS, TextColor.color(0x7324FF))
+ .put(MobCategory.UNDERGROUND_WATER_CREATURE, TextColor.color(0x3541E6))
+ .put(MobCategory.WATER_CREATURE, TextColor.color(0x006EFF))
+ .put(MobCategory.WATER_AMBIENT, TextColor.color(0x00B3FF))
+ .put(MobCategory.MISC, TextColor.color(0x636363))
+ .build();
+
+ @Override
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
+ switch (subCommand) {
+ case "mobcaps" -> this.printMobcaps(sender, args);
+ case "playermobcaps" -> this.printPlayerMobcaps(sender, args);
+ }
+ return true;
+ }
+
+ @Override
+ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
+ return switch (subCommand) {
+ case "mobcaps" -> CommandUtil.getListMatchingLast(sender, args, this.suggestMobcaps(args));
+ case "playermobcaps" -> CommandUtil.getListMatchingLast(sender, args, this.suggestPlayerMobcaps(sender, args));
+ default -> throw new IllegalArgumentException();
+ };
+ }
+
+ private List<String> suggestMobcaps(final String[] args) {
+ if (args.length == 1) {
+ final List<String> worlds = new ArrayList<>(Bukkit.getWorlds().stream().map(World::getName).toList());
+ worlds.add("*");
+ return worlds;
+ }
+
+ return Collections.emptyList();
+ }
+
+ private List<String> suggestPlayerMobcaps(final CommandSender sender, final String[] args) {
+ if (args.length == 1) {
+ final List<String> list = new ArrayList<>();
+ for (final Player player : Bukkit.getOnlinePlayers()) {
+ if (!(sender instanceof Player senderPlayer) || senderPlayer.canSee(player)) {
+ list.add(player.getName());
+ }
+ }
+ return list;
+ }
+
+ return Collections.emptyList();
+ }
+
+ private void printMobcaps(final CommandSender sender, final String[] args) {
+ final List<World> worlds;
+ if (args.length == 0) {
+ if (sender instanceof Player player) {
+ worlds = List.of(player.getWorld());
+ } else {
+ sender.sendMessage(Component.text("Must specify a world! ex: '/paper mobcaps world'", NamedTextColor.RED));
+ return;
+ }
+ } else if (args.length == 1) {
+ final String input = args[0];
+ if (input.equals("*")) {
+ worlds = Bukkit.getWorlds();
+ } else {
+ final @Nullable World world = Bukkit.getWorld(input);
+ if (world == null) {
+ sender.sendMessage(Component.text("'" + input + "' is not a valid world!", NamedTextColor.RED));
+ return;
+ } else {
+ worlds = List.of(world);
+ }
+ }
+ } else {
+ sender.sendMessage(Component.text("Too many arguments!", NamedTextColor.RED));
+ return;
+ }
+
+ for (final World world : worlds) {
+ final ServerLevel level = ((CraftWorld) world).getHandle();
+ final NaturalSpawner.@Nullable SpawnState state = level.getChunkSource().getLastSpawnState();
+
+ final int chunks;
+ if (state == null) {
+ chunks = 0;
+ } else {
+ chunks = state.getSpawnableChunkCount();
+ }
+ sender.sendMessage(Component.join(JoinConfiguration.noSeparators(),
+ Component.text("Mobcaps for world: "),
+ Component.text(world.getName(), NamedTextColor.AQUA),
+ Component.text(" (" + chunks + " spawnable chunks)")
+ ));
+
+ sender.sendMessage(createMobcapsComponent(
+ category -> {
+ if (state == null) {
+ return 0;
+ } else {
+ return state.getMobCategoryCounts().getOrDefault(category, 0);
+ }
+ },
+ category -> NaturalSpawner.globalLimitForCategory(level, category, chunks)
+ ));
+ }
+ }
+
+ private void printPlayerMobcaps(final CommandSender sender, final String[] args) {
+ final @Nullable Player player;
+ if (args.length == 0) {
+ if (sender instanceof Player pl) {
+ player = pl;
+ } else {
+ sender.sendMessage(Component.text("Must specify a player! ex: '/paper playermobcount playerName'", NamedTextColor.RED));
+ return;
+ }
+ } else if (args.length == 1) {
+ final String input = args[0];
+ player = Bukkit.getPlayerExact(input);
+ if (player == null) {
+ sender.sendMessage(Component.text("Could not find player named '" + input + "'", NamedTextColor.RED));
+ return;
+ }
+ } else {
+ sender.sendMessage(Component.text("Too many arguments!", NamedTextColor.RED));
+ return;
+ }
+
+ final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
+ final ServerLevel level = serverPlayer.serverLevel();
+
+ if (!level.paperConfig().entities.spawning.perPlayerMobSpawns) {
+ sender.sendMessage(Component.text("Use '/paper mobcaps' for worlds where per-player mob spawning is disabled.", NamedTextColor.RED));
+ return;
+ }
+
+ sender.sendMessage(Component.join(JoinConfiguration.noSeparators(), Component.text("Mobcaps for player: "), Component.text(player.getName(), NamedTextColor.GREEN)));
+ sender.sendMessage(createMobcapsComponent(
+ category -> level.chunkSource.chunkMap.getMobCountNear(serverPlayer, category),
+ category -> level.getWorld().getSpawnLimitUnsafe(org.bukkit.craftbukkit.util.CraftSpawnCategory.toBukkit(category))
+ ));
+ }
+
+ private static Component createMobcapsComponent(final ToIntFunction<MobCategory> countGetter, final ToIntFunction<MobCategory> limitGetter) {
+ return MOB_CATEGORY_COLORS.entrySet().stream()
+ .map(entry -> {
+ final MobCategory category = entry.getKey();
+ final TextColor color = entry.getValue();
+
+ final Component categoryHover = Component.join(JoinConfiguration.noSeparators(),
+ Component.text("Entity types in category ", TextColor.color(0xE0E0E0)),
+ Component.text(category.getName(), color),
+ Component.text(':', NamedTextColor.GRAY),
+ Component.newline(),
+ Component.newline(),
+ BuiltInRegistries.ENTITY_TYPE.entrySet().stream()
+ .filter(it -> it.getValue().getCategory() == category)
+ .map(it -> Component.translatable(it.getValue().getDescriptionId()))
+ .collect(Component.toComponent(Component.text(", ", NamedTextColor.GRAY)))
+ );
+
+ final Component categoryComponent = Component.text()
+ .content(" " + category.getName())
+ .color(color)
+ .hoverEvent(categoryHover)
+ .build();
+
+ final TextComponent.Builder builder = Component.text()
+ .append(
+ categoryComponent,
+ Component.text(": ", NamedTextColor.GRAY)
+ );
+ final int limit = limitGetter.applyAsInt(category);
+ if (limit != -1) {
+ builder.append(
+ Component.text(countGetter.applyAsInt(category)),
+ Component.text("/", NamedTextColor.GRAY),
+ Component.text(limit)
+ );
+ } else {
+ builder.append(Component.text()
+ .append(
+ Component.text('n'),
+ Component.text("/", NamedTextColor.GRAY),
+ Component.text('a')
+ )
+ .hoverEvent(Component.text("This category does not naturally spawn.")));
+ }
+ return builder;
+ })
+ .map(ComponentLike::asComponent)
+ .collect(Component.toComponent(Component.newline()));
+ }
+}
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
index 9df761f5cf043e8d2dffa711c20ab32fe2992331..d08c7b0b52065980f1f13c5533ff6355028322db 100644
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
+++ b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
@@ -190,6 +190,16 @@ public final class NaturalSpawner {
world.getProfiler().pop();
}
+ // Paper start
+ public static int globalLimitForCategory(final ServerLevel level, final MobCategory category, final int spawnableChunkCount) {
+ final int categoryLimit = level.getWorld().getSpawnLimitUnsafe(CraftSpawnCategory.toBukkit(category));
+ if (categoryLimit < 1) {
+ return categoryLimit;
+ }
+ return categoryLimit * spawnableChunkCount / NaturalSpawner.MAGIC_NUMBER;
+ }
+ // Paper end
+
public static void spawnCategoryForChunk(MobCategory group, ServerLevel world, LevelChunk chunk, NaturalSpawner.SpawnPredicate checker, NaturalSpawner.AfterSpawnCallback runner) {
// Paper start - add parameters and int ret type
spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 0b422ba3241b31ad9e4f227bcb5033df2545d84b..dcb3bc211b834aa6fd4cb19197debc93df3f4839 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -2279,6 +2279,11 @@ public final class CraftServer implements Server {
@Override
public int getSpawnLimit(SpawnCategory spawnCategory) {
+ // Paper start
+ return this.getSpawnLimitUnsafe(spawnCategory);
+ }
+ public int getSpawnLimitUnsafe(final SpawnCategory spawnCategory) {
+ // Paper end
return this.spawnCategoryLimit.getOrDefault(spawnCategory, -1);
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 19db9c2447ffc2321e2f8a3b86f561d29b9eb6de..3a5843b50a5cc5110bf484f9697674e5bfc9699f 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -1713,9 +1713,14 @@ public class CraftWorld extends CraftRegionAccessor implements World {
Preconditions.checkArgument(spawnCategory != null, "SpawnCategory cannot be null");
Preconditions.checkArgument(CraftSpawnCategory.isValidForLimits(spawnCategory), "SpawnCategory.%s are not supported", spawnCategory);
+ // Paper start
+ return this.getSpawnLimitUnsafe(spawnCategory);
+ }
+ public final int getSpawnLimitUnsafe(final SpawnCategory spawnCategory) {
int limit = this.spawnCategoryLimit.getOrDefault(spawnCategory, -1);
if (limit < 0) {
- limit = this.server.getSpawnLimit(spawnCategory);
+ limit = this.server.getSpawnLimitUnsafe(spawnCategory);
+ // Paper end
}
return limit;
}
diff --git a/src/test/java/io/papermc/paper/command/subcommands/MobcapsCommandTest.java b/src/test/java/io/papermc/paper/command/subcommands/MobcapsCommandTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd238eacee24ebf0d0ce82b96107e093ca4866b0
--- /dev/null
+++ b/src/test/java/io/papermc/paper/command/subcommands/MobcapsCommandTest.java
@@ -0,0 +1,20 @@
+package io.papermc.paper.command.subcommands;
+
+import java.util.HashSet;
+import java.util.Set;
+import net.minecraft.world.entity.MobCategory;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class MobcapsCommandTest {
+ @Test
+ public void testMobCategoryColors() {
+ final Set<String> missing = new HashSet<>();
+ for (final MobCategory value : MobCategory.values()) {
+ if (!MobcapsCommand.MOB_CATEGORY_COLORS.containsKey(value)) {
+ missing.add(value.getName());
+ }
+ }
+ Assertions.assertTrue(missing.isEmpty(), "MobcapsCommand.MOB_CATEGORY_COLORS map missing TextColors for [" + String.join(", ", missing + "]"));
+ }
+}

View file

@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Thu, 26 Aug 2021 12:09:47 +0200
Subject: [PATCH] Sanitize ResourceLocation error logging
diff --git a/src/main/java/net/minecraft/resources/ResourceLocation.java b/src/main/java/net/minecraft/resources/ResourceLocation.java
index d3300e43523429fdd0e61c5aa9aed2e6bc78e07e..38e2a8cec48bc779b8154d6d719031f457a2403e 100644
--- a/src/main/java/net/minecraft/resources/ResourceLocation.java
+++ b/src/main/java/net/minecraft/resources/ResourceLocation.java
@@ -211,7 +211,7 @@ public class ResourceLocation implements Comparable<ResourceLocation> {
private static String assertValidNamespace(String namespace, String path) {
if (!isValidNamespace(namespace)) {
- throw new ResourceLocationException("Non [a-z0-9_.-] character in namespace of location: " + namespace + ":" + path);
+ throw new ResourceLocationException("Non [a-z0-9_.-] character in namespace of location: " + org.apache.commons.lang3.StringUtils.normalizeSpace(namespace) + ":" + org.apache.commons.lang3.StringUtils.normalizeSpace(path)); // Paper
} else {
return namespace;
}
@@ -232,7 +232,7 @@ public class ResourceLocation implements Comparable<ResourceLocation> {
private static String assertValidPath(String namespace, String path) {
if (!isValidPath(path)) {
- throw new ResourceLocationException("Non [a-z0-9/._-] character in path of location: " + namespace + ":" + path);
+ throw new ResourceLocationException("Non [a-z0-9/._-] character in path of location: " + namespace + ":" + org.apache.commons.lang3.StringUtils.normalizeSpace(path)); // Paper
} else {
return path;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,343 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Thu, 11 Mar 2021 20:05:44 -0800
Subject: [PATCH] Custom table implementation for blockstate state lookups
Testing some redstone intensive machines showed to bring about a 10%
improvement.
diff --git a/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java b/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java
new file mode 100644
index 0000000000000000000000000000000000000000..57d0cd3ad6f972e986c72a57f1a6e36003f190c2
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/table/ZeroCollidingReferenceStateTable.java
@@ -0,0 +1,160 @@
+package io.papermc.paper.util.table;
+
+import com.google.common.collect.Table;
+import net.minecraft.world.level.block.state.StateHolder;
+import net.minecraft.world.level.block.state.properties.Property;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public final class ZeroCollidingReferenceStateTable {
+
+ // upper 32 bits: starting index
+ // lower 32 bits: bitset for contained ids
+ protected final long[] this_index_table;
+ protected final Comparable<?>[] this_table;
+ protected final StateHolder<?, ?> this_state;
+
+ protected long[] index_table;
+ protected StateHolder<?, ?>[][] value_table;
+
+ public ZeroCollidingReferenceStateTable(final StateHolder<?, ?> state, final Map<Property<?>, Comparable<?>> this_map) {
+ this.this_state = state;
+ this.this_index_table = this.create_table(this_map.keySet());
+
+ int max_id = -1;
+ for (final Property<?> property : this_map.keySet()) {
+ final int id = lookup_vindex(property, this.this_index_table);
+ if (id > max_id) {
+ max_id = id;
+ }
+ }
+
+ this.this_table = new Comparable[max_id + 1];
+ for (final Map.Entry<Property<?>, Comparable<?>> entry : this_map.entrySet()) {
+ this.this_table[lookup_vindex(entry.getKey(), this.this_index_table)] = entry.getValue();
+ }
+ }
+
+ public void loadInTable(final Table<Property<?>, Comparable<?>, StateHolder<?, ?>> table,
+ final Map<Property<?>, Comparable<?>> this_map) {
+ final Set<Property<?>> combined = new HashSet<>(table.rowKeySet());
+ combined.addAll(this_map.keySet());
+
+ this.index_table = this.create_table(combined);
+
+ int max_id = -1;
+ for (final Property<?> property : combined) {
+ final int id = lookup_vindex(property, this.index_table);
+ if (id > max_id) {
+ max_id = id;
+ }
+ }
+
+ this.value_table = new StateHolder[max_id + 1][];
+
+ final Map<Property<?>, Map<Comparable<?>, StateHolder<?, ?>>> map = table.rowMap();
+ for (final Property<?> property : map.keySet()) {
+ final Map<Comparable<?>, StateHolder<?, ?>> propertyMap = map.get(property);
+
+ final int id = lookup_vindex(property, this.index_table);
+ final StateHolder<?, ?>[] states = this.value_table[id] = new StateHolder[property.getPossibleValues().size()];
+
+ for (final Map.Entry<Comparable<?>, StateHolder<?, ?>> entry : propertyMap.entrySet()) {
+ if (entry.getValue() == null) {
+ // TODO what
+ continue;
+ }
+
+ states[((Property)property).getIdFor(entry.getKey())] = entry.getValue();
+ }
+ }
+
+
+ for (final Map.Entry<Property<?>, Comparable<?>> entry : this_map.entrySet()) {
+ final Property<?> property = entry.getKey();
+ final int index = lookup_vindex(property, this.index_table);
+
+ if (this.value_table[index] == null) {
+ this.value_table[index] = new StateHolder[property.getPossibleValues().size()];
+ }
+
+ this.value_table[index][((Property)property).getIdFor(entry.getValue())] = this.this_state;
+ }
+ }
+
+
+ protected long[] create_table(final Collection<Property<?>> collection) {
+ int max_id = -1;
+ for (final Property<?> property : collection) {
+ final int id = property.getId();
+ if (id > max_id) {
+ max_id = id;
+ }
+ }
+
+ final long[] ret = new long[((max_id + 1) + 31) >>> 5]; // ceil((max_id + 1) / 32)
+
+ for (final Property<?> property : collection) {
+ final int id = property.getId();
+
+ ret[id >>> 5] |= (1L << (id & 31));
+ }
+
+ int total = 0;
+ for (int i = 1, len = ret.length; i < len; ++i) {
+ ret[i] |= (long)(total += Long.bitCount(ret[i - 1] & 0xFFFFFFFFL)) << 32;
+ }
+
+ return ret;
+ }
+
+ public Comparable<?> get(final Property<?> state) {
+ final Comparable<?>[] table = this.this_table;
+ final int index = lookup_vindex(state, this.this_index_table);
+
+ if (index < 0 || index >= table.length) {
+ return null;
+ }
+ return table[index];
+ }
+
+ public StateHolder<?, ?> get(final Property<?> property, final Comparable<?> with) {
+ final int withId = ((Property)property).getIdFor(with);
+ if (withId < 0) {
+ return null;
+ }
+
+ final int index = lookup_vindex(property, this.index_table);
+ final StateHolder<?, ?>[][] table = this.value_table;
+ if (index < 0 || index >= table.length) {
+ return null;
+ }
+
+ final StateHolder<?, ?>[] values = table[index];
+
+ if (withId >= values.length) {
+ return null;
+ }
+
+ return values[withId];
+ }
+
+ protected static int lookup_vindex(final Property<?> property, final long[] index_table) {
+ final int id = property.getId();
+ final long bitset_mask = (1L << (id & 31));
+ final long lower_mask = bitset_mask - 1;
+ final int index = id >>> 5;
+ if (index >= index_table.length) {
+ return -1;
+ }
+ final long index_value = index_table[index];
+ final long contains_check = ((index_value & bitset_mask) - 1) >> (Long.SIZE - 1); // -1L if doesn't contain
+
+ // index = total bits set in lower table values (upper 32 bits of index_value) plus total bits set in lower indices below id
+ // contains_check is 0 if the bitset had id set, else it's -1: so index is unaffected if contains_check == 0,
+ // otherwise it comes out as -1.
+ return (int)(((index_value >>> 32) + Long.bitCount(index_value & lower_mask)) | contains_check);
+ }
+}
diff --git a/src/main/java/net/minecraft/world/level/block/state/StateHolder.java b/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
index 89ba12d44865ecddebabbc39c769af4cd8c82702..5f285d190186a2ff5a61d05070593e1d633dd79a 100644
--- a/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
+++ b/src/main/java/net/minecraft/world/level/block/state/StateHolder.java
@@ -39,11 +39,13 @@ public abstract class StateHolder<O, S> {
private final ImmutableMap<Property<?>, Comparable<?>> values;
private Table<Property<?>, Comparable<?>, S> neighbours;
protected final MapCodec<S> propertiesCodec;
+ protected final io.papermc.paper.util.table.ZeroCollidingReferenceStateTable optimisedTable; // Paper - optimise state lookup
protected StateHolder(O owner, ImmutableMap<Property<?>, Comparable<?>> entries, MapCodec<S> codec) {
this.owner = owner;
this.values = entries;
this.propertiesCodec = codec;
+ this.optimisedTable = new io.papermc.paper.util.table.ZeroCollidingReferenceStateTable(this, entries); // Paper - optimise state lookup
}
public <T extends Comparable<T>> S cycle(Property<T> property) {
@@ -84,11 +86,11 @@ public abstract class StateHolder<O, S> {
}
public <T extends Comparable<T>> boolean hasProperty(Property<T> property) {
- return this.values.containsKey(property);
+ return this.optimisedTable.get(property) != null; // Paper - optimise state lookup
}
public <T extends Comparable<T>> T getValue(Property<T> property) {
- Comparable<?> comparable = this.values.get(property);
+ Comparable<?> comparable = this.optimisedTable.get(property); // Paper - optimise state lookup
if (comparable == null) {
throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner);
} else {
@@ -97,24 +99,18 @@ public abstract class StateHolder<O, S> {
}
public <T extends Comparable<T>> Optional<T> getOptionalValue(Property<T> property) {
- Comparable<?> comparable = this.values.get(property);
+ Comparable<?> comparable = this.optimisedTable.get(property); // Paper - optimise state lookup
return comparable == null ? Optional.empty() : Optional.of(property.getValueClass().cast(comparable));
}
public <T extends Comparable<T>, V extends T> S setValue(Property<T> property, V value) {
- Comparable<?> comparable = this.values.get(property);
- if (comparable == null) {
- throw new IllegalArgumentException("Cannot set property " + property + " as it does not exist in " + this.owner);
- } else if (comparable.equals(value)) {
- return (S)this;
- } else {
- S object = this.neighbours.get(property, value);
- if (object == null) {
- throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner + ", it is not an allowed value");
- } else {
- return object;
- }
+ // Paper start - optimise state lookup
+ final S ret = (S)this.optimisedTable.get(property, value);
+ if (ret == null) {
+ throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.owner + ", it is not an allowed value");
}
+ return ret;
+ // Paper end - optimise state lookup
}
public <T extends Comparable<T>, V extends T> S trySetValue(Property<T> property, V value) {
@@ -147,7 +143,7 @@ public abstract class StateHolder<O, S> {
}
}
- this.neighbours = (Table<Property<?>, Comparable<?>, S>)(table.isEmpty() ? table : ArrayTable.create(table));
+ this.neighbours = (Table<Property<?>, Comparable<?>, S>)(table.isEmpty() ? table : ArrayTable.create(table)); this.optimisedTable.loadInTable((Table)this.neighbours, this.values); // Paper - optimise state lookup
}
}
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
index bf7ed22094ac92a152e522bafccffb9589f84343..591ee974391c465be0e523e2da0f176436a7d567 100644
--- a/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/BooleanProperty.java
@@ -7,6 +7,13 @@ import java.util.Optional;
public class BooleanProperty extends Property<Boolean> {
private final ImmutableSet<Boolean> values = ImmutableSet.of(true, false);
+ // Paper start - optimise iblockdata state lookup
+ @Override
+ public final int getIdFor(final Boolean value) {
+ return value.booleanValue() ? 1 : 0;
+ }
+ // Paper end - optimise iblockdata state lookup
+
protected BooleanProperty(String name) {
super(name, Boolean.class);
}
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
index 2d69d1c17f734ee38667d51e6fd0a268211ec595..034b2618aa178c003c34eb621c6beb7c30958a38 100644
--- a/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/EnumProperty.java
@@ -15,6 +15,15 @@ public class EnumProperty<T extends Enum<T> & StringRepresentable> extends Prope
private final ImmutableSet<T> values;
private final Map<String, T> names = Maps.newHashMap();
+ // Paper start - optimise iblockdata state lookup
+ private int[] idLookupTable;
+
+ @Override
+ public final int getIdFor(final T value) {
+ return this.idLookupTable[value.ordinal()];
+ }
+ // Paper end - optimise iblockdata state lookup
+
protected EnumProperty(String name, Class<T> type, Collection<T> values) {
super(name, type);
this.values = ImmutableSet.copyOf(values);
@@ -28,6 +37,14 @@ public class EnumProperty<T extends Enum<T> & StringRepresentable> extends Prope
this.names.put(string, enum_);
}
+ // Paper start - optimise iblockdata state lookup
+ int id = 0;
+ this.idLookupTable = new int[type.getEnumConstants().length];
+ java.util.Arrays.fill(this.idLookupTable, -1);
+ for (final T value : this.getPossibleValues()) {
+ this.idLookupTable[value.ordinal()] = id++;
+ }
+ // Paper end - optimise iblockdata state lookup
}
@Override
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java b/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
index 33268d953b30d384564eee6dfab2a37fa722e465..dfeec6c580963e4a6f907e1344e2265d39326027 100644
--- a/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/IntegerProperty.java
@@ -11,6 +11,16 @@ public class IntegerProperty extends Property<Integer> {
public final int min;
public final int max;
+ // Paper start - optimise iblockdata state lookup
+ @Override
+ public final int getIdFor(final Integer value) {
+ final int val = value.intValue();
+ final int ret = val - this.min;
+
+ return ret | ((this.max - ret) >> 31);
+ }
+ // Paper end - optimise iblockdata state lookup
+
protected IntegerProperty(String name, int min, int max) {
super(name, Integer.class);
if (min < 0) {
diff --git a/src/main/java/net/minecraft/world/level/block/state/properties/Property.java b/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
index 66b8e23d799adaf872233ea44c54330d75135544..9d4faa3da8d62028074ce25f4a728e9ba5281428 100644
--- a/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
+++ b/src/main/java/net/minecraft/world/level/block/state/properties/Property.java
@@ -24,6 +24,17 @@ public abstract class Property<T extends Comparable<T>> {
}, this::getName);
private final Codec<Property.Value<T>> valueCodec = this.codec.xmap(this::value, Property.Value::value);
+ // Paper start - optimise iblockdata state lookup
+ private static final java.util.concurrent.atomic.AtomicInteger ID_GENERATOR = new java.util.concurrent.atomic.AtomicInteger();
+ private final int id = ID_GENERATOR.getAndIncrement();
+
+ public final int getId() {
+ return this.id;
+ }
+
+ public abstract int getIdFor(final T value);
+ // Paper end - optimise state lookup
+
protected Property(String name, Class<T> type) {
this.clazz = type;
this.name = name;

View file

@ -0,0 +1,63 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Mon, 6 Jul 2020 22:48:48 -0700
Subject: [PATCH] Manually inline methods in BlockPosition
diff --git a/src/main/java/net/minecraft/core/BlockPos.java b/src/main/java/net/minecraft/core/BlockPos.java
index 441ea6b9fd55a5288f264472d7297728d0546d6b..83cab746d1d6fe25c043c8aee28c39412b90c127 100644
--- a/src/main/java/net/minecraft/core/BlockPos.java
+++ b/src/main/java/net/minecraft/core/BlockPos.java
@@ -515,9 +515,9 @@ public class BlockPos extends Vec3i {
}
public BlockPos.MutableBlockPos set(int x, int y, int z) {
- this.setX(x);
- this.setY(y);
- this.setZ(z);
+ this.x = x; // Paper - force inline
+ this.y = y; // Paper - force inline
+ this.z = z; // Paper - force inline
return this;
}
@@ -581,19 +581,19 @@ public class BlockPos extends Vec3i {
// Paper start - comment out useless overrides @Override - TODO figure out why this is suddenly important to keep
@Override
public BlockPos.MutableBlockPos setX(int i) {
- super.setX(i);
+ this.x = i; // Paper
return this;
}
@Override
public BlockPos.MutableBlockPos setY(int i) {
- super.setY(i);
+ this.y = i; // Paper
return this;
}
@Override
public BlockPos.MutableBlockPos setZ(int i) {
- super.setZ(i);
+ this.z = i; // Paper
return this;
}
// Paper end
diff --git a/src/main/java/net/minecraft/core/Vec3i.java b/src/main/java/net/minecraft/core/Vec3i.java
index e87ef99260bff134529e00b9a75381cecaec01a4..74a3f2ba6aaec39ba4721fb546bfccb325c86343 100644
--- a/src/main/java/net/minecraft/core/Vec3i.java
+++ b/src/main/java/net/minecraft/core/Vec3i.java
@@ -19,9 +19,9 @@ public class Vec3i implements Comparable<Vec3i> {
return IntStream.of(vec.getX(), vec.getY(), vec.getZ());
});
public static final Vec3i ZERO = new Vec3i(0, 0, 0);
- private int x;
- private int y;
- private int z;
+ protected int x; // Paper - protected
+ protected int y; // Paper - protected
+ protected int z; // Paper - protected
public static Codec<Vec3i> offsetCodec(int maxAbsValue) {
return ExtraCodecs.validate(CODEC, (vec) -> {

View file

@ -0,0 +1,33 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sun, 19 Jul 2020 15:17:01 -0700
Subject: [PATCH] Name craft scheduler threads according to the plugin using
them
Provides quick access to culprits running far more threads than
they should be
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
index 365b7e7c665bec357fb76d1479bf17da6f603590..b17d40ced3f2d5859c61f1826ead72c5f07abe9f 100644
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncTask.java
@@ -25,7 +25,10 @@ class CraftAsyncTask extends CraftTask {
@Override
public void run() {
final Thread thread = Thread.currentThread();
- synchronized (this.workers) {
+ // Paper start - name threads according to running plugin
+ final String nameBefore = thread.getName();
+ thread.setName(nameBefore + " - " + this.getOwner().getName());
+ try { synchronized (this.workers) { // Paper end - name threads according to running plugin
if (this.getPeriod() == CraftTask.CANCEL) {
// Never continue running after cancelled.
// Checking this with the lock is important!
@@ -92,6 +95,7 @@ class CraftAsyncTask extends CraftTask {
}
}
}
+ } finally { thread.setName(nameBefore); } // Paper - name worker thread according
}
LinkedList<BukkitWorker> getWorkers() {

View file

@ -0,0 +1,34 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sun, 20 Sep 2020 16:10:49 -0700
Subject: [PATCH] Make sure inlined getChunkAt has inlined logic for loaded
chunks
Tux did some profiling some time ago and showed that the
previous getChunkAt method which had inlined logic for loaded
chunks did get inlined, but the standard CPS.getChunkAt
method was not inlined.
Paper recently reverted this optimisation, so it's been reintroduced
here.
diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java
index 0e679bbbc000c32b840e5f3155a3c275216b20b5..58394bbf228f96f717e5318cd016caf9d2f09343 100644
--- a/src/main/java/net/minecraft/world/level/Level.java
+++ b/src/main/java/net/minecraft/world/level/Level.java
@@ -458,6 +458,15 @@ public abstract class Level implements LevelAccessor, AutoCloseable {
@Override
public final LevelChunk getChunk(int chunkX, int chunkZ) { // Paper - final to help inline
+ // Paper start - make sure loaded chunks get the inlined variant of this function
+ net.minecraft.server.level.ServerChunkCache cps = ((ServerLevel)this).getChunkSource();
+ if (cps.mainThread == Thread.currentThread()) {
+ LevelChunk ifLoaded = cps.getChunkAtIfLoadedMainThread(chunkX, chunkZ);
+ if (ifLoaded != null) {
+ return ifLoaded;
+ }
+ }
+ // Paper end - make sure loaded chunks get the inlined variant of this function
return (LevelChunk) this.getChunk(chunkX, chunkZ, ChunkStatus.FULL, true); // Paper - avoid a method jump
}

View file

@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Sun, 11 Apr 2021 02:58:48 -0700
Subject: [PATCH] Don't read neighbour chunk data off disk when converting
chunks
Lighting is purged on update anyways, so let's not add more
into the conversion process
diff --git a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
index e4b3a70ff9f906a10f2ba3c07642193ca3269db7..dfeda27add86be0d56ad023f7391fa21e36c5062 100644
--- a/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
+++ b/src/main/java/net/minecraft/world/level/chunk/storage/ChunkStorage.java
@@ -51,6 +51,7 @@ public class ChunkStorage implements AutoCloseable {
// CraftBukkit start
private boolean check(ServerChunkCache cps, int x, int z) {
+ if (true) return true; // Paper - this isn't even needed anymore, light is purged updating to 1.14+, why are we holding up the conversion process reading chunk data off disk - return true, we need to set light populated to true so the converter recognizes the chunk as being "full"
ChunkPos pos = new ChunkPos(x, z);
if (cps != null) {
//com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread"); // Paper - this function is now MT-Safe

View file

@ -0,0 +1,20 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Fri, 28 Aug 2020 12:33:47 -0700
Subject: [PATCH] Don't lookup fluid state when raytracing
Just use the iblockdata already retrieved, removes a getType call.
diff --git a/src/main/java/net/minecraft/world/level/BlockGetter.java b/src/main/java/net/minecraft/world/level/BlockGetter.java
index 24073d4d7c45d501c2baec2d8c8b49f60fb76e65..bd3eb39981c1d6028804f05c21eec6127389ca40 100644
--- a/src/main/java/net/minecraft/world/level/BlockGetter.java
+++ b/src/main/java/net/minecraft/world/level/BlockGetter.java
@@ -92,7 +92,7 @@ public interface BlockGetter extends LevelHeightAccessor {
return BlockHitResult.miss(raytrace1.getTo(), Direction.getNearest(vec3d.x, vec3d.y, vec3d.z), BlockPos.containing(raytrace1.getTo()));
}
// Paper end
- FluidState fluid = this.getFluidState(blockposition);
+ FluidState fluid = iblockdata.getFluidState(); // Paper - don't need to go to world state again
Vec3 vec3d = raytrace1.getFrom();
Vec3 vec3d1 = raytrace1.getTo();
VoxelShape voxelshape = raytrace1.getBlockShape(iblockdata, this, blockposition);