From dc1198d331ac57306894669576dd8577e5ed2342 Mon Sep 17 00:00:00 2001 From: nickrodii Date: Sun, 5 Apr 2026 18:51:28 -0700 Subject: [PATCH 1/2] sleep() and wakeup() api methods, for all living entities including mannequins --- .../java/org/bukkit/entity/LivingEntity.java | 18 +++++ .../craftbukkit/entity/CraftHumanEntity.java | 10 +++ .../craftbukkit/entity/CraftLivingEntity.java | 29 +++++++++ .../io/papermc/testplugin/TestPlugin.java | 65 +++++++++++++++++++ 4 files changed, 122 insertions(+) diff --git a/paper-api/src/main/java/org/bukkit/entity/LivingEntity.java b/paper-api/src/main/java/org/bukkit/entity/LivingEntity.java index 19a48dce7c2f..265968dcb27b 100644 --- a/paper-api/src/main/java/org/bukkit/entity/LivingEntity.java +++ b/paper-api/src/main/java/org/bukkit/entity/LivingEntity.java @@ -841,6 +841,24 @@ default boolean addPotionEffect(@NotNull PotionEffect effect) { */ public boolean isSleeping(); + /** + * Attempts to make the entity sleep at the given location. + *
+ * The location must be in the current world and have a bed placed at the + * location. + * + * @param location the location of the bed + * @return whether the sleep was successful + */ + public boolean sleep(@NotNull Location location); + + /** + * Causes this entity to wake up if it is currently sleeping. + * + * @throws IllegalStateException if not sleeping + */ + public void wakeup(); + /** * Gets if the entity is climbing. * diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java index 5a4e98531031..9db67e3acd4d 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftHumanEntity.java @@ -210,6 +210,11 @@ public boolean sleep(Location location, boolean force) { return true; } + @Override + public boolean sleep(Location location) { + return this.sleep(location, false); + } + @Override public void wakeup(boolean setSpawnLocation) { Preconditions.checkState(this.isSleeping(), "Cannot wakeup if not sleeping"); @@ -217,6 +222,11 @@ public void wakeup(boolean setSpawnLocation) { this.getHandle().stopSleepInBed(true, setSpawnLocation); } + @Override + public void wakeup() { + this.wakeup(false); + } + @Override public void startRiptideAttack(int duration, float damage, ItemStack attackItem) { Preconditions.checkArgument(duration > 0, "Duration must be greater than 0"); diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java index b0987314d263..89451bca56f0 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java @@ -13,6 +13,7 @@ import io.papermc.paper.adventure.PaperAdventure; import net.kyori.adventure.key.Key; import net.minecraft.Optionull; +import net.minecraft.core.BlockPos; import io.papermc.paper.world.damagesource.CombatTracker; import net.minecraft.core.component.DataComponents; import net.minecraft.network.protocol.game.ClientboundHurtAnimationPacket; @@ -40,6 +41,8 @@ import net.minecraft.world.entity.projectile.arrow.ThrownTrident; import net.minecraft.world.item.Items; import net.minecraft.world.item.component.Consumable; +import net.minecraft.world.level.block.BedBlock; +import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import net.minecraft.world.waypoints.WaypointStyleAsset; import net.minecraft.world.waypoints.WaypointStyleAssets; @@ -61,6 +64,7 @@ import org.bukkit.craftbukkit.inventory.CraftEntityEquipment; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.craftbukkit.potion.CraftPotionEffectType; +import org.bukkit.craftbukkit.util.CraftLocation; import org.bukkit.entity.AbstractArrow; import org.bukkit.entity.AbstractWindCharge; import org.bukkit.entity.Arrow; @@ -769,6 +773,31 @@ public boolean isSleeping() { return this.getHandle().isSleeping(); } + @Override + public boolean sleep(Location location) { + Preconditions.checkArgument(location != null, "Location cannot be null"); + Preconditions.checkArgument(location.getWorld() != null, "Location needs to be in a world"); + Preconditions.checkArgument(location.getWorld().equals(this.getWorld()), "Cannot sleep across worlds"); + Preconditions.checkState(!this.getHandle().generation, "Cannot sleep during world generation"); + + BlockPos position = CraftLocation.toBlockPosition(location); + BlockState state = this.getHandle().level().getBlockState(position); + if (!(state.getBlock() instanceof BedBlock)) { + return false; + } + + this.getHandle().startSleeping(position); + return true; + } + + @Override + public void wakeup() { + Preconditions.checkState(this.isSleeping(), "Cannot wakeup if not sleeping"); + Preconditions.checkState(!this.getHandle().generation, "Cannot wakeup during world generation"); + + this.getHandle().stopSleeping(); + } + @Override public boolean isClimbing() { Preconditions.checkState(!this.getHandle().generation, "Cannot check if climbing during world generation"); diff --git a/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java b/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java index fd891f5b1fad..6b5f2913281e 100644 --- a/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java +++ b/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java @@ -1,14 +1,79 @@ package io.papermc.testplugin; +import java.util.Comparator; +import java.util.List; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.defaults.BukkitCommand; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; public final class TestPlugin extends JavaPlugin implements Listener { @Override public void onEnable() { this.getServer().getPluginManager().registerEvents(this, this); + this.registerSleepTestCommands(); // io.papermc.testplugin.brigtests.Registration.registerViaOnEnable(this); } + + private void registerSleepTestCommands() { + this.getServer().getCommandMap().register("test-plugin", new BukkitCommand("testsleep", "Sleep the nearest living entity at its current location", "/testsleep", List.of()) { + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Only players can use this command."); + return true; + } + + LivingEntity target = findNearestLivingEntity(player); + if (target == null) { + player.sendMessage("No living entity found nearby."); + return true; + } + + boolean slept = target.sleep(target.getLocation()); + player.sendMessage("testsleep: " + target.getType() + " -> " + slept + " (sleeping=" + target.isSleeping() + ")"); + return true; + } + }); + + this.getServer().getCommandMap().register("test-plugin", new BukkitCommand("testwakeup", "Wake the nearest sleeping living entity", "/testwakeup", List.of()) { + @Override + public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage("Only players can use this command."); + return true; + } + + LivingEntity target = findNearestSleepingLivingEntity(player); + if (target == null) { + player.sendMessage("No sleeping living entity found nearby."); + return true; + } + + target.wakeup(); + player.sendMessage("testwakeup: " + target.getType() + " -> sleeping=" + target.isSleeping()); + return true; + } + }); + } + + private static LivingEntity findNearestLivingEntity(Player player) { + return player.getLocation().getNearbyEntitiesByType(LivingEntity.class, 8.0, entity -> !(entity instanceof Player)) + .stream() + .min(Comparator.comparingDouble(entity -> entity.getLocation().distanceSquared(player.getLocation()))) + .orElse(null); + } + + private static LivingEntity findNearestSleepingLivingEntity(Player player) { + return player.getLocation().getNearbyEntitiesByType(LivingEntity.class, 8.0, entity -> !(entity instanceof Player) && entity.isSleeping()) + .stream() + .min(Comparator.comparingDouble(entity -> entity.getLocation().distanceSquared(player.getLocation()))) + .orElse(null); + } } From ca4b33d3bb3eee898af2e89980224a84438d4e41 Mon Sep 17 00:00:00 2001 From: Nick Rodi Date: Sun, 12 Apr 2026 06:16:24 -0700 Subject: [PATCH 2/2] Fixed accidental local test push --- .../io/papermc/testplugin/TestPlugin.java | 65 ------------------- 1 file changed, 65 deletions(-) diff --git a/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java b/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java index 6b5f2913281e..fd891f5b1fad 100644 --- a/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java +++ b/test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java @@ -1,79 +1,14 @@ package io.papermc.testplugin; -import java.util.Comparator; -import java.util.List; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.command.defaults.BukkitCommand; -import org.bukkit.entity.LivingEntity; -import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; -import org.jetbrains.annotations.NotNull; public final class TestPlugin extends JavaPlugin implements Listener { @Override public void onEnable() { this.getServer().getPluginManager().registerEvents(this, this); - this.registerSleepTestCommands(); // io.papermc.testplugin.brigtests.Registration.registerViaOnEnable(this); } - - private void registerSleepTestCommands() { - this.getServer().getCommandMap().register("test-plugin", new BukkitCommand("testsleep", "Sleep the nearest living entity at its current location", "/testsleep", List.of()) { - @Override - public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { - if (!(sender instanceof Player player)) { - sender.sendMessage("Only players can use this command."); - return true; - } - - LivingEntity target = findNearestLivingEntity(player); - if (target == null) { - player.sendMessage("No living entity found nearby."); - return true; - } - - boolean slept = target.sleep(target.getLocation()); - player.sendMessage("testsleep: " + target.getType() + " -> " + slept + " (sleeping=" + target.isSleeping() + ")"); - return true; - } - }); - - this.getServer().getCommandMap().register("test-plugin", new BukkitCommand("testwakeup", "Wake the nearest sleeping living entity", "/testwakeup", List.of()) { - @Override - public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { - if (!(sender instanceof Player player)) { - sender.sendMessage("Only players can use this command."); - return true; - } - - LivingEntity target = findNearestSleepingLivingEntity(player); - if (target == null) { - player.sendMessage("No sleeping living entity found nearby."); - return true; - } - - target.wakeup(); - player.sendMessage("testwakeup: " + target.getType() + " -> sleeping=" + target.isSleeping()); - return true; - } - }); - } - - private static LivingEntity findNearestLivingEntity(Player player) { - return player.getLocation().getNearbyEntitiesByType(LivingEntity.class, 8.0, entity -> !(entity instanceof Player)) - .stream() - .min(Comparator.comparingDouble(entity -> entity.getLocation().distanceSquared(player.getLocation()))) - .orElse(null); - } - - private static LivingEntity findNearestSleepingLivingEntity(Player player) { - return player.getLocation().getNearbyEntitiesByType(LivingEntity.class, 8.0, entity -> !(entity instanceof Player) && entity.isSleeping()) - .stream() - .min(Comparator.comparingDouble(entity -> entity.getLocation().distanceSquared(player.getLocation()))) - .orElse(null); - } }