From 1015948024b8ac6b45f1c8e8ca14fb731b8821b4 Mon Sep 17 00:00:00 2001 From: Yannick Lamprecht Date: Tue, 23 Dec 2025 22:23:13 +0100 Subject: [PATCH 1/5] feature: expose mannequin poses and provide pose argument --- .../main/java/io/papermc/paper/InternalAPIBridge.java | 4 ++++ .../src/main/java/org/bukkit/entity/Mannequin.java | 10 ++++++++++ .../papermc/paper/PaperServerInternalAPIBridge.java | 11 +++++++++++ .../org/bukkit/craftbukkit/entity/CraftMannequin.java | 7 +++---- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java index 609b4b1c7d97..7031a6bc9e60 100644 --- a/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java +++ b/paper-api/src/main/java/io/papermc/paper/InternalAPIBridge.java @@ -5,6 +5,7 @@ import io.papermc.paper.datacomponent.item.ResolvableProfile; import io.papermc.paper.world.damagesource.CombatEntry; import io.papermc.paper.world.damagesource.FallLocationType; +import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import net.kyori.adventure.text.Component; @@ -14,6 +15,7 @@ import org.bukkit.damage.DamageEffect; import org.bukkit.damage.DamageSource; import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Pose; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Contract; import org.jspecify.annotations.NullMarked; @@ -100,4 +102,6 @@ class Holder { Component defaultMannequinDescription(); GameRule legacyGameRuleBridge(GameRule rule, Function fromLegacyToModern, Function toLegacyFromModern, Class legacyClass); + + Set validMannequinPoses(); } diff --git a/paper-api/src/main/java/org/bukkit/entity/Mannequin.java b/paper-api/src/main/java/org/bukkit/entity/Mannequin.java index 02555b387d56..2d19a56096b2 100644 --- a/paper-api/src/main/java/org/bukkit/entity/Mannequin.java +++ b/paper-api/src/main/java/org/bukkit/entity/Mannequin.java @@ -3,6 +3,7 @@ import com.destroystokyo.paper.SkinParts; import io.papermc.paper.InternalAPIBridge; import io.papermc.paper.datacomponent.item.ResolvableProfile; +import java.util.Set; import net.kyori.adventure.text.Component; import org.bukkit.inventory.EntityEquipment; import org.bukkit.inventory.MainHand; @@ -12,6 +13,15 @@ @NullMarked public interface Mannequin extends LivingEntity { + /** + * Returns the valid poses for a mannequin. + * + * @return the valid poses + */ + static Set validPoses() { + return InternalAPIBridge.get().validMannequinPoses(); + } + /** * Returns the default mannequin profile. * diff --git a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java index f7979f188c3d..ed1624ae7e21 100644 --- a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java +++ b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java @@ -10,8 +10,10 @@ import io.papermc.paper.world.damagesource.FallLocationType; import io.papermc.paper.world.damagesource.PaperCombatEntryWrapper; import io.papermc.paper.world.damagesource.PaperCombatTrackerWrapper; +import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collectors; import net.kyori.adventure.text.Component; import net.minecraft.Optionull; import net.minecraft.commands.Commands; @@ -27,6 +29,7 @@ import org.bukkit.damage.DamageEffect; import org.bukkit.damage.DamageSource; import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Pose; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -34,6 +37,9 @@ public class PaperServerInternalAPIBridge implements InternalAPIBridge { public static final PaperServerInternalAPIBridge INSTANCE = new PaperServerInternalAPIBridge(); + private static final Set validMannequinPoses = Mannequin.VALID_POSES.stream() + .map(pose -> Pose.values()[pose.ordinal()]).collect(Collectors.toSet()); + @Override public DamageEffect getDamageEffect(final String key) { return CraftDamageEffect.getById(key); @@ -116,4 +122,9 @@ public Component defaultMannequinDescription() { public GameRule legacyGameRuleBridge(GameRule rule, Function fromLegacyToModern, Function toLegacyFromModern, Class legacyClass) { return CraftGameRule.wrap(rule, fromLegacyToModern, toLegacyFromModern, legacyClass); } + + @Override + public Set validMannequinPoses() { + return validMannequinPoses; + } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java index c22c295114ff..f2062cc9af94 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java @@ -31,15 +31,14 @@ public net.minecraft.world.entity.decoration.Mannequin getHandle() { @Override public void setPose(Pose pose, boolean fixed) { Preconditions.checkArgument(pose != null, "pose cannot be null"); - net.minecraft.world.entity.Pose internalPose = net.minecraft.world.entity.Pose.values()[pose.ordinal()]; - if (!net.minecraft.world.entity.decoration.Mannequin.VALID_POSES.contains(internalPose)) { + if (!Mannequin.validPoses().contains(pose)) { throw new IllegalArgumentException("Invalid pose '%s', expected one of: %s".formatted( pose.name(), - net.minecraft.world.entity.decoration.Mannequin.VALID_POSES.stream().map(p -> Pose.values()[p.ordinal()]).toList() // name doesn't match + Mannequin.validPoses().stream().toList() // name doesn't match )); } - this.setPose0(internalPose, fixed); + this.setPose0(net.minecraft.world.entity.Pose.values()[pose.ordinal()], fixed); } @Override From 6b0dd91374ae2b95f58f268576774cab77a270fd Mon Sep 17 00:00:00 2001 From: Bjarne Koll Date: Tue, 30 Dec 2025 21:38:52 +0100 Subject: [PATCH 2/5] Naming --- .../io/papermc/paper/PaperServerInternalAPIBridge.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java index ed1624ae7e21..e4aa8da9dbc8 100644 --- a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java +++ b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java @@ -10,6 +10,7 @@ import io.papermc.paper.world.damagesource.FallLocationType; import io.papermc.paper.world.damagesource.PaperCombatEntryWrapper; import io.papermc.paper.world.damagesource.PaperCombatTrackerWrapper; +import java.util.Collections; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; @@ -37,8 +38,9 @@ public class PaperServerInternalAPIBridge implements InternalAPIBridge { public static final PaperServerInternalAPIBridge INSTANCE = new PaperServerInternalAPIBridge(); - private static final Set validMannequinPoses = Mannequin.VALID_POSES.stream() - .map(pose -> Pose.values()[pose.ordinal()]).collect(Collectors.toSet()); + private static final Set VALID_MANNEQUIN_POSES = Collections.unmodifiableSet( + Mannequin.VALID_POSES.stream().map(pose -> Pose.values()[pose.ordinal()]).collect(Collectors.toSet()) + ); @Override public DamageEffect getDamageEffect(final String key) { @@ -125,6 +127,6 @@ public GameRule legacyGameRuleBridge(GameRule r @Override public Set validMannequinPoses() { - return validMannequinPoses; + return VALID_MANNEQUIN_POSES; } } From 34c6e101c097d555377d0a4e7f483509820425ea Mon Sep 17 00:00:00 2001 From: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com> Date: Tue, 30 Dec 2025 22:39:24 +0100 Subject: [PATCH 3/5] tweaks --- .../java/org/bukkit/craftbukkit/entity/CraftMannequin.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java index f2062cc9af94..c88eda6de25f 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java @@ -32,10 +32,7 @@ public net.minecraft.world.entity.decoration.Mannequin getHandle() { public void setPose(Pose pose, boolean fixed) { Preconditions.checkArgument(pose != null, "pose cannot be null"); if (!Mannequin.validPoses().contains(pose)) { - throw new IllegalArgumentException("Invalid pose '%s', expected one of: %s".formatted( - pose.name(), - Mannequin.validPoses().stream().toList() // name doesn't match - )); + throw new IllegalArgumentException("Invalid pose '%s', expected one of: %s".formatted(pose.name(), Mannequin.validPoses())); } this.setPose0(net.minecraft.world.entity.Pose.values()[pose.ordinal()], fixed); From 912233029033b990379ba09936d81c5e4e654a08 Mon Sep 17 00:00:00 2001 From: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com> Date: Tue, 30 Dec 2025 22:54:36 +0100 Subject: [PATCH 4/5] move constant --- .../papermc/paper/PaperServerInternalAPIBridge.java | 9 ++------- .../bukkit/craftbukkit/entity/CraftMannequin.java | 12 ++++++++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java index e4aa8da9dbc8..7435a9ebb62f 100644 --- a/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java +++ b/paper-server/src/main/java/io/papermc/paper/PaperServerInternalAPIBridge.java @@ -10,11 +10,9 @@ import io.papermc.paper.world.damagesource.FallLocationType; import io.papermc.paper.world.damagesource.PaperCombatEntryWrapper; import io.papermc.paper.world.damagesource.PaperCombatTrackerWrapper; -import java.util.Collections; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; -import java.util.stream.Collectors; import net.kyori.adventure.text.Component; import net.minecraft.Optionull; import net.minecraft.commands.Commands; @@ -27,6 +25,7 @@ import org.bukkit.craftbukkit.damage.CraftDamageEffect; import org.bukkit.craftbukkit.damage.CraftDamageSource; import org.bukkit.craftbukkit.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.entity.CraftMannequin; import org.bukkit.damage.DamageEffect; import org.bukkit.damage.DamageSource; import org.bukkit.entity.LivingEntity; @@ -38,10 +37,6 @@ public class PaperServerInternalAPIBridge implements InternalAPIBridge { public static final PaperServerInternalAPIBridge INSTANCE = new PaperServerInternalAPIBridge(); - private static final Set VALID_MANNEQUIN_POSES = Collections.unmodifiableSet( - Mannequin.VALID_POSES.stream().map(pose -> Pose.values()[pose.ordinal()]).collect(Collectors.toSet()) - ); - @Override public DamageEffect getDamageEffect(final String key) { return CraftDamageEffect.getById(key); @@ -127,6 +122,6 @@ public GameRule legacyGameRuleBridge(GameRule r @Override public Set validMannequinPoses() { - return VALID_MANNEQUIN_POSES; + return CraftMannequin.VALID_POSES; } } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java index c88eda6de25f..f4f286f39c8f 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java @@ -6,6 +6,9 @@ import io.papermc.paper.adventure.PaperAdventure; import io.papermc.paper.datacomponent.item.PaperResolvableProfile; import io.papermc.paper.datacomponent.item.ResolvableProfile; +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; import net.kyori.adventure.text.Component; import net.minecraft.Optionull; import net.minecraft.world.entity.Avatar; @@ -19,6 +22,11 @@ @NullMarked public class CraftMannequin extends CraftLivingEntity implements Mannequin { + + public static final Set VALID_POSES = Collections.unmodifiableSet( + net.minecraft.world.entity.decoration.Mannequin.VALID_POSES.stream().map(pose -> Pose.values()[pose.ordinal()]).collect(Collectors.toSet()) + ); + public CraftMannequin(final CraftServer server, final net.minecraft.world.entity.decoration.Mannequin entity) { super(server, entity); } @@ -31,8 +39,8 @@ public net.minecraft.world.entity.decoration.Mannequin getHandle() { @Override public void setPose(Pose pose, boolean fixed) { Preconditions.checkArgument(pose != null, "pose cannot be null"); - if (!Mannequin.validPoses().contains(pose)) { - throw new IllegalArgumentException("Invalid pose '%s', expected one of: %s".formatted(pose.name(), Mannequin.validPoses())); + if (!VALID_POSES.contains(pose)) { + throw new IllegalArgumentException("Invalid pose '%s', expected one of: %s".formatted(pose.name(), VALID_POSES)); } this.setPose0(net.minecraft.world.entity.Pose.values()[pose.ordinal()], fixed); From fa9d13100ec1396d6d04a1889451d2b0cf2c3fa9 Mon Sep 17 00:00:00 2001 From: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com> Date: Wed, 31 Dec 2025 18:55:44 +0100 Subject: [PATCH 5/5] simplify --- .../java/org/bukkit/craftbukkit/entity/CraftMannequin.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java index f4f286f39c8f..869452f4f356 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftMannequin.java @@ -6,7 +6,6 @@ import io.papermc.paper.adventure.PaperAdventure; import io.papermc.paper.datacomponent.item.PaperResolvableProfile; import io.papermc.paper.datacomponent.item.ResolvableProfile; -import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; import net.kyori.adventure.text.Component; @@ -23,9 +22,8 @@ @NullMarked public class CraftMannequin extends CraftLivingEntity implements Mannequin { - public static final Set VALID_POSES = Collections.unmodifiableSet( - net.minecraft.world.entity.decoration.Mannequin.VALID_POSES.stream().map(pose -> Pose.values()[pose.ordinal()]).collect(Collectors.toSet()) - ); + public static final Set VALID_POSES = net.minecraft.world.entity.decoration.Mannequin.VALID_POSES.stream() + .map(pose -> Pose.values()[pose.ordinal()]).collect(Collectors.toUnmodifiableSet()); public CraftMannequin(final CraftServer server, final net.minecraft.world.entity.decoration.Mannequin entity) { super(server, entity);