Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions paper-api/src/main/java/org/bukkit/entity/LivingEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,24 @@ default boolean addPotionEffect(@NotNull PotionEffect effect) {
*/
public boolean isSleeping();

/**
* Attempts to make the entity sleep at the given location.
* <br>
* 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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,23 @@ 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");

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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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");
Expand Down
65 changes: 65 additions & 0 deletions test-plugin/src/main/java/io/papermc/testplugin/TestPlugin.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading