Some fixes, start updating CustomModelData API

This commit is contained in:
Nassim Jahnke
2024-12-03 20:58:25 +01:00
parent 2c2dadf75b
commit a894c3a437
28 changed files with 56 additions and 16 deletions

View File

@@ -1,53 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Doc <nachito94@msn.com>
Date: Sun, 6 Oct 2024 16:46:36 -0300
Subject: [PATCH] Add AnvilView#bypassEnchantmentLevelRestriction
Anvils, by default, limit applied enchantment levels to their respective
maximum level. The added API enables plugins to disable this behaviour,
allowing enchantments that are overleveled to be applied via the anvil.
diff --git a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/AnvilMenu.java
@@ -0,0 +0,0 @@ public class AnvilMenu extends ItemCombinerMenu {
public int maximumRepairCost = 40;
private CraftAnvilView bukkitEntity;
// CraftBukkit end
+ public boolean bypassEnchantmentLevelRestriction = false; // Paper - bypass anvil level restrictions
public AnvilMenu(int syncId, Inventory inventory) {
this(syncId, inventory, ContainerLevelAccess.NULL);
@@ -0,0 +0,0 @@ public class AnvilMenu extends ItemCombinerMenu {
flag2 = true;
} else {
flag1 = true;
- if (i2 > enchantment.getMaxLevel()) {
+ if (i2 > enchantment.getMaxLevel() && !this.bypassEnchantmentLevelRestriction) { // Paper - bypass anvil level restrictions
i2 = enchantment.getMaxLevel();
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftAnvilView.java
@@ -0,0 +0,0 @@ public class CraftAnvilView extends CraftInventoryView<AnvilMenu, AnvilInventory
this.container.maximumRepairCost = cost;
}
+ // Paper start
+ @Override
+ public boolean bypassesEnchantmentLevelRestriction() {
+ return this.container.bypassEnchantmentLevelRestriction;
+ }
+
+ @Override
+ public void bypassEnchantmentLevelRestriction(final boolean bypassEnchantmentLevelRestriction) {
+ this.container.bypassEnchantmentLevelRestriction = bypassEnchantmentLevelRestriction;
+ }
+ // Paper end
+
public void updateFromLegacy(CraftInventoryAnvil legacy) {
if (legacy.isRepairCostSet()) {
this.setRepairCost(legacy.getRepairCost());

View File

@@ -1,430 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Wed, 29 May 2024 19:50:21 -0700
Subject: [PATCH] Add FeatureFlag API
diff --git a/src/main/java/io/papermc/paper/world/flag/PaperFeatureDependent.java b/src/main/java/io/papermc/paper/world/flag/PaperFeatureDependent.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/flag/PaperFeatureDependent.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.world.flag;
+
+import java.util.Set;
+import net.minecraft.world.flag.FeatureElement;
+import org.bukkit.FeatureFlag;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.jetbrains.annotations.Unmodifiable;
+
+public interface PaperFeatureDependent extends FeatureDependant {
+
+ <M extends FeatureElement> M getHandle();
+
+ @Override
+ default @Unmodifiable @NonNull Set<FeatureFlag> requiredFeatures() {
+ return PaperFeatureFlagProviderImpl.fromNms(this.getHandle().requiredFeatures());
+ }
+}
diff --git a/src/main/java/io/papermc/paper/world/flag/PaperFeatureFlagProviderImpl.java b/src/main/java/io/papermc/paper/world/flag/PaperFeatureFlagProviderImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/world/flag/PaperFeatureFlagProviderImpl.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.world.flag;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import net.minecraft.world.flag.FeatureElement;
+import net.minecraft.world.flag.FeatureFlagSet;
+import net.minecraft.world.flag.FeatureFlags;
+import org.bukkit.FeatureFlag;
+import org.bukkit.craftbukkit.entity.CraftEntityType;
+import org.bukkit.craftbukkit.entity.CraftEntityTypes;
+import org.bukkit.craftbukkit.potion.CraftPotionType;
+import org.bukkit.craftbukkit.util.CraftMagicNumbers;
+import org.bukkit.entity.EntityType;
+import org.bukkit.potion.PotionType;
+
+public class PaperFeatureFlagProviderImpl implements FeatureFlagProvider {
+
+ public static final BiMap<FeatureFlag, net.minecraft.world.flag.FeatureFlag> FLAGS = ImmutableBiMap.of(
+ FeatureFlag.VANILLA, FeatureFlags.VANILLA,
+ FeatureFlag.TRADE_REBALANCE, FeatureFlags.TRADE_REBALANCE,
+ FeatureFlag.MINECART_IMPROVEMENTS, FeatureFlags.MINECART_IMPROVEMENTS,
+ FeatureFlag.REDSTONE_EXPERIMENTS, FeatureFlags.REDSTONE_EXPERIMENTS,
+ FeatureFlag.WINTER_DROP, FeatureFlags.WINTER_DROP
+ );
+
+ @Override
+ public Set<FeatureFlag> requiredFeatures(final FeatureDependant dependant) {
+ final FeatureFlagSet requiredFeatures = getFeatureElement(dependant).requiredFeatures();
+ return fromNms(requiredFeatures);
+ }
+
+ public static Set<FeatureFlag> fromNms(final FeatureFlagSet flagSet) {
+ final Set<FeatureFlag> flags = new HashSet<>();
+ for (final net.minecraft.world.flag.FeatureFlag nmsFlag : FeatureFlags.REGISTRY.names.values()) {
+ if (flagSet.contains(nmsFlag)) {
+ flags.add(FLAGS.inverse().get(nmsFlag));
+ }
+ }
+ return Collections.unmodifiableSet(flags);
+ }
+
+ static FeatureElement getFeatureElement(final FeatureDependant dependant) {
+ if (dependant instanceof final EntityType entityType) {
+ // TODO remove when EntityType is server-backed
+ return CraftEntityType.bukkitToMinecraft(entityType);
+ } else if (dependant instanceof final PotionType potionType) {
+ return CraftPotionType.bukkitToMinecraft(potionType);
+ } else {
+ throw new IllegalArgumentException(dependant + " is not a valid feature dependant");
+ }
+ }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftFeatureFlag.java b/src/main/java/org/bukkit/craftbukkit/CraftFeatureFlag.java
deleted file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- a/src/main/java/org/bukkit/craftbukkit/CraftFeatureFlag.java
+++ /dev/null
@@ -0,0 +0,0 @@
-package org.bukkit.craftbukkit;
-
-import java.util.HashSet;
-import java.util.Set;
-import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.flag.FeatureFlagSet;
-import net.minecraft.world.flag.FeatureFlags;
-import org.bukkit.FeatureFlag;
-import org.bukkit.NamespacedKey;
-import org.bukkit.craftbukkit.util.CraftNamespacedKey;
-import org.jetbrains.annotations.NotNull;
-
-public class CraftFeatureFlag implements FeatureFlag {
-
- private final NamespacedKey namespacedKey;
- private final net.minecraft.world.flag.FeatureFlag featureFlag;
-
- public CraftFeatureFlag(ResourceLocation minecraftKey, net.minecraft.world.flag.FeatureFlag featureFlag) {
- this.namespacedKey = CraftNamespacedKey.fromMinecraft(minecraftKey);
- this.featureFlag = featureFlag;
- }
-
- public net.minecraft.world.flag.FeatureFlag getHandle() {
- return this.featureFlag;
- }
-
- @NotNull
- @Override
- public NamespacedKey getKey() {
- return this.namespacedKey;
- }
-
- @Override
- public String toString() {
- return "CraftFeatureFlag{key=" + this.getKey() + ",keyUniverse=" + this.getHandle().universe.toString() + "}";
- }
-
- public static Set<CraftFeatureFlag> getFromNMS(FeatureFlagSet featureFlagSet) {
- Set<CraftFeatureFlag> set = new HashSet<>();
- FeatureFlags.REGISTRY.names.forEach((minecraftkey, featureflag) -> {
- if (featureFlagSet.contains(featureflag)) {
- set.add(new CraftFeatureFlag(minecraftkey, featureflag));
- }
- });
- return set;
- }
-
- public static CraftFeatureFlag getFromNMS(NamespacedKey namespacedKey) {
- return FeatureFlags.REGISTRY.names.entrySet().stream().filter(entry -> CraftNamespacedKey.fromMinecraft(entry.getKey()).equals(namespacedKey)).findFirst().map(entry -> new CraftFeatureFlag(entry.getKey(), entry.getValue())).orElse(null);
- }
-}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
@@ -0,0 +0,0 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
return !this.getHandle().noCollision(aabb);
}
// Paper end
+
+ // Paper start - feature flag API
+ @Override
+ public java.util.Set<org.bukkit.FeatureFlag> getFeatureFlags() {
+ return io.papermc.paper.world.flag.PaperFeatureFlagProviderImpl.fromNms(this.getHandle().enabledFeatures());
+ }
+ // Paper end - feature flag API
}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
return this.persistentDataContainer;
}
- @Override
- public Set<FeatureFlag> getFeatureFlags() {
- return CraftFeatureFlag.getFromNMS(this.getHandle().enabledFeatures()).stream().map(FeatureFlag.class::cast).collect(Collectors.toUnmodifiableSet());
- }
+ // Paper - replace feature flag API
public void storeBukkitValues(CompoundTag c) {
if (!this.persistentDataContainer.isEmpty()) {
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockType.java
@@ -0,0 +0,0 @@ import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.inventory.ItemType;
import org.jetbrains.annotations.NotNull;
-public class CraftBlockType<B extends BlockData> implements BlockType.Typed<B>, Handleable<Block> {
+public class CraftBlockType<B extends BlockData> implements BlockType.Typed<B>, Handleable<Block>, io.papermc.paper.world.flag.PaperFeatureDependent { // Paper - feature flag API
private final NamespacedKey key;
private final Block block;
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java
+++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftWorldInfo.java
@@ -0,0 +0,0 @@ public class CraftWorldInfo implements WorldInfo {
private final long seed;
private final int minHeight;
private final int maxHeight;
+ private final net.minecraft.world.flag.FeatureFlagSet enabledFeatures; // Paper - feature flag API
// Paper start
private final net.minecraft.world.level.chunk.ChunkGenerator vanillaChunkGenerator;
private final net.minecraft.core.RegistryAccess.Frozen registryAccess;
@@ -0,0 +0,0 @@ public class CraftWorldInfo implements WorldInfo {
this.seed = ((PrimaryLevelData) worldDataServer).worldGenOptions().seed();
this.minHeight = dimensionManager.minY();
this.maxHeight = dimensionManager.minY() + dimensionManager.height();
+ this.enabledFeatures = worldDataServer.enabledFeatures(); // Paper - feature flag API
}
@Override
@@ -0,0 +0,0 @@ public class CraftWorldInfo implements WorldInfo {
};
}
// Paper end
+
+ // Paper start - feature flag API
+ @Override
+ public java.util.Set<org.bukkit.FeatureFlag> getFeatureFlags() {
+ return io.papermc.paper.world.flag.PaperFeatureFlagProviderImpl.fromNms(this.enabledFeatures);
+ }
+ // Paper end - feature flag API
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemType.java
@@ -0,0 +0,0 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Handleable<Item> {
+public class CraftItemType<M extends ItemMeta> implements ItemType.Typed<M>, Handleable<Item>, io.papermc.paper.world.flag.PaperFeatureDependent { // Paper - feature flag API
private final NamespacedKey key;
private final Item item;
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMenuType.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMenuType.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMenuType.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMenuType.java
@@ -0,0 +0,0 @@ import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.MenuType;
-public class CraftMenuType<V extends InventoryView> implements MenuType.Typed<V>, Handleable<net.minecraft.world.inventory.MenuType<?>> {
+public class CraftMenuType<V extends InventoryView> implements MenuType.Typed<V>, Handleable<net.minecraft.world.inventory.MenuType<?>>, io.papermc.paper.world.flag.PaperFeatureDependent { // Paper - make FeatureDependant
private final NamespacedKey key;
private final net.minecraft.world.inventory.MenuType<?> handle;
diff --git a/src/main/java/org/bukkit/craftbukkit/packs/CraftDataPack.java b/src/main/java/org/bukkit/craftbukkit/packs/CraftDataPack.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/packs/CraftDataPack.java
+++ b/src/main/java/org/bukkit/craftbukkit/packs/CraftDataPack.java
@@ -0,0 +0,0 @@ import net.minecraft.util.InclusiveRange;
import org.bukkit.Bukkit;
import org.bukkit.FeatureFlag;
import org.bukkit.NamespacedKey;
-import org.bukkit.craftbukkit.CraftFeatureFlag;
+// import org.bukkit.craftbukkit.CraftFeatureFlag; // Paper - replace feature flag API
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.packs.DataPack;
@@ -0,0 +0,0 @@ public class CraftDataPack implements DataPack {
@Override
public Set<FeatureFlag> getRequestedFeatures() {
- return CraftFeatureFlag.getFromNMS(this.getHandle().getRequestedFeatures()).stream().map(FeatureFlag.class::cast).collect(Collectors.toUnmodifiableSet());
+ return io.papermc.paper.world.flag.PaperFeatureFlagProviderImpl.fromNms(this.getHandle().getRequestedFeatures()); // Paper - replace feature flag API
}
@Override
diff --git a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
+++ b/src/main/java/org/bukkit/craftbukkit/potion/CraftPotionEffectType.java
@@ -0,0 +0,0 @@ import org.bukkit.potion.PotionEffectType;
import org.bukkit.potion.PotionEffectTypeCategory;
import org.jetbrains.annotations.NotNull;
-public class CraftPotionEffectType extends PotionEffectType implements Handleable<MobEffect> {
+public class CraftPotionEffectType extends PotionEffectType implements Handleable<MobEffect>, io.papermc.paper.world.flag.PaperFeatureDependent { // Paper - feature flag API
public static PotionEffectType minecraftHolderToBukkit(Holder<MobEffect> minecraft) {
return CraftPotionEffectType.minecraftToBukkit(minecraft.value());
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -0,0 +0,0 @@ import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeModifier;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
-import org.bukkit.craftbukkit.CraftFeatureFlag;
+// import org.bukkit.craftbukkit.CraftFeatureFlag; // Paper
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.block.CraftBiome;
@@ -0,0 +0,0 @@ public final class CraftMagicNumbers implements UnsafeValues {
return attribute.getTranslationKey();
}
- @Override
- public FeatureFlag getFeatureFlag(NamespacedKey namespacedKey) {
- Preconditions.checkArgument(namespacedKey != null, "NamespaceKey cannot be null");
- return CraftFeatureFlag.getFromNMS(namespacedKey);
- }
+ // Paper - replace feature flag API
@Override
public PotionType.InternalPotionData getInternalPotionData(NamespacedKey namespacedKey) {
diff --git a/src/main/resources/META-INF/services/io.papermc.paper.world.flag.FeatureFlagProvider b/src/main/resources/META-INF/services/io.papermc.paper.world.flag.FeatureFlagProvider
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/resources/META-INF/services/io.papermc.paper.world.flag.FeatureFlagProvider
@@ -0,0 +1 @@
+io.papermc.paper.world.flag.PaperFeatureFlagProviderImpl
diff --git a/src/main/resources/META-INF/services/io.papermc.paper.world.flag.FeatureFlags$FeatureFlagProvider b/src/main/resources/META-INF/services/io.papermc.paper.world.flag.FeatureFlags$FeatureFlagProvider
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/resources/META-INF/services/io.papermc.paper.world.flag.FeatureFlags$FeatureFlagProvider
@@ -0,0 +1 @@
+io.papermc.paper.world.flag.PaperFeatureFlagProviderImpl
diff --git a/src/test/java/io/papermc/paper/world/flag/FeatureFlagTest.java b/src/test/java/io/papermc/paper/world/flag/FeatureFlagTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/test/java/io/papermc/paper/world/flag/FeatureFlagTest.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.world.flag;
+
+import io.papermc.paper.adventure.PaperAdventure;
+import io.papermc.paper.registry.PaperRegistries;
+import io.papermc.paper.registry.RegistryAccess;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.entry.RegistryEntry;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Stream;
+import net.kyori.adventure.key.Key;
+import net.minecraft.resources.ResourceKey;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.flag.FeatureElement;
+import net.minecraft.world.flag.FeatureFlagSet;
+import net.minecraft.world.flag.FeatureFlags;
+import org.bukkit.FeatureFlag;
+import org.bukkit.Keyed;
+import org.bukkit.support.RegistryHelper;
+import org.bukkit.support.environment.AllFeatures;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+
+@AllFeatures
+class FeatureFlagTest {
+
+ @Test
+ void testFeatureFlagParity() {
+ final Set<ResourceLocation> locations = new HashSet<>();
+ for (final FeatureFlag flag : FeatureFlag.ALL_FLAGS.values()) {
+ locations.add(PaperAdventure.asVanilla(flag.getKey()));
+ }
+ FeatureFlags.REGISTRY.fromNames(locations, unknown -> {
+ fail("Unknown api feature flag: " + unknown);
+ });
+
+ for (final ResourceLocation nmsFlag : allNames()) {
+ assertNotNull(FeatureFlag.ALL_FLAGS.value(Key.key(nmsFlag.toString())), "can't find api flag for " + nmsFlag);
+ }
+ }
+
+ @Test
+ void testFeatureFlagConversion() {
+ assertEquals(allNames().size(), PaperFeatureFlagProviderImpl.FLAGS.size());
+ for (final FeatureFlag featureFlag : PaperFeatureFlagProviderImpl.FLAGS.keySet()) {
+ final net.minecraft.world.flag.FeatureFlag nmsFlag = PaperFeatureFlagProviderImpl.FLAGS.get(featureFlag);
+ final ResourceLocation nmsFlagName = FeatureFlags.REGISTRY.toNames(FeatureFlagSet.of(nmsFlag)).iterator().next();
+ assertEquals(nmsFlagName.toString(), featureFlag.key().asString());
+ }
+ }
+
+ static Set<ResourceLocation> allNames() {
+ return FeatureFlags.REGISTRY.toNames(FeatureFlags.REGISTRY.allFlags());
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ static Set<RegistryKey<?>> featureFilteredRegistries() {
+ final Set<RegistryKey<?>> registryKeys = new HashSet<>();
+ for (final ResourceKey filteredRegistry : FeatureElement.FILTERED_REGISTRIES) {
+ registryKeys.add(PaperRegistries.registryFromNms(filteredRegistry));
+ }
+ return registryKeys;
+ }
+
+ @MethodSource("featureFilteredRegistries")
+ @ParameterizedTest
+ <T extends Keyed> void testApiImplementsFeatureDependant(final RegistryKey<T> registryKey) {
+ final org.bukkit.Registry<T> registry = RegistryAccess.registryAccess().getRegistry(registryKey);
+ final T anyElement = registry.iterator().next();
+ assertInstanceOf(FeatureDependant.class, anyElement, "Registry " + registryKey + " doesn't have feature dependent elements");
+ final FeatureDependant dependant = ((FeatureDependant) anyElement);
+ assertDoesNotThrow(dependant::requiredFeatures, "Failed to get required features for " + anyElement + " in " + registryKey);
+ }
+
+ static Stream<RegistryKey<?>> nonFeatureFilteredRegistries() {
+ return RegistryHelper.getRegistry().registries().filter(r -> {
+ final RegistryEntry<?, ?> entry = PaperRegistries.getEntry(r.key());
+ // has an API registry and isn't a filtered registry
+ return entry != null && !FeatureElement.FILTERED_REGISTRIES.contains(r.key());
+ }).map(r -> PaperRegistries.getEntry(r.key()).apiKey());
+ }
+
+
+ @MethodSource("nonFeatureFilteredRegistries")
+ @ParameterizedTest
+ <T extends Keyed> void testApiDoesntImplementFeatureDependant(final RegistryKey<T> registryKey) {
+ final org.bukkit.Registry<T> registry = RegistryAccess.registryAccess().getRegistry(registryKey);
+ final T anyElement = registry.iterator().next();
+ assertFalse(anyElement instanceof FeatureDependant, "Registry " + registryKey + " has feature dependent elements");
+ }
+}

View File

@@ -1,45 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com>
Date: Sat, 9 Jul 2022 17:28:42 +0200
Subject: [PATCH] Add Offline PDC API
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
@@ -0,0 +0,0 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
}
// Paper end
+ // Paper start - Add Offline PDC API
+ private static final org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new org.bukkit.craftbukkit.persistence.CraftPersistentDataTypeRegistry();
+ private io.papermc.paper.persistence.@org.checkerframework.checker.nullness.qual.MonotonicNonNull PersistentDataContainerView persistentDataContainerView;
+
+ @Override
+ public io.papermc.paper.persistence.PersistentDataContainerView getPersistentDataContainer() {
+ if (this.persistentDataContainerView == null) {
+ this.persistentDataContainerView = new io.papermc.paper.persistence.PaperPersistentDataContainerView(DATA_TYPE_REGISTRY) {
+
+ private CompoundTag getPersistentTag() {
+ return net.minecraft.Optionull.map(CraftOfflinePlayer.this.getData(), data -> data.getCompound("BukkitValues"));
+ }
+
+ @Override
+ public CompoundTag toTagCompound() {
+ return java.util.Objects.requireNonNullElseGet(this.getPersistentTag(), CompoundTag::new);
+ }
+
+ @Override
+ public net.minecraft.nbt.Tag getTag(String key) {
+ return net.minecraft.Optionull.map(this.getPersistentTag(), tag -> tag.get(key));
+ }
+ };
+ }
+ return this.persistentDataContainerView;
+ }
+ // Paper end - Add Offline PDC API
+
@Override
public Location getLastDeathLocation() {
if (this.getData().contains("LastDeathLocation", 10)) {

View File

@@ -1,36 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Mariell Hoversholm <proximyst@proximyst.com>
Date: Sun, 29 Sep 2024 16:21:26 +0200
Subject: [PATCH] Add PlayerInsertLecternBookEvent
diff --git a/src/main/java/net/minecraft/world/level/block/LecternBlock.java b/src/main/java/net/minecraft/world/level/block/LecternBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/LecternBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/LecternBlock.java
@@ -0,0 +0,0 @@ public class LecternBlock extends BaseEntityBlock {
BlockEntity tileentity = world.getBlockEntity(pos);
if (tileentity instanceof LecternBlockEntity tileentitylectern) {
- tileentitylectern.setBook(stack.consumeAndReturn(1, user));
+ // Paper start - Add PlayerInsertLecternBookEvent
+ ItemStack eventSourcedBookStack = null;
+ if (user instanceof final net.minecraft.server.level.ServerPlayer serverPlayer) {
+ final io.papermc.paper.event.player.PlayerInsertLecternBookEvent event = new io.papermc.paper.event.player.PlayerInsertLecternBookEvent(
+ serverPlayer.getBukkitEntity(),
+ org.bukkit.craftbukkit.block.CraftBlock.at(world, pos),
+ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack.copyWithCount(1))
+ );
+ if (!event.callEvent()) return;
+ eventSourcedBookStack = org.bukkit.craftbukkit.inventory.CraftItemStack.unwrap(event.getBook());
+ }
+ if (eventSourcedBookStack == null) {
+ eventSourcedBookStack = stack.consumeAndReturn(1, user);
+ } else {
+ stack.consume(1, user);
+ }
+ tileentitylectern.setBook(eventSourcedBookStack);
+ // Paper end - Add PlayerInsertLecternBookEvent
LecternBlock.resetBookState(user, world, pos, state, true);
world.playSound((Player) null, pos, SoundEvents.BOOK_PUT, SoundSource.BLOCKS, 1.0F, 1.0F);
}

View File

@@ -1,33 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: masmc05 <masmc05@gmail.com>
Date: Wed, 25 Sep 2024 16:26:04 +0300
Subject: [PATCH] Add enchantWithLevels with enchantment registry set
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemFactory.java
@@ -0,0 +0,0 @@ public final class CraftItemFactory implements ItemFactory {
);
}
+ @Override
+ public ItemStack enchantWithLevels(ItemStack itemStack, int levels, io.papermc.paper.registry.set.RegistryKeySet<org.bukkit.enchantments.Enchantment> keySet, java.util.Random random) {
+ return enchantWithLevels(
+ itemStack,
+ levels,
+ Optional.of(
+ io.papermc.paper.registry.set.PaperRegistrySets.convertToNms(
+ Registries.ENCHANTMENT,
+ net.minecraft.server.MinecraftServer.getServer().registryAccess().createSerializationContext(net.minecraft.nbt.NbtOps.INSTANCE).lookupProvider,
+ keySet
+ )
+ ),
+ random
+ );
+ }
+
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private ItemStack enchantWithLevels(
ItemStack itemStack,

View File

@@ -1,39 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: okx-code <okx@okx.sh>
Date: Sat, 17 Aug 2024 13:02:45 +0100
Subject: [PATCH] Add enchantment seed update API
diff --git a/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java b/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/EnchantmentMenu.java
@@ -0,0 +0,0 @@ public class EnchantmentMenu extends AbstractContainerMenu {
return this.bukkitEntity;
}
// CraftBukkit end
+
+ // Paper start - add enchantment seed update API
+ public void setEnchantmentSeed(int seed) {
+ this.enchantmentSeed.set(seed);
+ }
+ // Paper end - add enchantment seed update API
}
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftEnchantmentView.java b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftEnchantmentView.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftEnchantmentView.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftEnchantmentView.java
@@ -0,0 +0,0 @@ public class CraftEnchantmentView extends CraftInventoryView<EnchantmentMenu, En
return this.container.getEnchantmentSeed();
}
+ // Paper start - add enchantment seed update API
+ @Override
+ public void setEnchantmentSeed(int seed) {
+ this.container.setEnchantmentSeed(seed);
+ }
+ // Paper end - add enchantment seed update API
+
@NotNull
@Override
public EnchantmentOffer[] getOffers() {

View File

@@ -1,168 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <Spottedleaf@users.noreply.github.com>
Date: Wed, 16 Oct 2024 06:41:32 -0700
Subject: [PATCH] Add proper async player disconnections
Blocking can cause performance problems
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/network/Connection.java
+++ b/src/main/java/net/minecraft/network/Connection.java
@@ -0,0 +0,0 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
}
+ // Paper start - add proper async disconnect
+ public void enableAutoRead() {
+ if (this.channel != null) {
+ this.channel.config().setAutoRead(true);
+ }
+ }
+ // Paper end - add proper async disconnect
+
public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) {
if (compressionThreshold >= 0) {
com.velocitypowered.natives.compression.VelocityCompressor compressor = com.velocitypowered.natives.util.Natives.compress.get().create(io.papermc.paper.configuration.GlobalConfiguration.get().misc.compressionLevel.or(-1)); // Paper - Use Velocity cipher
diff --git a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
@@ -0,0 +0,0 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
this.latency = (this.latency * 3 + i) / 4;
this.keepAlivePending = false;
} else if (!this.isSingleplayerOwner()) {
- // Paper start - This needs to be handled on the main thread for plugins
- server.submit(() -> {
- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause
- });
- // Paper end - This needs to be handled on the main thread for plugins
+ this.disconnectAsync(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - add proper async disconnect
}
}
@@ -0,0 +0,0 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
minecraftserver.scheduleOnMain(networkmanager::handleDisconnection); // Paper
}
+ // Paper start - add proper async disconnect
+ public void disconnectAsync(net.kyori.adventure.text.Component reason, PlayerKickEvent.Cause cause) {
+ this.disconnectAsync(io.papermc.paper.adventure.PaperAdventure.asVanilla(reason), cause);
+ }
+
+ public void disconnectAsync(Component reason, PlayerKickEvent.Cause cause) {
+ this.disconnectAsync(new DisconnectionDetails(reason), cause);
+ }
+
+ public void disconnectAsync(DisconnectionDetails disconnectionInfo, PlayerKickEvent.Cause cause) {
+ if (this.cserver.isPrimaryThread()) {
+ this.disconnect(disconnectionInfo, cause);
+ return;
+ }
+ this.connection.setReadOnly();
+ this.server.scheduleOnMain(() -> {
+ ServerCommonPacketListenerImpl.this.disconnect(disconnectionInfo, cause);
+ if (ServerCommonPacketListenerImpl.this.player.quitReason == null) {
+ // cancelled
+ ServerCommonPacketListenerImpl.this.connection.enableAutoRead();
+ }
+ });
+ }
+ // Paper end - add proper async disconnect
+
protected boolean isSingleplayerOwner() {
return this.server.isSingleplayerOwner(this.playerProfile());
}
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
// PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // Paper - AsyncTabCompleteEvent; run this async
// CraftBukkit start
if (!this.tabSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) { // Paper - configurable tab spam limits
- this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - Kick event cause
+ this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - Kick event cause // Paper - add proper async disconnect
return;
}
// CraftBukkit end
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
// Paper start
final int index;
if (packet.getCommand().length() > 64 && ((index = packet.getCommand().indexOf(' ')) == -1 || index >= 64)) {
- this.disconnect(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM);
+ this.disconnectAsync(Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - add proper async disconnect
return;
}
// Paper end
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
if (byteTotal > byteAllowed) {
ServerGamePacketListenerImpl.LOGGER.warn("{} tried to send a book too large. Book size: {} - Allowed: {} - Pages: {}", this.player.getScoreboardName(), byteTotal, byteAllowed, pageList.size());
- this.disconnect(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause
+ this.disconnectAsync(Component.literal("Book too large!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause // Paper - add proper async disconnect
return;
}
}
// Paper end - Book size limits
// CraftBukkit start
if (this.lastBookTick + 20 > MinecraftServer.currentTick) {
- this.disconnect(Component.literal("Book edited too quickly!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause
+ this.disconnectAsync(Component.literal("Book edited too quickly!"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_ACTION); // Paper - kick event cause // Paper - add proper async disconnect
return;
}
this.lastBookTick = MinecraftServer.currentTick;
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
private void tryHandleChat(String s, Runnable runnable, boolean sync) { // CraftBukkit
if (ServerGamePacketListenerImpl.isChatMessageIllegal(s)) {
- this.disconnect((Component) Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper
+ this.disconnectAsync((Component) Component.translatable("multiplayer.disconnect.illegal_characters"), org.bukkit.event.player.PlayerKickEvent.Cause.ILLEGAL_CHARACTERS); // Paper // Paper - add proper async disconnect
} else if (this.player.isRemoved() || this.player.getChatVisibility() == ChatVisiblity.HIDDEN) { // CraftBukkit - dead men tell no tales
this.send(new ClientboundSystemChatPacket(Component.translatable("chat.disabled.options").withStyle(ChatFormatting.RED), false));
} else {
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
if (optional.isEmpty()) {
ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString());
- this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes
+ this.disconnectAsync(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes // Paper - add proper async disconnect
}
return optional;
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
// this.chatSpamThrottler.increment();
if (!this.chatSpamThrottler.isIncrementAndUnderThreshold() && !this.server.getPlayerList().isOp(this.player.getGameProfile()) && !this.server.isSingleplayerOwner(this.player.getGameProfile())) {
// CraftBukkit end
- this.disconnect((Component) Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause
+ this.disconnectAsync((Component) Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause // Paper - add proper async disconnect
}
}
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
synchronized (this.lastSeenMessages) {
if (!this.lastSeenMessages.applyOffset(packet.offset())) {
ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString());
- this.disconnect(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes
+ this.disconnectAsync(ServerGamePacketListenerImpl.CHAT_VALIDATION_FAILED, org.bukkit.event.player.PlayerKickEvent.Cause.CHAT_VALIDATION_FAILED); // Paper - kick event causes // Paper - add proper async disconnect
}
}
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
if (i > 4096) {
- this.disconnect((Component) Component.translatable("multiplayer.disconnect.too_many_pending_chats"), org.bukkit.event.player.PlayerKickEvent.Cause.TOO_MANY_PENDING_CHATS); // Paper - kick event cause
+ this.disconnectAsync((Component) Component.translatable("multiplayer.disconnect.too_many_pending_chats"), org.bukkit.event.player.PlayerKickEvent.Cause.TOO_MANY_PENDING_CHATS); // Paper - kick event cause // Paper - add proper async disconnect
}
}
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
// Paper start - auto recipe limit
if (!org.bukkit.Bukkit.isPrimaryThread()) {
if (!this.recipeSpamPackets.isIncrementAndUnderThreshold()) {
- this.disconnect(net.minecraft.network.chat.Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause
+ this.disconnectAsync(net.minecraft.network.chat.Component.translatable("disconnect.spam"), org.bukkit.event.player.PlayerKickEvent.Cause.SPAM); // Paper - kick event cause // Paper - add proper async disconnect
return;
}
}

View File

@@ -1,181 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tamion <70228790+notTamion@users.noreply.github.com>
Date: Sun, 15 Sep 2024 19:17:12 +0200
Subject: [PATCH] Add recipeBrewTime
== AT ==
public net.minecraft.world.inventory.BrewingStandMenu brewingStandData
diff --git a/src/main/java/io/papermc/paper/inventory/BrewingSimpleContainerData.java b/src/main/java/io/papermc/paper/inventory/BrewingSimpleContainerData.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/inventory/BrewingSimpleContainerData.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.inventory;
+
+import net.minecraft.world.inventory.SimpleContainerData;
+
+public class BrewingSimpleContainerData extends SimpleContainerData {
+
+ public BrewingSimpleContainerData() {
+ super(3);
+ this.set(2, 400);
+ }
+}
diff --git a/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java b/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/BrewingStandMenu.java
@@ -0,0 +0,0 @@ public class BrewingStandMenu extends AbstractContainerMenu {
// CraftBukkit end
public BrewingStandMenu(int syncId, Inventory playerInventory) {
- this(syncId, playerInventory, new SimpleContainer(5), new SimpleContainerData(2));
+ this(syncId, playerInventory, new SimpleContainer(5), new io.papermc.paper.inventory.BrewingSimpleContainerData()); // Paper - Add totalBrewTime
}
public BrewingStandMenu(int syncId, Inventory playerInventory, Container inventory, ContainerData propertyDelegate) {
super(MenuType.BREWING_STAND, syncId);
this.player = playerInventory; // CraftBukkit
checkContainerSize(inventory, 5);
- checkContainerDataCount(propertyDelegate, 2);
+ checkContainerDataCount(propertyDelegate, 3); // Paper - Add recipeBrewTime
this.brewingStand = inventory;
this.brewingStandData = propertyDelegate;
PotionBrewing potionbrewer = playerInventory.player.level().potionBrewing();
@@ -0,0 +0,0 @@ public class BrewingStandMenu extends AbstractContainerMenu {
// Paper end - custom potion mixes
this.ingredientSlot = this.addSlot(new BrewingStandMenu.IngredientsSlot(potionbrewer, inventory, 3, 79, 17));
this.addSlot(new BrewingStandMenu.FuelSlot(inventory, 4, 17, 17));
- this.addDataSlots(propertyDelegate);
+ // Paper start - Add recipeBrewTime
+ this.addDataSlots(new SimpleContainerData(2) {
+ @Override
+ public int get(final int index) {
+ if (index == 0) return 400 * propertyDelegate.get(index) / propertyDelegate.get(2);
+ return propertyDelegate.get(index);
+ }
+
+ @Override
+ public void set(final int index, final int value) {
+ propertyDelegate.set(index, value);
+ }
+ });
+ // Paper end - Add recipeBrewTime
this.addStandardInventorySlots(playerInventory, 8, 84);
}
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
@@ -0,0 +0,0 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
public static final int NUM_DATA_VALUES = 2;
private NonNullList<ItemStack> items;
public int brewTime;
+ public int recipeBrewTime = 400; // Paper - Add recipeBrewTime
private boolean[] lastPotionCount;
private Item ingredient;
public int fuel;
@@ -0,0 +0,0 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
case 1:
j = BrewingStandBlockEntity.this.fuel;
break;
+ // Paper start - Add recipeBrewTime
+ case 2:
+ j = BrewingStandBlockEntity.this.recipeBrewTime;
+ break;
+ // Paper end - Add recipeBrewTime
default:
j = 0;
}
@@ -0,0 +0,0 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
break;
case 1:
BrewingStandBlockEntity.this.fuel = value;
+ // Paper start - Add recipeBrewTime
+ case 2:
+ BrewingStandBlockEntity.this.recipeBrewTime = value;
+ break;
+ // Paper end - Add recipeBrewTime
}
}
@Override
public int getCount() {
- return 2;
+ return 3; // Paper - Add recipeBrewTime
}
};
}
@@ -0,0 +0,0 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
// CraftBukkit start
BrewingStartEvent event = new BrewingStartEvent(CraftBlock.at(world, pos), CraftItemStack.asCraftMirror(itemstack1), 400);
world.getCraftServer().getPluginManager().callEvent(event);
- blockEntity.brewTime = event.getTotalBrewTime(); // 400 -> event.getTotalBrewTime()
+ blockEntity.recipeBrewTime = event.getRecipeBrewTime(); // Paper - use recipe brew time from event
+ blockEntity.brewTime = event.getBrewingTime(); // 400 -> event.getTotalBrewTime() // Paper - use brewing time from event
// CraftBukkit end
blockEntity.ingredient = itemstack1.getItem();
setChanged(world, pos, state);
diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java
+++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBrewingStand.java
@@ -0,0 +0,0 @@ public class CraftBrewingStand extends CraftContainer<BrewingStandBlockEntity> i
this.getSnapshot().brewTime = brewTime;
}
+ // Paper start - Add recipeBrewTime
+ @Override
+ public void setRecipeBrewTime(int recipeBrewTime) {
+ com.google.common.base.Preconditions.checkArgument(recipeBrewTime > 0, "recipeBrewTime must be positive");
+ this.getSnapshot().recipeBrewTime = recipeBrewTime;
+ }
+
+ @Override
+ public int getRecipeBrewTime() {
+ return this.getSnapshot().recipeBrewTime;
+ }
+ // Paper end - Add recipeBrewTime
+
@Override
public int getFuelLevel() {
return this.getSnapshot().fuel;
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
@@ -0,0 +0,0 @@ public class CraftContainer extends AbstractContainerMenu {
this.delegate = new EnchantmentMenu(windowId, bottom);
break;
case BREWING:
- this.delegate = new BrewingStandMenu(windowId, bottom, top, new SimpleContainerData(2));
+ this.delegate = new BrewingStandMenu(windowId, bottom, top, new io.papermc.paper.inventory.BrewingSimpleContainerData()); // Paper - Add recipeBrewTime
break;
case HOPPER:
this.delegate = new HopperMenu(windowId, bottom, top);
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftBrewingStandView.java b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftBrewingStandView.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftBrewingStandView.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/view/CraftBrewingStandView.java
@@ -0,0 +0,0 @@ public class CraftBrewingStandView extends CraftInventoryView<BrewingStandMenu,
Preconditions.checkArgument(brewingTicks > 0, "The given brewing ticks must be greater than 0");
this.container.setData(BrewingStandBlockEntity.DATA_BREW_TIME, brewingTicks);
}
+
+ // Paper start - Add recipeBrewTime
+ @Override
+ public void setRecipeBrewTime(int recipeBrewTime) {
+ com.google.common.base.Preconditions.checkArgument(recipeBrewTime > 0, "recipeBrewTime must be positive");
+ this.container.brewingStandData.set(2, recipeBrewTime);
+ }
+
+ @Override
+ public int getRecipeBrewTime() {
+ return this.container.brewingStandData.get(2);
+ }
+ // Paper end - Add recipeBrewTime
}

View File

@@ -1,203 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Sun, 16 Jan 2022 10:13:33 -0800
Subject: [PATCH] Call bucket events for cauldrons
diff --git a/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java b/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java
+++ b/src/main/java/net/minecraft/core/cauldron/CauldronInteraction.java
@@ -0,0 +0,0 @@ public interface CauldronInteraction {
static CauldronInteraction.InteractionMap newInteractionMap(String name) {
Object2ObjectOpenHashMap<Item, CauldronInteraction> object2objectopenhashmap = new Object2ObjectOpenHashMap();
- object2objectopenhashmap.defaultReturnValue((iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ object2objectopenhashmap.defaultReturnValue((iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return InteractionResult.TRY_WITH_EMPTY_HAND;
});
CauldronInteraction.InteractionMap cauldroninteraction_a = new CauldronInteraction.InteractionMap(name, object2objectopenhashmap);
@@ -0,0 +0,0 @@ public interface CauldronInteraction {
return cauldroninteraction_a;
}
- InteractionResult interact(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack);
+ InteractionResult interact(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection); // Paper - add hitDirection
static void bootStrap() {
Map<Item, CauldronInteraction> map = CauldronInteraction.EMPTY.map();
CauldronInteraction.addDefaultInteractions(map);
- map.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
PotionContents potioncontents = (PotionContents) itemstack.get(DataComponents.POTION_CONTENTS);
if (potioncontents != null && potioncontents.is(Potions.WATER)) {
@@ -0,0 +0,0 @@ public interface CauldronInteraction {
Map<Item, CauldronInteraction> map1 = CauldronInteraction.WATER.map();
CauldronInteraction.addDefaultInteractions(map1);
- map1.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map1.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.WATER_BUCKET), (iblockdata1) -> {
return (Integer) iblockdata1.getValue(LayeredCauldronBlock.LEVEL) == 3;
- }, SoundEvents.BUCKET_FILL);
+ }, SoundEvents.BUCKET_FILL, hitDirection); // Paper - add hitDirection
});
- map1.put(Items.GLASS_BOTTLE, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map1.put(Items.GLASS_BOTTLE, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
if (!world.isClientSide) {
// CraftBukkit start
if (!LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition, entityhuman, CauldronLevelChangeEvent.ChangeReason.BOTTLE_FILL)) {
@@ -0,0 +0,0 @@ public interface CauldronInteraction {
return InteractionResult.SUCCESS;
});
- map1.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map1.put(Items.POTION, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
if ((Integer) iblockdata.getValue(LayeredCauldronBlock.LEVEL) == 3) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
@@ -0,0 +0,0 @@ public interface CauldronInteraction {
map1.put(Items.YELLOW_SHULKER_BOX, CauldronInteraction::shulkerBoxInteraction);
Map<Item, CauldronInteraction> map2 = CauldronInteraction.LAVA.map();
- map2.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map2.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.LAVA_BUCKET), (iblockdata1) -> {
return true;
- }, SoundEvents.BUCKET_FILL_LAVA);
+ }, SoundEvents.BUCKET_FILL_LAVA, hitDirection); // Paper - add hitDirection
});
CauldronInteraction.addDefaultInteractions(map2);
Map<Item, CauldronInteraction> map3 = CauldronInteraction.POWDER_SNOW.map();
- map3.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack) -> {
+ map3.put(Items.BUCKET, (iblockdata, world, blockposition, entityhuman, enumhand, itemstack, hitDirection) -> { // Paper - add hitDirection
return CauldronInteraction.fillBucket(iblockdata, world, blockposition, entityhuman, enumhand, itemstack, new ItemStack(Items.POWDER_SNOW_BUCKET), (iblockdata1) -> {
return (Integer) iblockdata1.getValue(LayeredCauldronBlock.LEVEL) == 3;
- }, SoundEvents.BUCKET_FILL_POWDER_SNOW);
+ }, SoundEvents.BUCKET_FILL_POWDER_SNOW, hitDirection); // Paper - add hitDirection
});
CauldronInteraction.addDefaultInteractions(map3);
}
@@ -0,0 +0,0 @@ public interface CauldronInteraction {
}
static InteractionResult fillBucket(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, ItemStack output, Predicate<BlockState> fullPredicate, SoundEvent soundEvent) {
+ // Paper start - add hitDirection
+ return fillBucket(state, world, pos, player, hand, stack, output, fullPredicate, soundEvent, null); // Paper - add hitDirection
+ }
+ static InteractionResult fillBucket(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, ItemStack output, Predicate<BlockState> fullPredicate, SoundEvent soundEvent, @javax.annotation.Nullable net.minecraft.core.Direction hitDirection) {
+ // Paper end - add hitDirection
if (!fullPredicate.test(state)) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else {
if (!world.isClientSide) {
+ // Paper start - fire PlayerBucketFillEvent
+ if (hitDirection != null) {
+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketFillEvent((net.minecraft.server.level.ServerLevel) world, player, pos, pos, hitDirection, stack, output.getItem(), hand);
+ if (event.isCancelled()) {
+ return InteractionResult.PASS;
+ }
+ output = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY;
+ }
+ // Paper end - fire PlayerBucketFillEvent
// CraftBukkit start
if (!LayeredCauldronBlock.changeLevel(state, world, pos, Blocks.CAULDRON.defaultBlockState(), player, CauldronLevelChangeEvent.ChangeReason.BUCKET_FILL, false)) { // Paper - Call CauldronLevelChangeEvent
return InteractionResult.SUCCESS;
@@ -0,0 +0,0 @@ public interface CauldronInteraction {
}
static InteractionResult emptyBucket(Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, BlockState state, SoundEvent soundEvent) {
+ // Paper start - add hitDirection
+ return emptyBucket(world, pos, player, hand, stack, state, soundEvent, null);
+ }
+ static InteractionResult emptyBucket(Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, BlockState state, SoundEvent soundEvent, @javax.annotation.Nullable net.minecraft.core.Direction hitDirection) {
+ // Paper end - add hitDirection
if (!world.isClientSide) {
+ // Paper start - fire PlayerBucketEmptyEvent
+ ItemStack output = new ItemStack(Items.BUCKET);
+ if (hitDirection != null) {
+ org.bukkit.event.player.PlayerBucketEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerBucketEmptyEvent((net.minecraft.server.level.ServerLevel) world, player, pos, pos, hitDirection, stack, hand);
+ if (event.isCancelled()) {
+ return InteractionResult.PASS;
+ }
+ output = event.getItemStack() != null ? org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItemStack()) : ItemStack.EMPTY;
+ }
+ // Paper end - fire PlayerBucketEmptyEvent
// CraftBukkit start
if (!LayeredCauldronBlock.changeLevel(state, world, pos, state, player, CauldronLevelChangeEvent.ChangeReason.BUCKET_EMPTY, false)) { // Paper - Call CauldronLevelChangeEvent
return InteractionResult.SUCCESS;
@@ -0,0 +0,0 @@ public interface CauldronInteraction {
// CraftBukkit end
Item item = stack.getItem();
- player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, new ItemStack(Items.BUCKET)));
+ player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, output)); // Paper
player.awardStat(Stats.FILL_CAULDRON);
player.awardStat(Stats.ITEM_USED.get(item));
// world.setBlockAndUpdate(blockposition, iblockdata); // CraftBukkit
@@ -0,0 +0,0 @@ public interface CauldronInteraction {
return InteractionResult.SUCCESS;
}
- private static InteractionResult fillWaterInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
- return CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY);
+ private static InteractionResult fillWaterInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
+ return CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY, hitDirection); // Paper - add hitDirection
}
- private static InteractionResult fillLavaInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
- return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA));
+ private static InteractionResult fillLavaInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
+ return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, Blocks.LAVA_CAULDRON.defaultBlockState(), SoundEvents.BUCKET_EMPTY_LAVA, hitDirection)); // Paper - add hitDirection
}
- private static InteractionResult fillPowderSnowInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
- return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY_POWDER_SNOW));
+ private static InteractionResult fillPowderSnowInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
+ return (InteractionResult) (CauldronInteraction.isUnderWater(world, pos) ? InteractionResult.CONSUME : CauldronInteraction.emptyBucket(world, pos, player, hand, stack, (BlockState) Blocks.POWDER_SNOW_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 3), SoundEvents.BUCKET_EMPTY_POWDER_SNOW, hitDirection)); // Paper - add hitDirection
}
- private static InteractionResult shulkerBoxInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
+ private static InteractionResult shulkerBoxInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
Block block = Block.byItem(stack.getItem());
if (!(block instanceof ShulkerBoxBlock)) {
@@ -0,0 +0,0 @@ public interface CauldronInteraction {
player.awardStat(Stats.CLEAN_SHULKER_BOX);
// LayeredCauldronBlock.lowerFillLevel(iblockdata, world, blockposition); // CraftBukkit
}
-
return InteractionResult.SUCCESS;
}
}
- private static InteractionResult bannerInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
+ private static InteractionResult bannerInteraction(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
BannerPatternLayers bannerpatternlayers = (BannerPatternLayers) stack.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY);
if (bannerpatternlayers.layers().isEmpty()) {
@@ -0,0 +0,0 @@ public interface CauldronInteraction {
}
}
- private static InteractionResult dyedItemIteration(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack) {
+ private static InteractionResult dyedItemIteration(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, ItemStack stack, final net.minecraft.core.Direction hitDirection) { // Paper - add hitDirection
if (!stack.is(ItemTags.DYEABLE)) {
return InteractionResult.TRY_WITH_EMPTY_HAND;
} else if (!stack.has(DataComponents.DYED_COLOR)) {
diff --git a/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java b/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/AbstractCauldronBlock.java
@@ -0,0 +0,0 @@ public abstract class AbstractCauldronBlock extends Block {
@Override
protected InteractionResult useItemOn(ItemStack stack, BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
CauldronInteraction cauldronInteraction = this.interactions.map().get(stack.getItem());
- return cauldronInteraction.interact(state, world, pos, player, hand, stack);
+ return cauldronInteraction.interact(state, world, pos, player, hand, stack, hit.getDirection()); // Paper - pass hit direction
}
@Override

View File

@@ -1,29 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Newwind <support@newwindserver.com>
Date: Mon, 26 Aug 2024 14:01:37 +0200
Subject: [PATCH] Check dead flag in isAlive()
If a plugin sets the health of a living entity above 0 after it has already died, the entity will be "revived".
It will behave the exact same as before, except with the internal "dead" flag set, resulting in 2 behavior changes,
A: it's completely invulnerable to all damage
B: it's unable to pickup items
isValid() for these bugged entities will return true, isDead() will return false, despite the dead flag.
This patch checks that the mob isn't dead before saying its alive.
Also, even if the plugin is responsibly checking !isDead() before modifying health, on very rare circumstances
I am currently unable to replicate, these "revived" entities can still appear
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
@Override
public boolean isAlive() {
- return !this.isRemoved() && this.getHealth() > 0.0F;
+ return !this.isRemoved() && this.getHealth() > 0.0F && !this.dead; // Paper - Check this.dead
}
public boolean isLookingAtMe(LivingEntity entity, double d0, boolean flag, boolean visualShape, Predicate<LivingEntity> predicate, DoubleSupplier... entityYChecks) {

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Newwind <support@newwindserver.com>
Date: Fri, 20 Sep 2024 14:17:37 +0200
Subject: [PATCH] Disable pretty printing for advancement saving
Reduces json size by about 25%
Not sure why advancements even had pretty printing enabled.
My best guess was by accident on mojang's part, especially since stats json files don't have pretty printing.
diff --git a/src/main/java/net/minecraft/server/PlayerAdvancements.java b/src/main/java/net/minecraft/server/PlayerAdvancements.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/PlayerAdvancements.java
+++ b/src/main/java/net/minecraft/server/PlayerAdvancements.java
@@ -0,0 +0,0 @@ import org.slf4j.Logger;
public class PlayerAdvancements {
private static final Logger LOGGER = LogUtils.getLogger();
- private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().create();
+ private static final Gson GSON = (new GsonBuilder()).create(); // Paper - Remove pretty printing from advancements
private final PlayerList playerList;
private final Path playerSavePath;
private AdvancementTree tree;

View File

@@ -1,23 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nathan <nathanmaestas21@cnm.edu>
Date: Sat, 31 Aug 2024 18:09:55 -0600
Subject: [PATCH] Fire BlockExpEvent on grindstone use
diff --git a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/GrindstoneMenu.java
@@ -0,0 +0,0 @@ public class GrindstoneMenu extends AbstractContainerMenu {
public void onTake(net.minecraft.world.entity.player.Player player, ItemStack stack) {
context.execute((world, blockposition) -> {
if (world instanceof ServerLevel) {
- ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), this.getExperienceAmount(world), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player); // Paper
+ // Paper start - Fire BlockExpEvent on grindstone use
+ org.bukkit.event.block.BlockExpEvent event = new org.bukkit.event.block.BlockExpEvent(org.bukkit.craftbukkit.block.CraftBlock.at(world, blockposition), this.getExperienceAmount(world));
+ event.callEvent();
+ ExperienceOrb.award((ServerLevel) world, Vec3.atCenterOf(blockposition), event.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.GRINDSTONE, player);
+ // Paper end - Fire BlockExpEvent on grindstone use
}
world.levelEvent(1042, blockposition, 0);

View File

@@ -1,54 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tamion <70228790+notTamion@users.noreply.github.com>
Date: Sun, 26 May 2024 22:20:21 +0200
Subject: [PATCH] Fix CraftBukkit drag system
== AT ==
public net.minecraft.world.inventory.AbstractContainerMenu quickcraftSlots
public net.minecraft.world.inventory.AbstractContainerMenu quickcraftStatus
public net.minecraft.world.inventory.AbstractContainerMenu quickcraftType
public net.minecraft.world.inventory.AbstractContainerMenu resetQuickCraft()V
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
}
break;
case QUICK_CRAFT:
+ // Paper start - Fix CraftBukkit drag system
+ AbstractContainerMenu containerMenu = this.player.containerMenu;
+ int currentStatus = this.player.containerMenu.quickcraftStatus;
+ int newStatus = AbstractContainerMenu.getQuickcraftHeader(packet.getButtonNum());
+ if ((currentStatus != 1 || newStatus != 2 && currentStatus != newStatus)) {
+ } else if (containerMenu.getCarried().isEmpty()) {
+ } else if (newStatus == 0) {
+ } else if (newStatus == 1) {
+ } else if (newStatus == 2) {
+ if (!this.player.containerMenu.quickcraftSlots.isEmpty()) {
+ if (this.player.containerMenu.quickcraftSlots.size() == 1) {
+ int index = containerMenu.quickcraftSlots.iterator().next().index;
+ containerMenu.resetQuickCraft();
+ this.handleContainerClick(new ServerboundContainerClickPacket(packet.getContainerId(), packet.getStateId(), index, containerMenu.quickcraftType, net.minecraft.world.inventory.ClickType.PICKUP, packet.getCarriedItem(), packet.getChangedSlots()));
+ return;
+ }
+ }
+ }
+ // Paper end - Fix CraftBukkit drag system
this.player.containerMenu.clicked(packet.getSlotNum(), packet.getButtonNum(), packet.getClickType(), this.player);
break;
case PICKUP_ALL:
diff --git a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/src/main/java/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -0,0 +0,0 @@ public abstract class AbstractContainerMenu {
}
} else if (this.quickcraftStatus == 2) {
if (!this.quickcraftSlots.isEmpty()) {
- if (false && this.quickcraftSlots.size() == 1) { // CraftBukkit - treat everything as a drag since we are unable to easily call InventoryClickEvent instead
+ if (this.quickcraftSlots.size() == 1) { // Paper - Fix CraftBukkit drag system
k = ((Slot) this.quickcraftSlots.iterator().next()).index;
this.resetQuickCraft();
this.doClick(k, this.quickcraftType, ClickType.PICKUP, player);

View File

@@ -1,353 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Tamion <70228790+notTamion@users.noreply.github.com>
Date: Mon, 19 Aug 2024 18:05:26 +0200
Subject: [PATCH] Fix InventoryOpenEvent cancellation
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -0,0 +0,0 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player {
} else if (factory instanceof ChestBlock.DoubleInventory) {
// SPIGOT-5355 - double chests too :(
((ChestBlock.DoubleInventory) factory).inventorylargechest.stopOpen(this);
+ // Paper start - Fix InventoryOpenEvent cancellation
+ } else if (!this.enderChestInventory.isActiveChest(null)) {
+ this.enderChestInventory.stopOpen(this);
+ // Paper end - Fix InventoryOpenEvent cancellation
}
return OptionalInt.empty();
}
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -0,0 +0,0 @@ public class ServerPlayerGameMode {
} else if (this.gameModeForPlayer == GameType.SPECTATOR) {
MenuProvider itileinventory = iblockdata.getMenuProvider(world, blockposition);
- if (itileinventory != null) {
- player.openMenu(itileinventory);
+ if (itileinventory != null && player.openMenu(itileinventory).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
return InteractionResult.CONSUME;
} else {
return InteractionResult.PASS;
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/AbstractChestBoat.java b/src/main/java/net/minecraft/world/entity/vehicle/AbstractChestBoat.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/AbstractChestBoat.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/AbstractChestBoat.java
@@ -0,0 +0,0 @@ public abstract class AbstractChestBoat extends AbstractBoat implements HasCusto
@Override
public void openCustomInventoryScreen(Player player) {
- player.openMenu(this);
+ // Paper - fix inventory open cancel - moved into below if
Level world = player.level();
- if (world instanceof ServerLevel worldserver) {
+ if (world instanceof ServerLevel worldserver && player.openMenu(this).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
this.gameEvent(GameEvent.CONTAINER_OPEN, player);
PiglinAi.angerNearbyPiglins(worldserver, player, true);
}
diff --git a/src/main/java/net/minecraft/world/entity/vehicle/ContainerEntity.java b/src/main/java/net/minecraft/world/entity/vehicle/ContainerEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/vehicle/ContainerEntity.java
+++ b/src/main/java/net/minecraft/world/entity/vehicle/ContainerEntity.java
@@ -0,0 +0,0 @@ public interface ContainerEntity extends Container, MenuProvider {
}
default InteractionResult interactWithContainerVehicle(Player player) {
- player.openMenu(this);
+ // Paper start - Fix InventoryOpenEvent cancellation
+ if (player.openMenu(this).isEmpty()) {
+ return InteractionResult.PASS;
+ }
+ // Paper end - Fix InventoryOpenEvent cancellation
return InteractionResult.SUCCESS;
}
diff --git a/src/main/java/net/minecraft/world/level/block/AnvilBlock.java b/src/main/java/net/minecraft/world/level/block/AnvilBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/AnvilBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/AnvilBlock.java
@@ -0,0 +0,0 @@ public class AnvilBlock extends FallingBlock {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (!world.isClientSide) {
- player.openMenu(state.getMenuProvider(world, pos));
+ if (player.openMenu(state.getMenuProvider(world, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_ANVIL);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
return InteractionResult.SUCCESS;
diff --git a/src/main/java/net/minecraft/world/level/block/BarrelBlock.java b/src/main/java/net/minecraft/world/level/block/BarrelBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/BarrelBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/BarrelBlock.java
@@ -0,0 +0,0 @@ public class BarrelBlock extends BaseEntityBlock {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
- if (world instanceof ServerLevel serverLevel && world.getBlockEntity(pos) instanceof BarrelBlockEntity barrelBlockEntity) {
- player.openMenu(barrelBlockEntity);
+ if (world instanceof ServerLevel serverLevel && world.getBlockEntity(pos) instanceof BarrelBlockEntity barrelBlockEntity && player.openMenu(barrelBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.OPEN_BARREL);
PiglinAi.angerNearbyPiglins(serverLevel, player, true);
}
diff --git a/src/main/java/net/minecraft/world/level/block/BeaconBlock.java b/src/main/java/net/minecraft/world/level/block/BeaconBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/BeaconBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/BeaconBlock.java
@@ -0,0 +0,0 @@ public class BeaconBlock extends BaseEntityBlock implements BeaconBeamBlock {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
- if (!world.isClientSide && world.getBlockEntity(pos) instanceof BeaconBlockEntity beaconBlockEntity) {
- player.openMenu(beaconBlockEntity);
+ if (!world.isClientSide && world.getBlockEntity(pos) instanceof BeaconBlockEntity beaconBlockEntity && player.openMenu(beaconBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_BEACON);
}
diff --git a/src/main/java/net/minecraft/world/level/block/BlastFurnaceBlock.java b/src/main/java/net/minecraft/world/level/block/BlastFurnaceBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/BlastFurnaceBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/BlastFurnaceBlock.java
@@ -0,0 +0,0 @@ public class BlastFurnaceBlock extends AbstractFurnaceBlock {
@Override
protected void openContainer(Level world, BlockPos pos, Player player) {
BlockEntity blockEntity = world.getBlockEntity(pos);
- if (blockEntity instanceof BlastFurnaceBlockEntity) {
- player.openMenu((MenuProvider)blockEntity);
+ if (blockEntity instanceof BlastFurnaceBlockEntity && player.openMenu((MenuProvider)blockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_BLAST_FURNACE);
}
}
diff --git a/src/main/java/net/minecraft/world/level/block/BrewingStandBlock.java b/src/main/java/net/minecraft/world/level/block/BrewingStandBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/BrewingStandBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/BrewingStandBlock.java
@@ -0,0 +0,0 @@ public class BrewingStandBlock extends BaseEntityBlock {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
- if (!world.isClientSide && world.getBlockEntity(pos) instanceof BrewingStandBlockEntity brewingStandBlockEntity) {
- player.openMenu(brewingStandBlockEntity);
+ if (!world.isClientSide && world.getBlockEntity(pos) instanceof BrewingStandBlockEntity brewingStandBlockEntity && player.openMenu(brewingStandBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_BREWINGSTAND);
}
diff --git a/src/main/java/net/minecraft/world/level/block/CartographyTableBlock.java b/src/main/java/net/minecraft/world/level/block/CartographyTableBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/CartographyTableBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/CartographyTableBlock.java
@@ -0,0 +0,0 @@ public class CartographyTableBlock extends Block {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (!world.isClientSide) {
- player.openMenu(state.getMenuProvider(world, pos));
+ if (player.openMenu(state.getMenuProvider(world, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_CARTOGRAPHY_TABLE);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
return InteractionResult.SUCCESS;
diff --git a/src/main/java/net/minecraft/world/level/block/ChestBlock.java b/src/main/java/net/minecraft/world/level/block/ChestBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/ChestBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/ChestBlock.java
@@ -0,0 +0,0 @@ public class ChestBlock extends AbstractChestBlock<ChestBlockEntity> implements
if (world instanceof ServerLevel worldserver) {
MenuProvider itileinventory = this.getMenuProvider(state, world, pos);
- if (itileinventory != null) {
- player.openMenu(itileinventory);
+ if (itileinventory != null && player.openMenu(itileinventory).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(this.getOpenChestStat());
PiglinAi.angerNearbyPiglins(worldserver, player, true);
}
diff --git a/src/main/java/net/minecraft/world/level/block/CraftingTableBlock.java b/src/main/java/net/minecraft/world/level/block/CraftingTableBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/CraftingTableBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/CraftingTableBlock.java
@@ -0,0 +0,0 @@ public class CraftingTableBlock extends Block {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (!world.isClientSide) {
- player.openMenu(state.getMenuProvider(world, pos));
+ if (player.openMenu(state.getMenuProvider(world, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_CRAFTING_TABLE);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
return InteractionResult.SUCCESS;
diff --git a/src/main/java/net/minecraft/world/level/block/DispenserBlock.java b/src/main/java/net/minecraft/world/level/block/DispenserBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/DispenserBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/DispenserBlock.java
@@ -0,0 +0,0 @@ public class DispenserBlock extends BaseEntityBlock {
if (tileentity instanceof DispenserBlockEntity) {
DispenserBlockEntity tileentitydispenser = (DispenserBlockEntity) tileentity;
- player.openMenu(tileentitydispenser);
+ if (player.openMenu(tileentitydispenser).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(tileentitydispenser instanceof DropperBlockEntity ? Stats.INSPECT_DROPPER : Stats.INSPECT_DISPENSER);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
}
diff --git a/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java b/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/EnderChestBlock.java
@@ -0,0 +0,0 @@ public class EnderChestBlock extends AbstractChestBlock<EnderChestBlockEntity> i
if (world.getBlockState(blockPos).isRedstoneConductor(world, blockPos)) { // Paper - diff on change; make sure that EnderChest#isBlocked uses the same logic
return InteractionResult.SUCCESS;
} else {
- if (world instanceof ServerLevel serverLevel) {
- playerEnderChestContainer.setActiveChest(enderChestBlockEntity);
- player.openMenu(
- new SimpleMenuProvider((i, inventory, playerx) -> ChestMenu.threeRows(i, inventory, playerEnderChestContainer), CONTAINER_TITLE)
- );
+ // Paper start - Fix InventoryOpenEvent cancellation - moved up;
+ playerEnderChestContainer.setActiveChest(enderChestBlockEntity); // Needs to happen before ChestMenu.threeRows as it is required for opening animations
+ if (world instanceof ServerLevel serverLevel && player.openMenu(
+ new SimpleMenuProvider((i, inventory, playerx) -> ChestMenu.threeRows(i, inventory, playerEnderChestContainer), CONTAINER_TITLE)
+ ).isPresent()) {
+ // Paper end - Fix InventoryOpenEvent cancellation - moved up;
+ // Paper - Fix InventoryOpenEvent cancellation - moved up;
player.awardStat(Stats.OPEN_ENDERCHEST);
PiglinAi.angerNearbyPiglins(serverLevel, player, true);
}
diff --git a/src/main/java/net/minecraft/world/level/block/FurnaceBlock.java b/src/main/java/net/minecraft/world/level/block/FurnaceBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/FurnaceBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/FurnaceBlock.java
@@ -0,0 +0,0 @@ public class FurnaceBlock extends AbstractFurnaceBlock {
@Override
protected void openContainer(Level world, BlockPos pos, Player player) {
BlockEntity blockEntity = world.getBlockEntity(pos);
- if (blockEntity instanceof FurnaceBlockEntity) {
- player.openMenu((MenuProvider)blockEntity);
+ if (blockEntity instanceof FurnaceBlockEntity && player.openMenu((MenuProvider)blockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_FURNACE);
}
}
diff --git a/src/main/java/net/minecraft/world/level/block/GrindstoneBlock.java b/src/main/java/net/minecraft/world/level/block/GrindstoneBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/GrindstoneBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/GrindstoneBlock.java
@@ -0,0 +0,0 @@ public class GrindstoneBlock extends FaceAttachedHorizontalDirectionalBlock {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (!world.isClientSide) {
- player.openMenu(state.getMenuProvider(world, pos));
+ if (player.openMenu(state.getMenuProvider(world, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_GRINDSTONE);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
return InteractionResult.SUCCESS;
diff --git a/src/main/java/net/minecraft/world/level/block/HopperBlock.java b/src/main/java/net/minecraft/world/level/block/HopperBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/HopperBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/HopperBlock.java
@@ -0,0 +0,0 @@ public class HopperBlock extends BaseEntityBlock {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
- if (!world.isClientSide && world.getBlockEntity(pos) instanceof HopperBlockEntity hopperBlockEntity) {
- player.openMenu(hopperBlockEntity);
+ if (!world.isClientSide && world.getBlockEntity(pos) instanceof HopperBlockEntity hopperBlockEntity && player.openMenu(hopperBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INSPECT_HOPPER);
}
diff --git a/src/main/java/net/minecraft/world/level/block/LecternBlock.java b/src/main/java/net/minecraft/world/level/block/LecternBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/LecternBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/LecternBlock.java
@@ -0,0 +0,0 @@ public class LecternBlock extends BaseEntityBlock {
private void openScreen(Level world, BlockPos pos, Player player) {
BlockEntity tileentity = world.getBlockEntity(pos);
- if (tileentity instanceof LecternBlockEntity) {
- player.openMenu((LecternBlockEntity) tileentity);
+ if (tileentity instanceof LecternBlockEntity && player.openMenu((LecternBlockEntity) tileentity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_LECTERN);
}
diff --git a/src/main/java/net/minecraft/world/level/block/LoomBlock.java b/src/main/java/net/minecraft/world/level/block/LoomBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/LoomBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/LoomBlock.java
@@ -0,0 +0,0 @@ public class LoomBlock extends HorizontalDirectionalBlock {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (!world.isClientSide) {
- player.openMenu(state.getMenuProvider(world, pos));
+ if (player.openMenu(state.getMenuProvider(world, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_LOOM);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
return InteractionResult.SUCCESS;
diff --git a/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java b/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/ShulkerBoxBlock.java
@@ -0,0 +0,0 @@ public class ShulkerBoxBlock extends BaseEntityBlock {
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (world instanceof ServerLevel serverLevel
&& world.getBlockEntity(pos) instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity
- && canOpen(state, world, pos, shulkerBoxBlockEntity)) {
- player.openMenu(shulkerBoxBlockEntity);
+ && canOpen(state, world, pos, shulkerBoxBlockEntity) // Paper - Fix InventoryOpenEvent cancellation - expand if for belows check
+ && player.openMenu(shulkerBoxBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.OPEN_SHULKER_BOX);
PiglinAi.angerNearbyPiglins(serverLevel, player, true);
}
diff --git a/src/main/java/net/minecraft/world/level/block/SmithingTableBlock.java b/src/main/java/net/minecraft/world/level/block/SmithingTableBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/SmithingTableBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/SmithingTableBlock.java
@@ -0,0 +0,0 @@ public class SmithingTableBlock extends CraftingTableBlock {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (!world.isClientSide) {
- player.openMenu(state.getMenuProvider(world, pos));
+ if (player.openMenu(state.getMenuProvider(world, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_SMITHING_TABLE);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
return InteractionResult.SUCCESS;
diff --git a/src/main/java/net/minecraft/world/level/block/SmokerBlock.java b/src/main/java/net/minecraft/world/level/block/SmokerBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/SmokerBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/SmokerBlock.java
@@ -0,0 +0,0 @@ public class SmokerBlock extends AbstractFurnaceBlock {
@Override
protected void openContainer(Level world, BlockPos pos, Player player) {
BlockEntity blockEntity = world.getBlockEntity(pos);
- if (blockEntity instanceof SmokerBlockEntity) {
- player.openMenu((MenuProvider)blockEntity);
+ if (blockEntity instanceof SmokerBlockEntity && player.openMenu((MenuProvider)blockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_SMOKER);
}
}
diff --git a/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java b/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java
+++ b/src/main/java/net/minecraft/world/level/block/StonecutterBlock.java
@@ -0,0 +0,0 @@ public class StonecutterBlock extends Block {
@Override
protected InteractionResult useWithoutItem(BlockState state, Level world, BlockPos pos, Player player, BlockHitResult hit) {
if (!world.isClientSide) {
- player.openMenu(state.getMenuProvider(world, pos));
+ if (player.openMenu(state.getMenuProvider(world, pos)).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
player.awardStat(Stats.INTERACT_WITH_STONECUTTER);
+ } // Paper - Fix InventoryOpenEvent cancellation
}
return InteractionResult.SUCCESS;

View File

@@ -1,52 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nassim Jahnke <nassim@njahnke.dev>
Date: Sat, 21 Sep 2024 22:01:52 +0200
Subject: [PATCH] Fix PlayerCommandPreprocessEvent on signed commands
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(this.getCraftPlayer(), command, new LazyPlayerSet(this.server));
this.cserver.getPluginManager().callEvent(event);
-
- if (event.isCancelled()) {
- return;
- }
command = event.getMessage().substring(1);
- ParseResults<CommandSourceStack> parseresults = this.parseCommand(command);
- // CraftBukkit end
-
- Map map;
+ // Paper start - Fix cancellation and message changing
+ ParseResults<CommandSourceStack> parseresults = this.parseCommand(packet.command());
+ Map<String, PlayerChatMessage> map;
try {
- map = (packet.command().equals(command)) ? this.collectSignedArguments(packet, SignableCommand.of(parseresults), lastSeenMessages) : Collections.emptyMap(); // CraftBukkit
+ // Always parse the original command to add to the chat chain
+ map = this.collectSignedArguments(packet, SignableCommand.of(parseresults), lastSeenMessages);
} catch (SignedMessageChain.DecodeException signedmessagechain_a) {
this.handleMessageDecodeFailure(signedmessagechain_a);
return;
}
+ if (event.isCancelled()) {
+ // Only now are we actually good to return
+ return;
+ }
+
+ // Remove signed parts if the command was changed
+ if (!command.equals(packet.command())) {
+ parseresults = this.parseCommand(command);
+ map = Collections.emptyMap();
+ }
+ // Paper end - Fix cancellation and message changing
+
CommandSigningContext.SignedArguments commandsigningcontext_a = new CommandSigningContext.SignedArguments(map);
parseresults = Commands.<CommandSourceStack>mapSource(parseresults, (commandlistenerwrapper) -> { // CraftBukkit - decompile error

View File

@@ -1,39 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Mon, 19 Aug 2024 13:43:06 -0700
Subject: [PATCH] Fix SculkBloomEvent firing for block entity loading
diff --git a/src/main/java/net/minecraft/world/level/block/SculkSpreader.java b/src/main/java/net/minecraft/world/level/block/SculkSpreader.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/SculkSpreader.java
+++ b/src/main/java/net/minecraft/world/level/block/SculkSpreader.java
@@ -0,0 +0,0 @@ public class SculkSpreader {
int i = Math.min(list.size(), 32);
for (int j = 0; j < i; ++j) {
- this.addCursor((SculkSpreader.ChargeCursor) list.get(j));
+ this.addCursor((SculkSpreader.ChargeCursor) list.get(j), false); // Paper - don't fire event for block entity loading
}
}
@@ -0,0 +0,0 @@ public class SculkSpreader {
while (charge > 0) {
int j = Math.min(charge, 1000);
- this.addCursor(new SculkSpreader.ChargeCursor(pos, j));
+ this.addCursor(new SculkSpreader.ChargeCursor(pos, j), true); // Paper - allow firing event for other causes
charge -= j;
}
}
- private void addCursor(SculkSpreader.ChargeCursor cursor) {
+ private void addCursor(SculkSpreader.ChargeCursor cursor, boolean fireEvent) { // Paper - add boolean to conditionally fire SculkBloomEvent
if (this.cursors.size() < 32) {
// CraftBukkit start
- if (!this.isWorldGeneration()) { // CraftBukkit - SPIGOT-7475: Don't call event during world generation
+ if (!this.isWorldGeneration() && fireEvent) { // CraftBukkit - SPIGOT-7475: Don't call event during world generation // Paper - add boolean to conditionally fire SculkBloomEvent
CraftBlock bukkitBlock = CraftBlock.at(this.level, cursor.pos);
SculkBloomEvent event = new SculkBloomEvent(bukkitBlock, cursor.getCharge());
Bukkit.getPluginManager().callEvent(event);

View File

@@ -1,27 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Gegy <gegy.dev@gmail.com>
Date: Mon, 26 Aug 2024 19:45:07 +0200
Subject: [PATCH] Fix: synchronise sending chat to client with updating message
signature cache
In the case where multiple messages from different players are being processed in parallel, there was a potential race condition where the messages would be sent to the client in a different order than the message signature cache was updated. However, the cache relies on the fact that the client and server get the exact same updates in the same order. This race condition would cause the caches to become corrupted, and any future message received by the client would fail to validate.
This also applies to the last seen state of the server, which becomes inconsistent in the same way as the message signature cache and would cause any messages sent to be rejected by the server too.
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -0,0 +0,0 @@ public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl
return;
}
// CraftBukkit end
+ // Paper start - Ensure that client receives chat packets in the same order that we add into the message signature cache
+ synchronized (this.messageSignatureCache) {
this.send(new ClientboundPlayerChatPacket(message.link().sender(), message.link().index(), message.signature(), message.signedBody().pack(this.messageSignatureCache), message.unsignedContent(), message.filterMask(), params));
this.addPendingMessage(message);
+ }
+ // Paper end - Ensure that client receives chat packets in the same order that we add into the message signature cache
}
public void sendDisguisedChatMessage(Component message, ChatType.Bound params) {

View File

@@ -1,154 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Fri, 27 Sep 2024 17:13:16 -0700
Subject: [PATCH] Improve entity effect API
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -0,0 +0,0 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
return this.getHandle().getScoreboardName();
}
// Paper end - entity scoreboard name
+
+ // Paper start - broadcast hurt animation
+ @Override
+ public void broadcastHurtAnimation(java.util.Collection<Player> players) {
+ //noinspection SuspiciousMethodCalls
+ Preconditions.checkArgument(!players.contains(this), "Cannot broadcast hurt animation to self without a yaw");
+ for (final org.bukkit.entity.Player player : players) {
+ ((CraftPlayer) player).sendHurtAnimation(0, this);
+ }
+ }
+ // Paper end - broadcast hurt animation
}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -0,0 +0,0 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public void sendHurtAnimation(float yaw) {
+ // Paper start - Add target entity to sendHurtAnimation
+ this.sendHurtAnimation(yaw, this);
+ }
+ public void sendHurtAnimation(float yaw, org.bukkit.entity.Entity target) {
+ // Paper end - Add target entity to sendHurtAnimation
if (this.getHandle().connection == null) {
return;
}
@@ -0,0 +0,0 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
* This makes no sense. We'll add 90 to it so that 0 = front, clockwise from there.
*/
float actualYaw = yaw + 90;
- this.getHandle().connection.send(new ClientboundHurtAnimationPacket(this.getEntityId(), actualYaw));
+ this.getHandle().connection.send(new ClientboundHurtAnimationPacket(target.getEntityId(), actualYaw)); // Paper - Add target entity to sendHurtAnimation
}
@Override
@@ -0,0 +0,0 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
public void setSendViewDistance(final int viewDistance) {
throw new UnsupportedOperationException("Not implemented yet");
}
+
+ // Paper start - entity effect API
+ @Override
+ public void sendEntityEffect(final org.bukkit.EntityEffect effect, final org.bukkit.entity.Entity target) {
+ if (this.getHandle().connection == null || !effect.isApplicableTo(target)) {
+ return;
+ }
+ this.getHandle().connection.send(new net.minecraft.network.protocol.game.ClientboundEntityEventPacket(((CraftEntity) target).getHandle(), effect.getData()));
+ }
+ // Paper end - entity effect API
}
diff --git a/src/test/java/org/bukkit/EntityEffectTest.java b/src/test/java/org/bukkit/EntityEffectTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/test/java/org/bukkit/EntityEffectTest.java
@@ -0,0 +0,0 @@
+package org.bukkit;
+
+import com.google.common.base.Joiner;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import net.minecraft.world.entity.EntityEvent;
+import org.bukkit.support.environment.Normal;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+@Normal
+public class EntityEffectTest {
+
+ private static List<Byte> collectNmsLevelEvents() throws ReflectiveOperationException {
+ final List<Byte> events = new ArrayList<>();
+ for (final Field field : EntityEvent.class.getFields()) {
+ if (Modifier.isStatic(field.getModifiers()) && Modifier.isFinal(field.getModifiers()) && field.getType() == byte.class) {
+ events.add((byte) field.get(null));
+ }
+ }
+ for (int i = 22; i <= 28; i++) {
+ events.remove(Byte.valueOf((byte) i)); // all have existing API (debug info and op level)
+ }
+ events.remove(Byte.valueOf(EntityEvent.STOP_ATTACKING)); // not used on client anywhere
+ events.remove(Byte.valueOf(EntityEvent.USE_ITEM_COMPLETE)); // not suitable for API (complete using item on Player)
+ events.remove(Byte.valueOf(EntityEvent.FISHING_ROD_REEL_IN)); // not suitable for API (fishing rod reel in on FishingHook)
+ events.add((byte) 0); // handled on Arrow (for some reason it's not in the EntityEvent nms file as a constant)
+ return events;
+ }
+
+ private static boolean isNotDeprecated(EntityEffect effect) throws ReflectiveOperationException {
+ return !EntityEffect.class.getDeclaredField(effect.name()).isAnnotationPresent(Deprecated.class);
+ }
+
+ @Test
+ public void checkAllApiExists() throws ReflectiveOperationException {
+ Map<Byte, EntityEffect> toId = new HashMap<>();
+ for (final EntityEffect effect : EntityEffect.values()) {
+ if (isNotDeprecated(effect)) {
+ toId.put(effect.getData(), effect);
+ }
+ }
+
+ final Set<Byte> missingEvents = new HashSet<>();
+ for (final Byte event : collectNmsLevelEvents()) {
+ if (toId.get(event) == null) {
+ missingEvents.add(event);
+ }
+ }
+ if (!missingEvents.isEmpty()) {
+ fail("Missing API EntityEffects:\n" + Joiner.on("\n").join(missingEvents));
+ }
+ }
+
+ @Test
+ public void checkNoExtraApi() throws ReflectiveOperationException {
+ Map<Byte, EntityEffect> toId = new HashMap<>();
+ for (final EntityEffect effect : EntityEffect.values()) {
+ if (isNotDeprecated(effect)) {
+ toId.put(effect.getData(), effect);
+ }
+ }
+
+ final List<Byte> nmsEvents = collectNmsLevelEvents();
+ final Set<EntityEffect> extraApiEffects = new HashSet<>();
+ for (final Map.Entry<Byte, EntityEffect> entry : toId.entrySet()) {
+ if (!nmsEvents.contains(entry.getKey())) {
+ extraApiEffects.add(entry.getValue());
+ }
+ }
+ if (!extraApiEffects.isEmpty()) {
+ fail("Extra API EntityEffects:\n" + Joiner.on("\n").join(extraApiEffects));
+ }
+ }
+}

View File

@@ -1,73 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: masmc05 <masmc05@gmail.com>
Date: Sun, 11 Aug 2024 03:01:52 +0300
Subject: [PATCH] Item serialization as json
diff --git a/src/main/java/net/minecraft/world/item/component/CustomData.java b/src/main/java/net/minecraft/world/item/component/CustomData.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/item/component/CustomData.java
+++ b/src/main/java/net/minecraft/world/item/component/CustomData.java
@@ -0,0 +0,0 @@ import org.slf4j.Logger;
public final class CustomData {
private static final Logger LOGGER = LogUtils.getLogger();
public static final CustomData EMPTY = new CustomData(new CompoundTag());
- public static final Codec<CustomData> CODEC = Codec.withAlternative(CompoundTag.CODEC, TagParser.AS_CODEC)
+ // Paper start - Item serialization as json
+ public static ThreadLocal<Boolean> SERIALIZE_CUSTOM_AS_SNBT = ThreadLocal.withInitial(() -> false);
+ public static final Codec<CustomData> CODEC = Codec.either(CompoundTag.CODEC, TagParser.AS_CODEC)
+ .xmap(com.mojang.datafixers.util.Either::unwrap, data -> { // Both will be used for deserialization, but we decide which one to use for serialization
+ if (!SERIALIZE_CUSTOM_AS_SNBT.get()) {
+ return com.mojang.datafixers.util.Either.left(data); // First codec
+ } else {
+ return com.mojang.datafixers.util.Either.right(data); // Second codec
+ }
+ })
+ // Paper end - Item serialization as json
.xmap(CustomData::new, component -> component.tag);
public static final Codec<CustomData> CODEC_WITH_ID = CODEC.validate(
component -> component.getUnsafe().contains("id", 8) ? DataResult.success(component) : DataResult.error(() -> "Missing id for entity in: " + component)
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -0,0 +0,0 @@ public final class CraftMagicNumbers implements UnsafeValues {
return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.parse(MinecraftServer.getServer().registryAccess(), compound).orElseThrow());
}
+ @Override
+ public com.google.gson.JsonObject serializeItemAsJson(ItemStack itemStack) {
+ Preconditions.checkNotNull(itemStack, "Cannot serialize empty ItemStack");
+ Preconditions.checkArgument(!itemStack.isEmpty(), "Cannot serialize empty ItemStack");
+
+ net.minecraft.core.RegistryAccess.Frozen reg = net.minecraft.server.MinecraftServer.getServer().registryAccess();
+ com.mojang.serialization.DynamicOps<com.google.gson.JsonElement> ops = reg.createSerializationContext(com.mojang.serialization.JsonOps.INSTANCE);
+ com.google.gson.JsonObject item;
+ // Serialize as SNBT to preserve exact NBT types; vanilla codecs already can handle such deserialization.
+ net.minecraft.world.item.component.CustomData.SERIALIZE_CUSTOM_AS_SNBT.set(true);
+ try {
+ item = net.minecraft.world.item.ItemStack.CODEC.encodeStart(ops, CraftItemStack.unwrap(itemStack)).getOrThrow().getAsJsonObject();
+ } finally {
+ net.minecraft.world.item.component.CustomData.SERIALIZE_CUSTOM_AS_SNBT.set(false);
+ }
+ item.addProperty("DataVersion", this.getDataVersion());
+ return item;
+ }
+
+ @Override
+ public ItemStack deserializeItemFromJson(com.google.gson.JsonObject data) throws IllegalArgumentException {
+ Preconditions.checkNotNull(data, "null cannot be deserialized");
+
+ final int dataVersion = data.get("DataVersion").getAsInt();
+ final int currentVersion = org.bukkit.craftbukkit.util.CraftMagicNumbers.INSTANCE.getDataVersion();
+ data = ca.spottedleaf.dataconverter.minecraft.MCDataConverter.convertJson(
+ ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.ITEM_STACK,
+ data, false, dataVersion, currentVersion
+ );
+ com.mojang.serialization.DynamicOps<com.google.gson.JsonElement> ops = MinecraftServer.getServer().registryAccess().createSerializationContext(com.mojang.serialization.JsonOps.INSTANCE);
+ return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.CODEC.parse(ops, data).getOrThrow(IllegalArgumentException::new));
+ }
+
@Override
public byte[] serializeEntity(org.bukkit.entity.Entity entity) {
Preconditions.checkNotNull(entity, "null cannot be serialized");

View File

@@ -1,41 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Newwind <support@newwindserver.com>
Date: Thu, 22 Aug 2024 22:55:37 +0200
Subject: [PATCH] Remove set damage lootable item function from compasses
In VanillaChestLoot, compasses accidentally have a setdamage loot
function on them, but compasses don't take durability, resulting in a warning.
This patch simply removes attempting to add damage to the compass item.
diff --git a/src/main/java/net/minecraft/data/loot/packs/VanillaChestLoot.java b/src/main/java/net/minecraft/data/loot/packs/VanillaChestLoot.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/data/loot/packs/VanillaChestLoot.java
+++ b/src/main/java/net/minecraft/data/loot/packs/VanillaChestLoot.java
@@ -0,0 +0,0 @@ public record VanillaChestLoot(HolderLookup.Provider registries) implements Loot
.add(
LootItem.lootTableItem(Items.COMPASS)
.apply(SetItemCountFunction.setCount(ConstantValue.exactly(1.0F)))
- .apply(SetItemDamageFunction.setDamage(UniformGenerator.between(0.15F, 0.8F)))
.setWeight(1)
)
.add(LootItem.lootTableItem(Items.BUCKET).apply(SetItemCountFunction.setCount(UniformGenerator.between(1.0F, 2.0F))).setWeight(1))
diff --git a/src/main/resources/data/minecraft/loot_table/chests/trial_chambers/intersection_barrel.json b/src/main/resources/data/minecraft/loot_table/chests/trial_chambers/intersection_barrel.json
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/resources/data/minecraft/loot_table/chests/trial_chambers/intersection_barrel.json
+++ b/src/main/resources/data/minecraft/loot_table/chests/trial_chambers/intersection_barrel.json
@@ -0,0 +0,0 @@
"add": false,
"count": 1.0,
"function": "minecraft:set_count"
- },
- {
- "add": false,
- "damage": {
- "type": "minecraft:uniform",
- "max": 0.8,
- "min": 0.15
- },
- "function": "minecraft:set_damage"
}
],
"name": "minecraft:compass"

View File

@@ -1,163 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bjarne Koll <git@lynxplay.dev>
Date: Mon, 16 Sep 2024 23:07:29 +0200
Subject: [PATCH] Remove wall-time / unused skip tick protection
Spigot still maintains some partial implementation of "tick skipping", a
practice in which the MinecraftServer.currentTick field is updated not
by an increment of one per actual tick, but instead set to
System.currentTimeMillis() / 50. This behaviour means that the tracked
tick may "skip" a tick value in case a previous tick took more than the
expected 50ms.
To compensate for this in important paths, spigot/craftbukkit
implements "wall-time". Instead of incrementing/decrementing ticks on
block entities/entities by one for each call to their tick() method,
they instead increment/decrement important values, like
an ItemEntity's age or pickupDelay, by the difference of
`currentTick - lastTick`, where `lastTick` is the value of
`currentTick` during the last tick() call.
These "fixes" however do not play nicely with minecraft's simulation
distance as entities/block entities implementing the above behaviour
would "catch up" their values when moving from a non-ticking chunk to a
ticking one as their `lastTick` value remains stuck on the last tick in
a ticking chunk and hence lead to a large "catch up" once ticked again.
Paper completely removes the "tick skipping" behaviour (See patch
"Further-improve-server-tick-loop"), making the above precautions
completely unnecessary, which also rids paper of the previous described
incompatibility with non-ticking chunks.
diff --git a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
+++ b/src/main/java/net/minecraft/world/entity/item/ItemEntity.java
@@ -0,0 +0,0 @@ public class ItemEntity extends Entity implements TraceableEntity {
@Nullable
public UUID target;
public final float bobOffs;
- private int lastTick = MinecraftServer.currentTick - 1; // CraftBukkit
+ // private int lastTick = MinecraftServer.currentTick - 1; // CraftBukkit // Paper - remove anti tick skipping measures / wall time
public boolean canMobPickup = true; // Paper - Item#canEntityPickup
private int despawnRate = -1; // Paper - Alternative item-despawn-rate
public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API
@@ -0,0 +0,0 @@ public class ItemEntity extends Entity implements TraceableEntity {
this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
} else {
super.tick();
- // CraftBukkit start - Use wall time for pickup and despawn timers
- int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
- if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
- if (this.age != -32768) this.age += elapsedTicks;
- this.lastTick = MinecraftServer.currentTick;
- // CraftBukkit end
+ // Paper start - remove anti tick skipping measures / wall time - revert to vanilla
+ if (this.pickupDelay > 0 && this.pickupDelay != 32767) {
+ --this.pickupDelay;
+ }
+ // Paper end - remove anti tick skipping measures / wall time - revert to vanilla
this.xo = this.getX();
this.yo = this.getY();
@@ -0,0 +0,0 @@ public class ItemEntity extends Entity implements TraceableEntity {
this.mergeWithNeighbours();
}
- /* CraftBukkit start - moved up
+ // Paper - remove anti tick skipping measures / wall time - revert to vanilla /* CraftBukkit start - moved up
if (this.age != -32768) {
++this.age;
}
@@ -0,0 +0,0 @@ public class ItemEntity extends Entity implements TraceableEntity {
// Spigot start - copied from above
@Override
public void inactiveTick() {
- // CraftBukkit start - Use wall time for pickup and despawn timers
- int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
- if (this.pickupDelay != 32767) this.pickupDelay -= elapsedTicks;
- if (this.age != -32768) this.age += elapsedTicks;
- this.lastTick = MinecraftServer.currentTick;
- // CraftBukkit end
+ // Paper start - remove anti tick skipping measures / wall time - copied from above
+ if (this.pickupDelay > 0 && this.pickupDelay != 32767) {
+ --this.pickupDelay;
+ }
+ if (this.age != -32768) {
+ ++this.age;
+ }
+ // Paper end - remove anti tick skipping measures / wall time - copied from above
if (!this.level().isClientSide && this.age >= this.despawnRate) { // Spigot // Paper - Alternative item-despawn-rate
// CraftBukkit start - fire ItemDespawnEvent
diff --git a/src/main/java/net/minecraft/world/entity/monster/Zombie.java b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/monster/Zombie.java
+++ b/src/main/java/net/minecraft/world/entity/monster/Zombie.java
@@ -0,0 +0,0 @@ public class Zombie extends Monster {
private boolean canBreakDoors;
private int inWaterTime;
public int conversionTime;
- private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field
+ // private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field // Paper - remove anti tick skipping measures / wall time
private boolean shouldBurnInDay = true; // Paper - Add more Zombie API
public Zombie(EntityType<? extends Zombie> type, Level world) {
@@ -0,0 +0,0 @@ public class Zombie extends Monster {
public void tick() {
if (!this.level().isClientSide && this.isAlive() && !this.isNoAi()) {
if (this.isUnderWaterConverting()) {
- // CraftBukkit start - Use wall time instead of ticks for conversion
- int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
- this.conversionTime -= elapsedTicks;
- // CraftBukkit end
+ --this.conversionTime; // Paper - remove anti tick skipping measures / wall time
if (this.conversionTime < 0) {
this.doUnderWaterConversion();
}
@@ -0,0 +0,0 @@ public class Zombie extends Monster {
}
super.tick();
- this.lastTick = MinecraftServer.currentTick; // CraftBukkit
+ // this.lastTick = MinecraftServer.currentTick; // CraftBukkit // Paper - remove anti tick skipping measures / wall time
}
@Override
@@ -0,0 +0,0 @@ public class Zombie extends Monster {
}
// Paper end - Add more Zombie API
public void startUnderWaterConversion(int ticksUntilWaterConversion) {
- this.lastTick = MinecraftServer.currentTick; // CraftBukkit
+ // this.lastTick = MinecraftServer.currentTick; // CraftBukkit // Paper - remove anti tick skipping measures / wall time
this.conversionTime = ticksUntilWaterConversion;
this.getEntityData().set(Zombie.DATA_DROWNED_CONVERSION_ID, true);
}
diff --git a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
+++ b/src/main/java/net/minecraft/world/level/block/entity/BrewingStandBlockEntity.java
@@ -0,0 +0,0 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
public int fuel;
protected final ContainerData dataAccess;
// CraftBukkit start - add fields and methods
- private int lastTick = MinecraftServer.currentTick;
+ // private int lastTick = MinecraftServer.currentTick; // Paper - remove anti tick skipping measures / wall time
public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
private int maxStack = MAX_STACK;
@@ -0,0 +0,0 @@ public class BrewingStandBlockEntity extends BaseContainerBlockEntity implements
boolean flag1 = blockEntity.brewTime > 0;
ItemStack itemstack1 = (ItemStack) blockEntity.items.get(3);
- // CraftBukkit start - Use wall time instead of ticks for brewing
- int elapsedTicks = MinecraftServer.currentTick - blockEntity.lastTick;
- blockEntity.lastTick = MinecraftServer.currentTick;
+ // Paper - remove anti tick skipping measures / wall time
if (flag1) {
- blockEntity.brewTime -= elapsedTicks;
+ --blockEntity.brewTime; // Paper - remove anti tick skipping measures / wall time - revert to vanilla
boolean flag2 = blockEntity.brewTime <= 0; // == -> <=
// CraftBukkit end

View File

@@ -1,595 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jake Potrebic <jake.m.potrebic@gmail.com>
Date: Thu, 20 Jun 2024 09:40:57 -0700
Subject: [PATCH] Tag Lifecycle Events
== AT ==
public net/minecraft/tags/TagEntry id
public net/minecraft/tags/TagEntry tag
public net/minecraft/tags/TagEntry required
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/LifecycleEventTypeProviderImpl.java
@@ -0,0 +0,0 @@ public final class LifecycleEventTypeProviderImpl implements LifecycleEventTypeP
return (LifecycleEventTypeProviderImpl) LifecycleEventTypeProvider.provider();
}
+ private final PaperTagEventTypeProvider provider = new PaperTagEventTypeProvider();
+
@Override
public <O extends LifecycleEventOwner, E extends LifecycleEvent> LifecycleEventType.Monitorable<O, E> monitor(final String name, final Class<? extends O> ownerType) {
return new MonitorableLifecycleEventType<>(name, ownerType);
@@ -0,0 +0,0 @@ public final class LifecycleEventTypeProviderImpl implements LifecycleEventTypeP
public <O extends LifecycleEventOwner, E extends LifecycleEvent> LifecycleEventType.Prioritizable<O, E> prioritized(final String name, final Class<? extends O> ownerType) {
return new PrioritizableLifecycleEventType.Simple<>(name, ownerType);
}
+
+ @Override
+ public PaperTagEventTypeProvider tagProvider() {
+ return this.provider;
+ }
}
diff --git a/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PaperTagEventTypeProvider.java b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PaperTagEventTypeProvider.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/lifecycle/event/types/PaperTagEventTypeProvider.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.plugin.lifecycle.event.types;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.tag.PaperTagListenerManager;
+import io.papermc.paper.tag.PostFlattenTagRegistrar;
+import io.papermc.paper.tag.PreFlattenTagRegistrar;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public class PaperTagEventTypeProvider implements TagEventTypeProvider {
+
+ @Override
+ public <T> PrioritizableLifecycleEventType.Simple<BootstrapContext, ReloadableRegistrarEvent<PreFlattenTagRegistrar<T>>> preFlatten(final RegistryKey<T> registryKey) {
+ return PaperTagListenerManager.INSTANCE.getPreFlattenType(registryKey);
+ }
+
+ @Override
+ public <T> PrioritizableLifecycleEventType.Simple<BootstrapContext, ReloadableRegistrarEvent<PostFlattenTagRegistrar<T>>> postFlatten(final RegistryKey<T> registryKey) {
+ return PaperTagListenerManager.INSTANCE.getPostFlattenType(registryKey);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/tag/PaperPostFlattenTagRegistrar.java b/src/main/java/io/papermc/paper/tag/PaperPostFlattenTagRegistrar.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/tag/PaperPostFlattenTagRegistrar.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.tag;
+
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import io.papermc.paper.adventure.PaperAdventure;
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.registrar.PaperRegistrar;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.TypedKey;
+import io.papermc.paper.registry.tag.TagKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.function.Function;
+import net.minecraft.resources.ResourceLocation;
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@SuppressWarnings("BoundedWildcard")
+@DefaultQualifier(NonNull.class)
+public class PaperPostFlattenTagRegistrar<M, T> implements PaperRegistrar<BootstrapContext>, PostFlattenTagRegistrar<T> {
+
+ public final Map<ResourceLocation, List<M>> tags;
+ private final Function<ResourceLocation, Optional<? extends M>> fromIdConverter;
+ private final Function<M, ResourceLocation> toIdConverter;
+ private final RegistryKey<T> registryKey;
+
+ public PaperPostFlattenTagRegistrar(
+ final Map<ResourceLocation, List<M>> tags,
+ final TagEventConfig<M, T> config
+ ) {
+ this.tags = tags;
+ this.fromIdConverter = config.fromIdConverter();
+ this.toIdConverter = config.toIdConverter();
+ this.registryKey = config.apiRegistryKey();
+ }
+
+ @Override
+ public void setCurrentContext(final @Nullable BootstrapContext owner) {
+ }
+
+ @Override
+ public RegistryKey<T> registryKey() {
+ return this.registryKey;
+ }
+
+ @Override
+ public Map<TagKey<T>, Collection<TypedKey<T>>> getAllTags() {
+ final ImmutableMap.Builder<TagKey<T>, Collection<TypedKey<T>>> tags = ImmutableMap.builderWithExpectedSize(this.tags.size());
+ for (final Map.Entry<ResourceLocation, List<M>> entry : this.tags.entrySet()) {
+ final TagKey<T> key = TagKey.create(this.registryKey, CraftNamespacedKey.fromMinecraft(entry.getKey()));
+ tags.put(key, this.convert(entry.getValue()));
+ }
+ return tags.build();
+ }
+
+ private List<TypedKey<T>> convert(final List<M> nms) {
+ return Collections.unmodifiableList(
+ Lists.transform(nms, m -> this.convert(this.toIdConverter.apply(m)))
+ );
+ }
+
+ private TypedKey<T> convert(final ResourceLocation location) {
+ return TypedKey.create(this.registryKey, CraftNamespacedKey.fromMinecraft(location));
+ }
+
+ private M convert(final TypedKey<T> key) {
+ final Optional<? extends M> optional = this.fromIdConverter.apply(PaperAdventure.asVanilla(key.key()));
+ if (optional.isEmpty()) {
+ throw new IllegalArgumentException(key + " doesn't exist");
+ }
+ return optional.get();
+ }
+
+ @Override
+ public boolean hasTag(final TagKey<T> tagKey) {
+ return this.tags.containsKey(PaperAdventure.asVanilla(tagKey.key()));
+ }
+
+ private List<M> getNmsTag(final TagKey<T> tagKey, final boolean create) {
+ final ResourceLocation vanillaKey = PaperAdventure.asVanilla(tagKey.key());
+ List<M> tag = this.tags.get(vanillaKey);
+ if (tag == null) {
+ if (create) {
+ tag = this.tags.computeIfAbsent(vanillaKey, k -> new ArrayList<>());
+ } else {
+ throw new NoSuchElementException("Tag " + tagKey + " is not present");
+ }
+ }
+ return tag;
+ }
+
+ @Override
+ public Collection<TypedKey<T>> getTag(final TagKey<T> tagKey) {
+ return this.convert(this.getNmsTag(tagKey, false));
+ }
+
+ @Override
+ public void addToTag(final TagKey<T> tagKey, final Collection<TypedKey<T>> values) {
+ final List<M> nmsTag = new ArrayList<>(this.getNmsTag(tagKey, true));
+ for (final TypedKey<T> key : values) {
+ nmsTag.add(this.convert(key));
+ }
+ this.tags.put(PaperAdventure.asVanilla(tagKey.key()), nmsTag);
+ }
+
+ @Override
+ public void setTag(final TagKey<T> tagKey, final Collection<TypedKey<T>> values) {
+ final List<M> newList = List.copyOf(Collections2.transform(values, this::convert));
+ this.tags.put(PaperAdventure.asVanilla(tagKey.key()), newList);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/tag/PaperPreFlattenTagRegistrar.java b/src/main/java/io/papermc/paper/tag/PaperPreFlattenTagRegistrar.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/tag/PaperPreFlattenTagRegistrar.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.tag;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import io.papermc.paper.adventure.PaperAdventure;
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.registrar.PaperRegistrar;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.tag.TagKey;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.tags.TagLoader;
+import org.bukkit.craftbukkit.util.CraftNamespacedKey;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@SuppressWarnings("BoundedWildcard")
+@DefaultQualifier(NonNull.class)
+public class PaperPreFlattenTagRegistrar<T> implements PaperRegistrar<BootstrapContext>, PreFlattenTagRegistrar<T> {
+
+ public final Map<ResourceLocation, List<TagLoader.EntryWithSource>> tags;
+ private final RegistryKey<T> registryKey;
+
+ private @Nullable BootstrapContext owner;
+
+ public PaperPreFlattenTagRegistrar(
+ final Map<ResourceLocation, List<TagLoader.EntryWithSource>> tags,
+ final TagEventConfig<?, T> config
+ ) {
+ this.tags = new HashMap<>(tags);
+ this.registryKey = config.apiRegistryKey();
+ }
+
+ @Override
+ public void setCurrentContext(final @Nullable BootstrapContext owner) {
+ this.owner = owner;
+ }
+
+ @Override
+ public RegistryKey<T> registryKey() {
+ return this.registryKey;
+ }
+
+ @Override
+ public Map<TagKey<T>, Collection<TagEntry<T>>> getAllTags() {
+ final ImmutableMap.Builder<TagKey<T>, Collection<io.papermc.paper.tag.TagEntry<T>>> builder = ImmutableMap.builderWithExpectedSize(this.tags.size());
+ for (final Map.Entry<ResourceLocation, List<TagLoader.EntryWithSource>> entry : this.tags.entrySet()) {
+ final TagKey<T> key = TagKey.create(this.registryKey, CraftNamespacedKey.fromMinecraft(entry.getKey()));
+ builder.put(key, convert(entry.getValue()));
+ }
+ return builder.build();
+ }
+
+ private static <T> List<io.papermc.paper.tag.TagEntry<T>> convert(final List<TagLoader.EntryWithSource> nmsEntries) {
+ return Collections.unmodifiableList(Lists.transform(nmsEntries, PaperPreFlattenTagRegistrar::convert));
+ }
+
+ private static <T> io.papermc.paper.tag.TagEntry<T> convert(final TagLoader.EntryWithSource nmsEntry) {
+ return new TagEntryImpl<>(CraftNamespacedKey.fromMinecraft(nmsEntry.entry().id), nmsEntry.entry().tag, nmsEntry.entry().required);
+ }
+
+ private TagLoader.EntryWithSource convert(final TagEntry<T> entry) {
+ Preconditions.checkState(this.owner != null, "Owner is not set");
+ final ResourceLocation vanilla = PaperAdventure.asVanilla(entry.key());
+ final net.minecraft.tags.TagEntry nmsEntry;
+ if (entry.isTag()) {
+ if (entry.isRequired()) {
+ nmsEntry = net.minecraft.tags.TagEntry.tag(vanilla);
+ } else {
+ nmsEntry = net.minecraft.tags.TagEntry.optionalTag(vanilla);
+ }
+ } else {
+ if (entry.isRequired()) {
+ nmsEntry = net.minecraft.tags.TagEntry.element(vanilla);
+ } else {
+ nmsEntry = net.minecraft.tags.TagEntry.optionalElement(vanilla);
+ }
+ }
+ return new TagLoader.EntryWithSource(nmsEntry, this.owner.getPluginMeta().getDisplayName());
+ }
+
+ @Override
+ public boolean hasTag(final TagKey<T> tagKey) {
+ return this.tags.containsKey(PaperAdventure.asVanilla(tagKey.key()));
+ }
+
+ private List<TagLoader.EntryWithSource> getNmsTag(final TagKey<T> tagKey, boolean create) {
+ final ResourceLocation vanillaKey = PaperAdventure.asVanilla(tagKey.key());
+ List<TagLoader.EntryWithSource> tag = this.tags.get(vanillaKey);
+ if (tag == null) {
+ if (create) {
+ tag = this.tags.computeIfAbsent(vanillaKey, k -> new ArrayList<>());
+ } else {
+ throw new NoSuchElementException("Tag " + tagKey + " is not present");
+ }
+ }
+ return tag;
+ }
+
+ @Override
+ public List<TagEntry<T>> getTag(final TagKey<T> tagKey) {
+ return convert(this.getNmsTag(tagKey, false));
+ }
+
+ @Override
+ public void addToTag(final TagKey<T> tagKey, final Collection<TagEntry<T>> entries) {
+ final List<TagLoader.EntryWithSource> nmsTag = new ArrayList<>(this.getNmsTag(tagKey, true));
+ for (final TagEntry<T> tagEntry : entries) {
+ nmsTag.add(this.convert(tagEntry));
+ }
+ this.tags.put(PaperAdventure.asVanilla(tagKey.key()), nmsTag);
+ }
+
+ @Override
+ public void setTag(final TagKey<T> tagKey, final Collection<TagEntry<T>> entries) {
+ final List<TagLoader.EntryWithSource> newList = List.copyOf(Collections2.transform(entries, this::convert));
+ this.tags.put(PaperAdventure.asVanilla(tagKey.key()), newList);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/tag/PaperTagListenerManager.java b/src/main/java/io/papermc/paper/tag/PaperTagListenerManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/tag/PaperTagListenerManager.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.tag;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.LifecycleEventRunner;
+import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent;
+import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
+import io.papermc.paper.plugin.lifecycle.event.types.PrioritizableLifecycleEventType;
+import io.papermc.paper.registry.PaperRegistries;
+import io.papermc.paper.registry.RegistryKey;
+import io.papermc.paper.registry.event.RegistryEventMap;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import net.minecraft.core.Holder;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.tags.TagLoader;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public class PaperTagListenerManager {
+
+ public static final String PRE_FLATTEN_EVENT_NAME = "pre-flatten";
+ public static final String POST_FLATTEN_EVENT_NAME = "post-flatten";
+
+ public static final PaperTagListenerManager INSTANCE = new PaperTagListenerManager();
+
+ public final RegistryEventMap preFlatten = new RegistryEventMap(PRE_FLATTEN_EVENT_NAME);
+ public final RegistryEventMap postFlatten = new RegistryEventMap(POST_FLATTEN_EVENT_NAME);
+
+ private PaperTagListenerManager() {
+ }
+
+ public <A> Map<ResourceLocation, List<TagLoader.EntryWithSource>> firePreFlattenEvent(
+ final Map<ResourceLocation, List<TagLoader.EntryWithSource>> initial,
+ final @Nullable TagEventConfig<?, A> config
+ ) {
+ if (config == null || config.preFlatten() == null || !config.preFlatten().hasHandlers()) {
+ return initial;
+ }
+ final PaperPreFlattenTagRegistrar<A> registrar = new PaperPreFlattenTagRegistrar<>(initial, config);
+ LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(
+ config.preFlatten(),
+ registrar,
+ BootstrapContext.class,
+ config.cause()
+ );
+ return Map.copyOf(registrar.tags);
+ }
+
+ public <M, A> Map<ResourceLocation, List<M>> firePostFlattenEvent(
+ final Map<ResourceLocation, List<M>> initial,
+ final @Nullable TagEventConfig<M, A> config
+ ) {
+ if (config == null || config.postFlatten() == null || !config.postFlatten().hasHandlers()) {
+ return initial;
+ }
+ final PaperPostFlattenTagRegistrar<M, A> registrar = new PaperPostFlattenTagRegistrar<>(initial, config);
+ LifecycleEventRunner.INSTANCE.callReloadableRegistrarEvent(
+ config.postFlatten(),
+ registrar,
+ BootstrapContext.class,
+ config.cause()
+ );
+ return Map.copyOf(registrar.tags);
+ }
+
+ public <M, B> @Nullable TagEventConfig<Holder<M>, B> createEventConfig(final Registry<M> registry, final ReloadableRegistrarEvent.Cause cause) {
+ if (PaperRegistries.getEntry(registry.key()) == null) {
+ // TODO probably should be able to modify every registry
+ return null;
+ }
+ final RegistryKey<B> registryKey = PaperRegistries.registryFromNms(registry.key());
+ @Nullable AbstractLifecycleEventType<BootstrapContext, ReloadableRegistrarEvent<PreFlattenTagRegistrar<B>>, ?> preFlatten = null;
+ if (this.preFlatten.hasHandlers(registryKey)) {
+ preFlatten = (AbstractLifecycleEventType<BootstrapContext, ReloadableRegistrarEvent<PreFlattenTagRegistrar<B>>, ?>) this.preFlatten.<B, ReloadableRegistrarEvent<PreFlattenTagRegistrar<B>>>getEventType(registryKey);
+ }
+ @Nullable AbstractLifecycleEventType<BootstrapContext, ReloadableRegistrarEvent<PostFlattenTagRegistrar<B>>, ?> postFlatten = null;
+ if (this.postFlatten.hasHandlers(registryKey)) {
+ postFlatten = (AbstractLifecycleEventType<BootstrapContext, ReloadableRegistrarEvent<PostFlattenTagRegistrar<B>>, ?>) this.postFlatten.<B, ReloadableRegistrarEvent<PostFlattenTagRegistrar<B>>>getEventType(registryKey);
+ }
+ return new TagEventConfig<>(
+ preFlatten,
+ postFlatten,
+ cause,
+ registry::get,
+ h -> ((Holder.Reference<M>) h).key().location(),
+ PaperRegistries.registryFromNms(registry.key())
+ );
+ }
+
+ public <T> PrioritizableLifecycleEventType.Simple<BootstrapContext, ReloadableRegistrarEvent<PreFlattenTagRegistrar<T>>> getPreFlattenType(final RegistryKey<T> registryKey) {
+ return this.preFlatten.getOrCreate(registryKey, (ignored, name) -> {
+ return new PrioritizableLifecycleEventType.Simple<>(name, BootstrapContext.class);
+ });
+ }
+
+ public <T> PrioritizableLifecycleEventType.Simple<BootstrapContext, ReloadableRegistrarEvent<PostFlattenTagRegistrar<T>>> getPostFlattenType(final RegistryKey<T> registryKey) {
+ return this.postFlatten.getOrCreate(registryKey, (ignored, name) -> {
+ return new PrioritizableLifecycleEventType.Simple<>(name, BootstrapContext.class);
+ });
+ }
+}
diff --git a/src/main/java/io/papermc/paper/tag/TagEventConfig.java b/src/main/java/io/papermc/paper/tag/TagEventConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/io/papermc/paper/tag/TagEventConfig.java
@@ -0,0 +0,0 @@
+package io.papermc.paper.tag;
+
+import io.papermc.paper.plugin.bootstrap.BootstrapContext;
+import io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent;
+import io.papermc.paper.plugin.lifecycle.event.types.AbstractLifecycleEventType;
+import io.papermc.paper.registry.RegistryKey;
+import java.util.Optional;
+import java.util.function.Function;
+import net.minecraft.resources.ResourceLocation;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+@DefaultQualifier(NonNull.class)
+public record TagEventConfig<M, A>(
+ @Nullable AbstractLifecycleEventType<BootstrapContext, ? extends ReloadableRegistrarEvent<PreFlattenTagRegistrar<A>>, ?> preFlatten,
+ @Nullable AbstractLifecycleEventType<BootstrapContext, ? extends ReloadableRegistrarEvent<PostFlattenTagRegistrar<A>>, ?> postFlatten,
+ ReloadableRegistrarEvent.Cause cause,
+ Function<ResourceLocation, Optional<? extends M>> fromIdConverter,
+ Function<M, ResourceLocation> toIdConverter,
+ RegistryKey<A> apiRegistryKey
+) {
+}
diff --git a/src/main/java/net/minecraft/resources/RegistryDataLoader.java b/src/main/java/net/minecraft/resources/RegistryDataLoader.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/resources/RegistryDataLoader.java
+++ b/src/main/java/net/minecraft/resources/RegistryDataLoader.java
@@ -0,0 +0,0 @@ public class RegistryDataLoader {
}
io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.runFreezeListeners(registry.key(), conversions); // Paper - run pre-freeze listeners
- TagLoader.loadTagsForRegistry(resourceManager, registry);
+ TagLoader.loadTagsForRegistry(resourceManager, registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL); // Paper - tag lifecycle - add cause
}
static <E> void loadContentsFromNetwork(
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return stream.<Pack>map(resourcepackrepository::getPack).filter(Objects::nonNull).map(Pack::open).collect(ImmutableList.toImmutableList()); // CraftBukkit - decompile error // Paper - decompile error // todo: is this needed anymore?
}, this).thenCompose((immutablelist) -> {
MultiPackResourceManager resourcemanager = new MultiPackResourceManager(PackType.SERVER_DATA, immutablelist);
- List<Registry.PendingTags<?>> list = TagLoader.loadTagsForExistingRegistries(resourcemanager, this.registries.compositeAccess());
+ List<Registry.PendingTags<?>> list = TagLoader.loadTagsForExistingRegistries(resourcemanager, this.registries.compositeAccess(), io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // Paper - tag lifecycle - add cause
return ReloadableServerResources.loadResources(resourcemanager, this.registries, list, this.worldData.enabledFeatures(), this.isDedicatedServer() ? Commands.CommandSelection.DEDICATED : Commands.CommandSelection.INTEGRATED, this.getFunctionCompilationLevel(), this.executor, this).whenComplete((datapackresources, throwable) -> {
if (throwable != null) {
diff --git a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
+++ b/src/main/java/net/minecraft/server/ReloadableServerRegistries.java
@@ -0,0 +0,0 @@ public class ReloadableServerRegistries {
String string = Registries.elementsDirPath(type.registryKey());
SimpleJsonResourceReloadListener.scanDirectory(resourceManager, string, ops, type.codec(), map);
map.forEach((id, value) -> io.papermc.paper.registry.PaperRegistryListenerManager.INSTANCE.registerWithListeners(writableRegistry, ResourceKey.create(type.registryKey(), id), value, DEFAULT_REGISTRATION_INFO, conversions)); // Paper - register with listeners
- TagLoader.loadTagsForRegistry(resourceManager, writableRegistry);
+ TagLoader.loadTagsForRegistry(resourceManager, writableRegistry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.RELOAD); // Paper - tag life cycle - reload
return writableRegistry;
}, prepareExecutor);
}
diff --git a/src/main/java/net/minecraft/server/ServerFunctionLibrary.java b/src/main/java/net/minecraft/server/ServerFunctionLibrary.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/ServerFunctionLibrary.java
+++ b/src/main/java/net/minecraft/server/ServerFunctionLibrary.java
@@ -0,0 +0,0 @@ public class ServerFunctionLibrary implements PreparableReloadListener {
return null;
}).join());
this.functions = builder.build();
- this.tags = this.tagsLoader.build((Map<ResourceLocation, List<TagLoader.EntryWithSource>>)intermediate.getFirst());
+ this.tags = this.tagsLoader.build((Map<ResourceLocation, List<TagLoader.EntryWithSource>>)intermediate.getFirst(), null); // Paper - command function tags are not implemented yet
},
applyExecutor
);
diff --git a/src/main/java/net/minecraft/server/WorldLoader.java b/src/main/java/net/minecraft/server/WorldLoader.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/WorldLoader.java
+++ b/src/main/java/net/minecraft/server/WorldLoader.java
@@ -0,0 +0,0 @@ public class WorldLoader {
CloseableResourceManager closeableResourceManager = pair.getSecond();
LayeredRegistryAccess<RegistryLayer> layeredRegistryAccess = RegistryLayer.createRegistryAccess();
List<Registry.PendingTags<?>> list = TagLoader.loadTagsForExistingRegistries(
- closeableResourceManager, layeredRegistryAccess.getLayer(RegistryLayer.STATIC)
+ closeableResourceManager, layeredRegistryAccess.getLayer(RegistryLayer.STATIC), io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL // Paper - tag lifecycle - add cause
);
RegistryAccess.Frozen frozen = layeredRegistryAccess.getAccessForLoading(RegistryLayer.WORLDGEN);
List<HolderLookup.RegistryLookup<?>> list2 = TagLoader.buildUpdatedLookups(frozen, list);
diff --git a/src/main/java/net/minecraft/tags/TagLoader.java b/src/main/java/net/minecraft/tags/TagLoader.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/tags/TagLoader.java
+++ b/src/main/java/net/minecraft/tags/TagLoader.java
@@ -0,0 +0,0 @@ public class TagLoader<T> {
return list.isEmpty() ? Either.right(List.copyOf(sequencedSet)) : Either.left(list);
}
- public Map<ResourceLocation, List<T>> build(Map<ResourceLocation, List<TagLoader.EntryWithSource>> tags) {
+ // Paper start - fire tag registrar events
+ public Map<ResourceLocation, List<T>> build(Map<ResourceLocation, List<TagLoader.EntryWithSource>> tags, @Nullable io.papermc.paper.tag.TagEventConfig<T, ?> eventConfig) {
+ tags = io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.firePreFlattenEvent(tags, eventConfig);
+ // Paper end - fire tag registrar event
final Map<ResourceLocation, List<T>> map = new HashMap<>();
TagEntry.Lookup<T> lookup = new TagEntry.Lookup<T>() {
@Nullable
@@ -0,0 +0,0 @@ public class TagLoader<T> {
)
.ifRight(values -> map.put(id, (List<T>)values))
);
- return map;
+ return io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.firePostFlattenEvent(map, eventConfig); // Paper - fire tag registrar events
}
public static <T> void loadTagsFromNetwork(TagNetworkSerialization.NetworkPayload tags, WritableRegistry<T> registry) {
@@ -0,0 +0,0 @@ public class TagLoader<T> {
}
public static List<Registry.PendingTags<?>> loadTagsForExistingRegistries(ResourceManager resourceManager, RegistryAccess registryManager) {
+ // Paper start - tag lifecycle - add cause
+ return loadTagsForExistingRegistries(resourceManager, registryManager, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL);
+ }
+ public static List<Registry.PendingTags<?>> loadTagsForExistingRegistries(ResourceManager resourceManager, RegistryAccess registryManager, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause cause) {
+ // Paper end - tag lifecycle - add cause
return registryManager.registries()
- .map(registry -> loadPendingTags(resourceManager, registry.value()))
+ .map(registry -> loadPendingTags(resourceManager, registry.value(), cause)) // Paper - tag lifecycle - add cause
.flatMap(Optional::stream)
.collect(Collectors.toUnmodifiableList());
}
public static <T> void loadTagsForRegistry(ResourceManager resourceManager, WritableRegistry<T> registry) {
+ // Paper start - tag lifecycle - add registrar event cause
+ loadTagsForRegistry(resourceManager, registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause.INITIAL);
+ }
+ public static <T> void loadTagsForRegistry(ResourceManager resourceManager, WritableRegistry<T> registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause cause) {
+ // Paper end - tag lifecycle - add registrar event cause
ResourceKey<? extends Registry<T>> resourceKey = registry.key();
TagLoader<Holder<T>> tagLoader = new TagLoader<>(TagLoader.ElementLookup.fromWritableRegistry(registry), Registries.tagsDirPath(resourceKey));
- tagLoader.build(tagLoader.load(resourceManager)).forEach((id, entries) -> registry.bindTag(TagKey.create(resourceKey, id), (List<Holder<T>>)entries));
+ tagLoader.build(tagLoader.load(resourceManager), io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.createEventConfig(registry, cause)).forEach((id, entries) -> registry.bindTag(TagKey.create(resourceKey, id), (List<Holder<T>>)entries)); // Paper - tag lifecycle - add registrar event cause
}
private static <T> Map<TagKey<T>, List<Holder<T>>> wrapTags(ResourceKey<? extends Registry<T>> registryRef, Map<ResourceLocation, List<Holder<T>>> tags) {
return tags.entrySet().stream().collect(Collectors.toUnmodifiableMap(entry -> TagKey.create(registryRef, entry.getKey()), Entry::getValue));
}
- private static <T> Optional<Registry.PendingTags<T>> loadPendingTags(ResourceManager resourceManager, Registry<T> registry) {
+ private static <T> Optional<Registry.PendingTags<T>> loadPendingTags(ResourceManager resourceManager, Registry<T> registry, io.papermc.paper.plugin.lifecycle.event.registrar.ReloadableRegistrarEvent.Cause cause) { // Paper - add registrar event cause
ResourceKey<? extends Registry<T>> resourceKey = registry.key();
TagLoader<Holder<T>> tagLoader = new TagLoader<>(
(TagLoader.ElementLookup<Holder<T>>)TagLoader.ElementLookup.fromFrozenRegistry(registry), Registries.tagsDirPath(resourceKey)
);
- TagLoader.LoadResult<T> loadResult = new TagLoader.LoadResult<>(resourceKey, wrapTags(registry.key(), tagLoader.build(tagLoader.load(resourceManager))));
+ TagLoader.LoadResult<T> loadResult = new TagLoader.LoadResult<>(resourceKey, wrapTags(registry.key(), tagLoader.build(tagLoader.load(resourceManager), io.papermc.paper.tag.PaperTagListenerManager.INSTANCE.createEventConfig(registry, cause)))); // Paper - add registrar event cause
return loadResult.tags().isEmpty() ? Optional.empty() : Optional.of(registry.prepareTagReload(loadResult));
}

View File

@@ -1,26 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: strnq <dev@aruus.uk>
Date: Sat, 14 Sep 2024 12:53:13 +0300
Subject: [PATCH] Validate slot in PlayerInventory#setSlot
The CraftPlayerInventory implementation sends a container_set_slot
packet to the client which will error if an invalid slot is passed to
the setSlot method, making a validation necessary over simply silently
ignoring invalid slot values.
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftInventoryPlayer.java
@@ -0,0 +0,0 @@ public class CraftInventoryPlayer extends CraftInventory implements org.bukkit.i
@Override
public void setItem(int index, ItemStack item) {
+ // Paper start - Validate setItem index
+ if (index < 0 || index > 40) {
+ throw new ArrayIndexOutOfBoundsException("Index must be between 0 and 40");
+ }
+ // Paper end - Validate setItem index
super.setItem(index, item);
if (this.getHolder() == null) return;
ServerPlayer player = ((CraftPlayer) this.getHolder()).getHandle();

View File

@@ -1,93 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Axionize <154778082+Axionize@users.noreply.github.com>
Date: Sun, 29 Sep 2024 14:20:42 -0700
Subject: [PATCH] Void damage configuration API
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -0,0 +0,0 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
public void checkBelowWorld() {
+ if (!this.level.getWorld().isVoidDamageEnabled()) return; // Paper - check if void damage is enabled on the world
// Paper start - Configurable nether ceiling damage
- if (this.getY() < (double) (this.level.getMinY() - 64) || (this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER
+ if (this.getY() < (double) (this.level.getMinY() + this.level.getWorld().getVoidDamageMinBuildHeightOffset()) || (this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER // Paper - use configured min build height offset
&& this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> this.getY() >= v)
&& (!(this instanceof Player player) || !player.getAbilities().invulnerable))) {
// Paper end - Configurable nether ceiling damage
diff --git a/src/main/java/net/minecraft/world/entity/LivingEntity.java b/src/main/java/net/minecraft/world/entity/LivingEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/world/entity/LivingEntity.java
+++ b/src/main/java/net/minecraft/world/entity/LivingEntity.java
@@ -0,0 +0,0 @@ public abstract class LivingEntity extends Entity implements Attackable {
@Override
protected void onBelowWorld() {
- this.hurt(this.damageSources().fellOutOfWorld(), 4.0F);
+ this.hurt(this.damageSources().fellOutOfWorld(), this.level().getWorld().getVoidDamageAmount()); // Paper - use configured void damage amount
}
protected void updateSwingTime() {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
private final Object2IntOpenHashMap<SpawnCategory> spawnCategoryLimit = new Object2IntOpenHashMap<>();
private final CraftPersistentDataContainer persistentDataContainer = new CraftPersistentDataContainer(CraftWorld.DATA_TYPE_REGISTRY);
private net.kyori.adventure.pointer.Pointers adventure$pointers; // Paper - implement pointers
+ // Paper start - void damage configuration
+ private boolean voidDamageEnabled;
+ private float voidDamageAmount;
+ private double voidDamageMinBuildHeightOffset;
+
+ @Override
+ public boolean isVoidDamageEnabled() {
+ return this.voidDamageEnabled;
+ }
+
+ @Override
+ public void setVoidDamageEnabled(final boolean enabled) {
+ this.voidDamageEnabled = enabled;
+ }
+
+ @Override
+ public float getVoidDamageAmount() {
+ return this.voidDamageAmount;
+ }
+
+ @Override
+ public void setVoidDamageAmount(float voidDamageAmount) {
+ this.voidDamageAmount = voidDamageAmount;
+ }
+
+ @Override
+ public double getVoidDamageMinBuildHeightOffset() {
+ return this.voidDamageMinBuildHeightOffset;
+ }
+
+ @Override
+ public void setVoidDamageMinBuildHeightOffset(double minBuildHeightOffset) {
+ this.voidDamageMinBuildHeightOffset = minBuildHeightOffset;
+ }
+ // Paper end - void damage configuration
// Paper start - Provide fast information methods
@Override
@@ -0,0 +0,0 @@ public class CraftWorld extends CraftRegionAccessor implements World {
}
}
// Paper end - per world spawn limits
+
+ // Paper start - per world void damage height
+ this.voidDamageEnabled = this.world.paperConfig().environment.voidDamageAmount.enabled();
+ this.voidDamageMinBuildHeightOffset = this.world.paperConfig().environment.voidDamageMinBuildHeightOffset;
+ this.voidDamageAmount = (float) this.world.paperConfig().environment.voidDamageAmount.or(0);
+ // Paper end - per world void damage height
}
@Override