From a0be612f48032a8f340e7eee2d2b751fba92c74e Mon Sep 17 00:00:00 2001
From: sylvessa <225480449+sylvessa@users.noreply.github.com>
Date: Mon, 23 Mar 2026 17:25:46 -0500
Subject: [PATCH] playsound, sleep ignoring
---
Minecraft.Client/ServerLevel.cpp | 4 +
Minecraft.Server.FourKit/Entity/Player.cs | 36 ++++
Minecraft.Server.FourKit/FourKitHost.cs | 12 +-
Minecraft.Server.FourKit/NativeBridge.cs | 12 +-
Minecraft.Server.FourKit/Sound.cs | 208 ++++++++++++++++++++++
Minecraft.Server/FourKitBridge.cpp | 30 +++-
Minecraft.World/Player.h | 1 +
7 files changed, 294 insertions(+), 9 deletions(-)
create mode 100644 Minecraft.Server.FourKit/Sound.cs
diff --git a/Minecraft.Client/ServerLevel.cpp b/Minecraft.Client/ServerLevel.cpp
index a2596911..37a2234e 100644
--- a/Minecraft.Client/ServerLevel.cpp
+++ b/Minecraft.Client/ServerLevel.cpp
@@ -324,6 +324,8 @@ void ServerLevel::updateSleepingPlayerList()
for (auto& player : players)
{
+ if (player->fk_sleepingIgnored)
+ continue;
if (!player->isSleeping())
{
allPlayersSleeping = false;
@@ -368,6 +370,8 @@ bool ServerLevel::allPlayersAreSleeping()
// all players are sleeping, but have they slept long enough?
for (auto& player : players)
{
+ if (player->fk_sleepingIgnored)
+ continue;
// System.out.println(player->entityId + ": " + player->getSleepTimer());
if (!player->isSleepingLongEnough())
{
diff --git a/Minecraft.Server.FourKit/Entity/Player.cs b/Minecraft.Server.FourKit/Entity/Player.cs
index c908f5fd..85c7529b 100644
--- a/Minecraft.Server.FourKit/Entity/Player.cs
+++ b/Minecraft.Server.FourKit/Entity/Player.cs
@@ -17,6 +17,7 @@ public class Player : HumanEntity, OfflinePlayer, CommandSender
private bool _sneaking;
private bool _sprinting;
private bool _allowFlight;
+ private bool _sleepingIgnored;
internal bool IsOnline { get; set; }
@@ -110,6 +111,40 @@ public class Player : HumanEntity, OfflinePlayer, CommandSender
/// True if player is sprinting.
public bool isSprinting() => _sprinting;
+ ///
+ /// Sets whether the player is ignored as not sleeping. If everyone is
+ /// either sleeping or has this flag set, then time will advance to the
+ /// next day. If everyone has this flag set but no one is actually in
+ /// bed, then nothing will happen.
+ ///
+ /// Whether to ignore.
+ public void setSleepingIgnored(bool isSleeping)
+ {
+ _sleepingIgnored = isSleeping;
+ NativeBridge.SetSleepingIgnored?.Invoke(getEntityId(), isSleeping ? 1 : 0);
+ }
+
+ ///
+ /// Returns whether the player is sleeping ignored.
+ ///
+ /// Whether player is ignoring sleep.
+ public bool isSleepingIgnored() => _sleepingIgnored;
+
+ ///
+ /// Play a sound for a player at the location.
+ /// This function will fail silently if Location or Sound are null.
+ ///
+ /// The location to play the sound.
+ /// The sound to play.
+ /// The volume of the sound.
+ /// The pitch of the sound.
+ public void playSound(Location location, Sound sound, float volume, float pitch)
+ {
+ if (location == null)
+ return;
+ NativeBridge.PlaySound?.Invoke(getEntityId(), (int)sound, location.X, location.Y, location.Z, volume, pitch);
+ }
+
///
/// Determines if the Player is allowed to fly via jump key double-tap
/// like in creative mode.
@@ -242,4 +277,5 @@ public class Player : HumanEntity, OfflinePlayer, CommandSender
internal void SetSneakingInternal(bool sneaking) => _sneaking = sneaking;
internal void SetSprintingInternal(bool sprinting) => _sprinting = sprinting;
internal void SetAllowFlightInternal(bool allowFlight) => _allowFlight = allowFlight;
+ internal void SetSleepingIgnoredInternal(bool ignored) => _sleepingIgnored = ignored;
}
diff --git a/Minecraft.Server.FourKit/FourKitHost.cs b/Minecraft.Server.FourKit/FourKitHost.cs
index 5d0776b3..9a8c28a8 100644
--- a/Minecraft.Server.FourKit/FourKitHost.cs
+++ b/Minecraft.Server.FourKit/FourKitHost.cs
@@ -538,6 +538,9 @@ public static class FourKitHost
Marshal.Copy(lens, 0, ptr, 4);
}
+
+ // todo: rework callback funcs due to it being messy
+
[UnmanagedCallersOnly]
public static void SetNativeCallbacks(IntPtr damage, IntPtr setHealth, IntPtr teleport, IntPtr setGameMode, IntPtr broadcastMessage, IntPtr setFallDistance, IntPtr getPlayerSnapshot, IntPtr sendMessage, IntPtr setWalkSpeed, IntPtr teleportEntity)
{
@@ -595,13 +598,13 @@ public static class FourKitHost
}
[UnmanagedCallersOnly]
- public static void SetEntityCallbacks(IntPtr setSneaking, IntPtr setVelocity, IntPtr setAllowFlight)
+ public static void SetEntityCallbacks(IntPtr setSneaking, IntPtr setVelocity, IntPtr setAllowFlight, IntPtr playSound, IntPtr setSleepingIgnored)
{
// setsneaking and setallowflight here cuz i am lazy
// should be under player stuff ill do that later
try
{
- NativeBridge.SetEntityCallbacks(setSneaking, setVelocity, setAllowFlight);
+ NativeBridge.SetEntityCallbacks(setSneaking, setVelocity, setAllowFlight, playSound, setSleepingIgnored);
}
catch (Exception ex)
{
@@ -963,12 +966,12 @@ public static class FourKitHost
};
}
- // double[20] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer, sneaking, sprinting, onGround, velocityX, velocityY, velocityZ, allowFlight }
+ // double[21] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer, sneaking, sprinting, onGround, velocityX, velocityY, velocityZ, allowFlight, sleepingIgnored }
private static void SyncPlayerFromNative(Player player)
{
if (NativeBridge.GetPlayerSnapshot == null)
return;
- double[] buf = new double[20];
+ double[] buf = new double[21];
var gh = GCHandle.Alloc(buf, GCHandleType.Pinned);
try
{
@@ -994,6 +997,7 @@ public static class FourKitHost
player.SetOnGroundInternal(buf[15] != 0.0);
player.SetVelocityInternal(buf[16], buf[17], buf[18]);
player.SetAllowFlightInternal(buf[19] != 0.0);
+ player.SetSleepingIgnoredInternal(buf[20] != 0.0);
}
private static void BroadcastNativeMessage(string message)
diff --git a/Minecraft.Server.FourKit/NativeBridge.cs b/Minecraft.Server.FourKit/NativeBridge.cs
index 83e35988..425892a1 100644
--- a/Minecraft.Server.FourKit/NativeBridge.cs
+++ b/Minecraft.Server.FourKit/NativeBridge.cs
@@ -126,6 +126,12 @@ internal static class NativeBridge
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void NativeSetAllowFlightDelegate(int entityId, int allowFlight);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void NativePlaySoundDelegate(int entityId, int soundId, double x, double y, double z, float volume, float pitch);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ internal delegate void NativeSetSleepingIgnoredDelegate(int entityId, int ignored);
+
internal static NativeDamageDelegate? DamagePlayer;
internal static NativeSetHealthDelegate? SetPlayerHealth;
@@ -168,6 +174,8 @@ internal static class NativeBridge
internal static NativeSetSneakingDelegate? SetSneaking;
internal static NativeSetVelocityDelegate? SetVelocity;
internal static NativeSetAllowFlightDelegate? SetAllowFlight;
+ internal static NativePlaySoundDelegate? PlaySound;
+ internal static NativeSetSleepingIgnoredDelegate? SetSleepingIgnored;
internal static void SetCallbacks(IntPtr damage, IntPtr setHealth, IntPtr teleport, IntPtr setGameMode, IntPtr broadcastMessage, IntPtr setFallDistance, IntPtr getPlayerSnapshot, IntPtr sendMessage, IntPtr setWalkSpeed, IntPtr teleportEntity)
{
@@ -222,10 +230,12 @@ internal static class NativeBridge
SetHeldItemSlot = Marshal.GetDelegateForFunctionPointer(setHeldItemSlot);
}
- internal static void SetEntityCallbacks(IntPtr setSneaking, IntPtr setVelocity, IntPtr setAllowFlight)
+ internal static void SetEntityCallbacks(IntPtr setSneaking, IntPtr setVelocity, IntPtr setAllowFlight, IntPtr playSound, IntPtr setSleepingIgnored)
{
SetSneaking = Marshal.GetDelegateForFunctionPointer(setSneaking);
SetVelocity = Marshal.GetDelegateForFunctionPointer(setVelocity);
SetAllowFlight = Marshal.GetDelegateForFunctionPointer(setAllowFlight);
+ PlaySound = Marshal.GetDelegateForFunctionPointer(playSound);
+ SetSleepingIgnored = Marshal.GetDelegateForFunctionPointer(setSleepingIgnored);
}
}
diff --git a/Minecraft.Server.FourKit/Sound.cs b/Minecraft.Server.FourKit/Sound.cs
new file mode 100644
index 00000000..ea0698e4
--- /dev/null
+++ b/Minecraft.Server.FourKit/Sound.cs
@@ -0,0 +1,208 @@
+namespace Minecraft.Server.FourKit;
+
+///
+/// An Enum of Sounds the server is able to send to players.
+///
+public enum Sound
+{
+ AMBIENCE_CAVE = 75,
+ AMBIENCE_RAIN = 73,
+ AMBIENCE_THUNDER = 74,
+ ANVIL_BREAK = 110,
+ ANVIL_LAND = 111,
+ ANVIL_USE = 112,
+ ARROW_HIT = 56,
+ BAT_DEATH = 140,
+ BAT_HURT = 139,
+ BAT_IDLE = 138,
+ BAT_LOOP = 138,
+ BAT_TAKEOFF = 141,
+ BLAZE_BREATH = 15,
+ BLAZE_DEATH = 17,
+ BLAZE_HIT = 16,
+ BREATH = 81,
+ BURP = 63,
+ CAT_HISS = 54,
+ CAT_HIT = 54,
+ CAT_MEOW = 53,
+ CAT_PURR = 51,
+ CAT_PURREOW = 52,
+ CHEST_CLOSE = 70,
+ CHEST_OPEN = 69,
+ CHICKEN_EGG_POP = 2,
+ CHICKEN_HURT = 1,
+ CHICKEN_IDLE = 0,
+ CHICKEN_WALK = 148,
+ CLICK = 65,
+ COW_HURT = 4,
+ COW_IDLE = 3,
+ COW_WALK = 147,
+ CREEPER_DEATH = 42,
+ CREEPER_HISS = 41,
+ DIG_GRASS = 125,
+ DIG_GRAVEL = 126,
+ DIG_SAND = 127,
+ DIG_SNOW = 128,
+ DIG_STONE = 129,
+ DIG_WOOD = 130,
+ DIG_WOOL = 124,
+ DONKEY_ANGRY = 175,
+ DONKEY_DEATH = 165,
+ DONKEY_HIT = 169,
+ DONKEY_IDLE = 173,
+ DOOR_CLOSE = 72,
+ DOOR_OPEN = 71,
+ DRINK = 61,
+ EAT = 62,
+ ENDERDRAGON_DEATH = 101,
+ ENDERDRAGON_GROWL = 102,
+ ENDERDRAGON_HIT = 103,
+ ENDERDRAGON_WINGS = 104,
+ ENDERMAN_DEATH = 25,
+ ENDERMAN_HIT = 24,
+ ENDERMAN_IDLE = 23,
+ ENDERMAN_SCREAM = 151,
+ ENDERMAN_STARE = 150,
+ ENDERMAN_TELEPORT = 26,
+ EXPLODE = 57,
+ FALL_BIG = 83,
+ FALL_SMALL = 82,
+ FIRE = 80,
+ FIRE_IGNITE = 79,
+ FIREWORK_BLAST = 132,
+ FIREWORK_BLAST2 = 133,
+ FIREWORK_LARGE_BLAST = 134,
+ FIREWORK_LARGE_BLAST2 = 135,
+ FIREWORK_LAUNCH = 131,
+ FIREWORK_TWINKLE = 136,
+ FIREWORK_TWINKLE2 = 137,
+ FIZZ = 58,
+ FUSE = 60,
+ GHAST_CHARGE = 22,
+ GHAST_DEATH = 20,
+ GHAST_FIREBALL = 21,
+ GHAST_MOAN = 18,
+ GHAST_SCREAM = 19,
+ GHAST_SCREAM2 = 19,
+ GLASS = 66,
+ HORSE_ANGRY = 176,
+ HORSE_ARMOR = 161,
+ HORSE_BREATHE = 178,
+ HORSE_DEATH = 166,
+ HORSE_GALLOP = 177,
+ HORSE_HIT = 170,
+ HORSE_IDLE = 174,
+ HORSE_JUMP = 181,
+ HORSE_LAND = 160,
+ HORSE_SADDLE = 162,
+ HORSE_SKELETON_DEATH = 164,
+ HORSE_SKELETON_HIT = 168,
+ HORSE_SKELETON_IDLE = 172,
+ HORSE_SOFT = 180,
+ HORSE_WOOD = 179,
+ HORSE_ZOMBIE_DEATH = 163,
+ HORSE_ZOMBIE_HIT = 167,
+ HORSE_ZOMBIE_IDLE = 171,
+ HURT = 81,
+ HURT_FLESH = 81,
+ IRONGOLEM_DEATH = 107,
+ IRONGOLEM_HIT = 106,
+ IRONGOLEM_THROW = 105,
+ IRONGOLEM_WALK = 108,
+ ITEM_BREAK = 68,
+ ITEM_PICKUP = 59,
+ LAVA = 93,
+ LAVA_POP = 92,
+ LEVEL_UP = 188,
+ MAGMACUBE_JUMP = 49,
+ MAGMACUBE_WALK = 50,
+ MAGMACUBE_WALK2 = 49,
+ MINECART_BASE = 94,
+ MINECART_INSIDE = 95,
+ NOTE_BASS = 88,
+ NOTE_BASS_DRUM = 85,
+ NOTE_BASS_GUITAR = 88,
+ NOTE_PIANO = 84,
+ NOTE_PLING = 84,
+ NOTE_SNARE_DRUM = 86,
+ NOTE_STICKS = 87,
+ ORB_PICKUP = 67,
+ PIG_DEATH = 6,
+ PIG_IDLE = 5,
+ PIG_WALK = 149,
+ PISTON_EXTEND = 90,
+ PISTON_RETRACT = 89,
+ PORTAL = 76,
+ PORTAL_TRAVEL = 78,
+ PORTAL_TRIGGER = 77,
+ SHEEP_IDLE = 7,
+ SHEEP_SHEAR = 152,
+ SHEEP_WALK = 153,
+ SHOOT_ARROW = 55,
+ SILVERFISH_HIT = 32,
+ SILVERFISH_IDLE = 31,
+ SILVERFISH_KILL = 33,
+ SILVERFISH_WALK = 34,
+ SKELETON_DEATH = 154,
+ SKELETON_HURT = 36,
+ SKELETON_IDLE = 35,
+ SKELETON_WALK = 155,
+ SLIME_ATTACK = 40,
+ SLIME_WALK = 185,
+ SLIME_WALK2 = 186,
+ SPIDER_DEATH = 38,
+ SPIDER_IDLE = 37,
+ SPIDER_WALK = 156,
+ SPLASH = 64,
+ SPLASH2 = 64,
+ STEP_GRASS = 97,
+ STEP_GRAVEL = 96,
+ STEP_LADDER = 123,
+ STEP_SAND = 100,
+ STEP_SNOW = 122,
+ STEP_STONE = 94,
+ STEP_WOOD = 95,
+ STEP_WOOL = 99,
+ SUCCESSFUL_HIT = 56,
+ SWIM = 159,
+ THORNS = 109,
+ VILLAGER_DEATH = 116,
+ VILLAGER_HAGGLE = 113,
+ VILLAGER_HIT = 115,
+ VILLAGER_IDLE = 114,
+ VILLAGER_NO = 118,
+ VILLAGER_YES = 117,
+ WATER = 91,
+ WITCH_DEATH = 184,
+ WITCH_HURT = 183,
+ WITCH_IDLE = 182,
+ WITHER_DEATH = 145,
+ WITHER_HURT = 144,
+ WITHER_IDLE = 143,
+ WITHER_SHOOT = 146,
+ WITHER_SPAWN = 142,
+ WOLF_BARK = 11,
+ WOLF_DEATH = 13,
+ WOLF_GROWL = 8,
+ WOLF_HOWL = 8,
+ WOLF_HURT = 12,
+ WOLF_PANT = 10,
+ WOLF_SHAKE = 14,
+ WOLF_WALK = 157,
+ WOLF_WHINE = 9,
+ WOOD_CLICK = 65,
+ ZOMBIE_DEATH = 45,
+ ZOMBIE_HURT = 44,
+ ZOMBIE_IDLE = 43,
+ ZOMBIE_INFECT = 119,
+ ZOMBIE_METAL = 48,
+ ZOMBIE_PIG_ANGRY = 30,
+ ZOMBIE_PIG_DEATH = 29,
+ ZOMBIE_PIG_HURT = 28,
+ ZOMBIE_PIG_IDLE = 27,
+ ZOMBIE_REMEDY = 121,
+ ZOMBIE_STEP = 158,
+ ZOMBIE_UNFECT = 120,
+ ZOMBIE_WOOD = 46,
+ ZOMBIE_WOODBREAK = 47,
+}
diff --git a/Minecraft.Server/FourKitBridge.cpp b/Minecraft.Server/FourKitBridge.cpp
index 321f3afb..245fdcc2 100644
--- a/Minecraft.Server/FourKitBridge.cpp
+++ b/Minecraft.Server/FourKitBridge.cpp
@@ -36,6 +36,7 @@
#include "..\Minecraft.World\Player.h"
#include "..\Minecraft.World\PlayerAbilitiesPacket.h"
#include "..\Minecraft.World\SetCarriedItemPacket.h"
+#include "..\Minecraft.World\LevelSoundPacket.h"
#include "..\Minecraft.World\SimpleContainer.h"
#include "..\Minecraft.World\Slot.h"
#include "..\Minecraft.World\Tile.h"
@@ -175,7 +176,7 @@ typedef int(__stdcall *fn_fire_inventory_click)(int entityId,
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);
-typedef void(__stdcall *fn_set_entity_callbacks)(void *setSneaking, void *setVelocity, void *setAllowFlight);
+typedef void(__stdcall *fn_set_entity_callbacks)(void *setSneaking, void *setVelocity, void *setAllowFlight, void *playSound, void *setSleepingIgnored);
struct OpenContainerInfo
{
@@ -328,13 +329,13 @@ static void __cdecl NativeSetFallDistance(int entityId, float distance)
}
}
-// double[20] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer, sneaking, sprinting, onGround, velocityX, velocityY, velocityZ, allowFlight }
+// double[21] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer, sneaking, sprinting, onGround, velocityX, velocityY, velocityZ, allowFlight, sleepingIgnored }
static void __cdecl NativeGetPlayerSnapshot(int entityId, double *outData)
{
auto player = FindPlayer(entityId);
if (!player)
{
- memset(outData, 0, 20 * sizeof(double));
+ memset(outData, 0, 21 * sizeof(double));
outData[3] = 20.0;
outData[4] = 20.0;
outData[7] = 0.1;
@@ -361,6 +362,7 @@ static void __cdecl NativeGetPlayerSnapshot(int entityId, double *outData)
outData[17] = player->yd;
outData[18] = player->zd;
outData[19] = player->abilities.mayfly ? 1.0 : 0.0;
+ outData[20] = player->fk_sleepingIgnored ? 1.0 : 0.0;
}
static void __cdecl NativeBroadcastMessage(const char *utf8, int len)
@@ -1332,6 +1334,24 @@ static void __cdecl NativeSetAllowFlight(int entityId, int allowFlight)
}
}
+static void __cdecl NativePlaySound(int entityId, int soundId, double x, double y, double z, float volume, float pitch)
+{
+ auto player = FindPlayer(entityId);
+ if (player && player->connection)
+ {
+ player->connection->send(std::make_shared(soundId, x, y, z, volume, pitch));
+ }
+}
+
+static void __cdecl NativeSetSleepingIgnored(int entityId, int ignored)
+{
+ auto player = FindPlayer(entityId);
+ if (player)
+ {
+ player->fk_sleepingIgnored = (ignored != 0);
+ }
+}
+
static std::wstring FindNet10SystemRoot()
{
// overengineered
@@ -1634,7 +1654,9 @@ void Initialize()
s_managedSetEntityCallbacks(
(void *)&NativeSetSneaking,
(void *)&NativeSetVelocity,
- (void *)&NativeSetAllowFlight);
+ (void *)&NativeSetAllowFlight,
+ (void *)&NativePlaySound,
+ (void *)&NativeSetSleepingIgnored);
LogInfo("fourkit", "FourKit initialized successfully.");
}
diff --git a/Minecraft.World/Player.h b/Minecraft.World/Player.h
index 74f03920..c8ca1b27 100644
--- a/Minecraft.World/Player.h
+++ b/Minecraft.World/Player.h
@@ -126,6 +126,7 @@ public:
bool fk_deathKeepLevel = false;
int fk_deathNewExp = 0;
int fk_deathNewLevel = 0;
+ bool fk_sleepingIgnored = false;
// 4J Stu - Made protected so that we can access it from MultiPlayerLocalPlayer
protected: