add PlayerBedEnterEvent and PlayerBedLeaveEvent + 2 new HumanEntity funcs

This commit is contained in:
sylvessa 2026-03-22 15:48:51 -05:00
parent 6847af62e1
commit 9cbc9aca28
7 changed files with 165 additions and 6 deletions

View file

@ -1046,6 +1046,10 @@ void ServerPlayer::take(shared_ptr<Entity> e, int orgCount)
Player::BedSleepingResult ServerPlayer::startSleepInBed(int x, int y, int z, bool bTestUse)
{
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (!bTestUse && FourKitBridge::FireBedEnter(entityId, dimension, x, y, z))
return OTHER_PROBLEM;
#endif
BedSleepingResult result = Player::startSleepInBed(x, y, z, bTestUse);
if (result == OK)
{
@ -1059,12 +1063,22 @@ Player::BedSleepingResult ServerPlayer::startSleepInBed(int x, int y, int z, boo
void ServerPlayer::stopSleepInBed(bool forcefulWakeUp, bool updateLevelList, bool saveRespawnPoint)
{
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
int bedX = bedPosition ? bedPosition->x : 0;
int bedY = bedPosition ? bedPosition->y : 0;
int bedZ = bedPosition ? bedPosition->z : 0;
bool wasSleeping = isSleeping();
#endif
if (isSleeping())
{
getLevel()->getTracker()->broadcastAndSend(shared_from_this(), std::make_shared<AnimatePacket>(shared_from_this(), AnimatePacket::WAKE_UP));
}
Player::stopSleepInBed(forcefulWakeUp, updateLevelList, saveRespawnPoint);
if (connection != nullptr) connection->teleport(x, y, z, yRot, xRot);
#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD)
if (wasSleeping)
FourKitBridge::FireBedLeave(entityId, dimension, bedX, bedY, bedZ);
#endif
}
void ServerPlayer::ride(shared_ptr<Entity> e)

View file

@ -13,6 +13,8 @@ public abstract class HumanEntity : LivingEntity, InventoryHolder
internal PlayerInventory _playerInventory = new();
internal Inventory _enderChestInventory = new("Ender Chest", InventoryType.ENDER_CHEST, 27);
private ItemStack? _cursorItem;
private bool _sleeping;
private int _sleepTicks;
/// <summary>
/// Gets this human's current <see cref="GameMode"/>.
@ -154,4 +156,20 @@ public abstract class HumanEntity : LivingEntity, InventoryHolder
internal void SetGameModeInternal(GameMode mode) => _gameMode = mode;
internal void SetNameInternal(string name) => _name = name;
internal void SetSleepingInternal(bool sleeping) => _sleeping = sleeping;
internal void SetSleepTicksInternal(int ticks) => _sleepTicks = ticks;
/// <summary>
/// Returns whether this player is slumbering.
/// </summary>
/// <returns>slumber state</returns>
public bool isSleeping() => _sleeping;
/// <summary>
/// Get the sleep ticks of the player. This value may be capped.
/// </summary>
/// <returns>slumber ticks</returns>
public int getSleepTicks() => _sleepTicks;
}

View file

@ -0,0 +1,33 @@
namespace Minecraft.Server.FourKit.Event.Player;
using Minecraft.Server.FourKit.Entity;
using Minecraft.Server.FourKit.Block;
/// <summary>
/// This event is fired when the player is almost about to enter the bed.
/// </summary>
public class PlayerBedEnterEvent : PlayerEvent, Cancellable
{
private readonly Block _bed;
private bool _cancelled;
internal PlayerBedEnterEvent(Player player, Block bed) : base(player)
{
_bed = bed;
}
/// <summary>
/// Returns the bed block involved in this event.
/// </summary>
/// <returns>the bed block involved in this event</returns>
public Block getBed() => _bed;
/// <inheritdoc />
public bool isCancelled() => _cancelled;
/// <inheritdoc />
public void setCancelled(bool cancel)
{
_cancelled = cancel;
}
}

View file

@ -0,0 +1,23 @@
namespace Minecraft.Server.FourKit.Event.Player;
using Minecraft.Server.FourKit.Entity;
using Minecraft.Server.FourKit.Block;
/// <summary>
/// This event is fired when the player is leaving a bed.
/// </summary>
public class PlayerBedLeaveEvent : PlayerEvent
{
private readonly Block _bed;
internal PlayerBedLeaveEvent(Player player, Block bed) : base(player)
{
_bed = bed;
}
/// <summary>
/// Returns the bed block involved in this event.
/// </summary>
/// <returns>the bed block involved in this event</returns>
public Block getBed() => _bed;
}

View file

@ -948,12 +948,12 @@ public static class FourKitHost
};
}
// double[11] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension }
// double[13] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer }
private static void SyncPlayerFromNative(Player player)
{
if (NativeBridge.GetPlayerSnapshot == null)
return;
double[] buf = new double[11];
double[] buf = new double[13];
var gh = GCHandle.Alloc(buf, GCHandleType.Pinned);
try
{
@ -972,6 +972,8 @@ public static class FourKitHost
player.SetFallDistanceInternal((float)buf[5]);
player.SetGameModeInternal((GameMode)(int)buf[6]);
player.SetWalkSpeedInternal((float)buf[7]);
player.SetSleepingInternal(buf[11] != 0.0);
player.SetSleepTicksInternal((int)buf[12]);
}
private static void BroadcastNativeMessage(string message)
@ -1180,6 +1182,51 @@ public static class FourKitHost
}
}
[UnmanagedCallersOnly]
public static int FireBedEnter(int entityId, int dimId, int bedX, int bedY, int bedZ)
{
try
{
var player = FourKit.GetPlayerByEntityId(entityId);
if (player == null) return 0;
SyncPlayerFromNative(player);
var world = FourKit.getWorld(dimId);
var bed = new Block.Block(world, bedX, bedY, bedZ);
var evt = new Event.Player.PlayerBedEnterEvent(player, bed);
FourKit.FireEvent(evt);
return evt.isCancelled() ? 1 : 0;
}
catch (Exception ex)
{
ServerLog.Error("fourkit", $"FireBedEnter error: {ex}");
return 0;
}
}
[UnmanagedCallersOnly]
public static void FireBedLeave(int entityId, int dimId, int bedX, int bedY, int bedZ)
{
try
{
var player = FourKit.GetPlayerByEntityId(entityId);
if (player == null) return;
SyncPlayerFromNative(player);
var world = FourKit.getWorld(dimId);
var bed = new Block.Block(world, bedX, bedY, bedZ);
var evt = new Event.Player.PlayerBedLeaveEvent(player, bed);
FourKit.FireEvent(evt);
}
catch (Exception ex)
{
ServerLog.Error("fourkit", $"FireBedLeave error: {ex}");
}
}
private static ClickType MapNativeClickType(int nativeClickType, int button)
{
return nativeClickType switch

View file

@ -170,8 +170,10 @@ typedef int(__stdcall *fn_fire_player_portal)(int entityId,
double toX, double toY, double toZ, int toDimId,
int cause, double *outCoords);
typedef int(__stdcall *fn_fire_inventory_click)(int entityId,
int slot, int button, int clickType, int nativeContainerType, int containerSize,
const char *titleUtf8, int titleByteLen);
int slot, int button, int clickType, int nativeContainerType, int containerSize,
const char *titleUtf8, int titleByteLen);
typedef int(__stdcall *fn_fire_bed_enter)(int entityId, int dimId, int bedX, int bedY, int bedZ);
typedef void(__stdcall *fn_fire_bed_leave)(int entityId, int dimId, int bedX, int bedY, int bedZ);
struct OpenContainerInfo
{
@ -210,6 +212,8 @@ static fn_get_plugin_command_help s_managedGetPluginCommandHelp = nullptr;
static fn_fire_player_teleport s_managedFirePlayerTeleport = nullptr;
static fn_fire_player_portal s_managedFirePlayerPortal = nullptr;
static fn_fire_inventory_click s_managedFireInventoryClick = nullptr;
static fn_fire_bed_enter s_managedFireBedEnter = nullptr;
static fn_fire_bed_leave s_managedFireBedLeave = nullptr;
static bool s_initialized = false;
@ -321,13 +325,13 @@ static void __cdecl NativeSetFallDistance(int entityId, float distance)
}
}
// double[11] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension }
// double[13] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer }
static void __cdecl NativeGetPlayerSnapshot(int entityId, double *outData)
{
auto player = FindPlayer(entityId);
if (!player)
{
memset(outData, 0, 11 * sizeof(double));
memset(outData, 0, 13 * sizeof(double));
outData[3] = 20.0;
outData[4] = 20.0;
outData[7] = 0.1;
@ -345,6 +349,8 @@ static void __cdecl NativeGetPlayerSnapshot(int entityId, double *outData)
outData[8] = (double)player->yRot;
outData[9] = (double)player->xRot;
outData[10] = (double)player->dimension;
outData[11] = player->isSleeping() ? 1.0 : 0.0;
outData[12] = (double)player->getSleepTimer();
}
static void __cdecl NativeBroadcastMessage(const char *utf8, int len)
@ -1303,6 +1309,8 @@ void Initialize()
ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"FirePlayerTeleport", (void **)&s_managedFirePlayerTeleport);
ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"FirePlayerPortal", (void **)&s_managedFirePlayerPortal);
ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"FireInventoryClick", (void **)&s_managedFireInventoryClick);
ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"FireBedEnter", (void **)&s_managedFireBedEnter);
ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"FireBedLeave", (void **)&s_managedFireBedLeave);
if (!ok)
{
@ -2110,4 +2118,18 @@ int FireInventoryClick(int entityId, int slot, int button, int clickType)
return s_managedFireInventoryClick(entityId, slot, button, clickType, nativeContainerType, containerSize,
titleUtf8.empty() ? nullptr : titleUtf8.data(), (int)titleUtf8.size());
}
bool FireBedEnter(int entityId, int dimId, int bedX, int bedY, int bedZ)
{
if (!s_initialized || !s_managedFireBedEnter)
return false;
return s_managedFireBedEnter(entityId, dimId, bedX, bedY, bedZ) != 0;
}
void FireBedLeave(int entityId, int dimId, int bedX, int bedY, int bedZ)
{
if (!s_initialized || !s_managedFireBedLeave)
return;
s_managedFireBedLeave(entityId, dimId, bedX, bedY, bedZ);
}
} // namespace FourKitBridge

View file

@ -78,4 +78,6 @@ namespace FourKitBridge
int cause,
double *outToX, double *outToY, double *outToZ);
int FireInventoryClick(int entityId, int slot, int button, int clickType);
bool FireBedEnter(int entityId, int dimId, int bedX, int bedY, int bedZ);
void FireBedLeave(int entityId, int dimId, int bedX, int bedY, int bedZ);
}