From a91abed6a123a22e881e03c0556aced75ceff440 Mon Sep 17 00:00:00 2001 From: sylvessa <225480449+sylvessa@users.noreply.github.com> Date: Mon, 23 Mar 2026 19:10:35 -0500 Subject: [PATCH] add more missing useful funcs --- Minecraft.Server.FourKit/Entity/Player.cs | 108 ++++++++++++++++++++++ Minecraft.Server.FourKit/FourKitHost.cs | 23 ++++- Minecraft.Server.FourKit/NativeBridge.cs | 39 ++++++++ Minecraft.Server/FourKitBridge.cpp | 94 ++++++++++++++++++- 4 files changed, 260 insertions(+), 4 deletions(-) diff --git a/Minecraft.Server.FourKit/Entity/Player.cs b/Minecraft.Server.FourKit/Entity/Player.cs index 85c7529bc..17640a5f7 100644 --- a/Minecraft.Server.FourKit/Entity/Player.cs +++ b/Minecraft.Server.FourKit/Entity/Player.cs @@ -12,6 +12,11 @@ public class Player : HumanEntity, OfflinePlayer, CommandSender { private float _saturation = 5.0f; private float _walkSpeed = 0.2f; + private float _exhaustion; + private int _foodLevel = 20; + private int _level; + private float _exp; + private int _totalExperience; private Guid _playerUniqueId; private string? _displayName; private bool _sneaking; @@ -270,6 +275,104 @@ public class Player : HumanEntity, OfflinePlayer, CommandSender } } + /// + /// Gets the players current experience level. + /// + /// Current experience level. + public int getLevel() => _level; + + /// + /// Sets the players current experience level. + /// + /// New experience level. + public void setLevel(int level) + { + _level = level; + NativeBridge.SetLevel?.Invoke(getEntityId(), level); + } + + /// + /// Gets the players current experience points towards the next level. + /// This is a percentage value. 0 is "no progress" and 1 is "next level". + /// + /// Current experience points. + public float getExp() => _exp; + + /// + /// Sets the players current experience points towards the next level. + /// This is a percentage value. 0 is "no progress" and 1 is "next level". + /// + /// New experience points. + public void setExp(float exp) + { + _exp = exp; + NativeBridge.SetExp?.Invoke(getEntityId(), exp); + } + + /// + /// Gives the player the amount of experience specified. + /// + /// Exp amount to give. + public void giveExp(int amount) + { + NativeBridge.GiveExp?.Invoke(getEntityId(), amount); + } + + /// + /// Gives the player the amount of experience levels specified. + /// Levels can be taken by specifying a negative amount. + /// + /// Amount of experience levels to give or take. + public void giveExpLevels(int amount) + { + NativeBridge.GiveExpLevels?.Invoke(getEntityId(), amount); + } + + /// + /// Gets the players current exhaustion level. + /// Exhaustion controls how fast the food level drops. While you have a + /// certain amount of exhaustion, your saturation will drop to zero, and + /// then your food will drop to zero. + /// + /// Exhaustion level. + public float getExhaustion() => _exhaustion; + + /// + /// Sets the players current exhaustion level. + /// + /// Exhaustion level. + public void setExhaustion(float value) + { + _exhaustion = value; + NativeBridge.SetExhaustion?.Invoke(getEntityId(), value); + } + + /// + /// Sets the players current saturation level. + /// + /// Saturation level. + public void setSaturation(float value) + { + _saturation = value; + NativeBridge.SetSaturation?.Invoke(getEntityId(), value); + } + + /// + /// Gets the players current food level. + /// + /// Food level. + public int getFoodLevel() => _foodLevel; + + /// + /// Sets the players current food level. + /// + /// New food level. + public void setFoodLevel(int value) + { + _foodLevel = value; + NativeBridge.SetFoodLevel?.Invoke(getEntityId(), value); + } + // INTERNAL internal void SetSaturationInternal(float saturation) => _saturation = saturation; internal void SetWalkSpeedInternal(float walkSpeed) => _walkSpeed = walkSpeed; @@ -278,4 +381,9 @@ public class Player : HumanEntity, OfflinePlayer, CommandSender internal void SetSprintingInternal(bool sprinting) => _sprinting = sprinting; internal void SetAllowFlightInternal(bool allowFlight) => _allowFlight = allowFlight; internal void SetSleepingIgnoredInternal(bool ignored) => _sleepingIgnored = ignored; + internal void SetLevelInternal(int level) => _level = level; + internal void SetExpInternal(float exp) => _exp = exp; + internal void SetTotalExperienceInternal(int totalExp) => _totalExperience = totalExp; + internal void SetFoodLevelInternal(int foodLevel) => _foodLevel = foodLevel; + internal void SetExhaustionInternal(float exhaustion) => _exhaustion = exhaustion; } diff --git a/Minecraft.Server.FourKit/FourKitHost.cs b/Minecraft.Server.FourKit/FourKitHost.cs index 9a8c28a8a..266b5f544 100644 --- a/Minecraft.Server.FourKit/FourKitHost.cs +++ b/Minecraft.Server.FourKit/FourKitHost.cs @@ -612,6 +612,19 @@ public static class FourKitHost } } + [UnmanagedCallersOnly] + public static void SetExperienceCallbacks(IntPtr setLevel, IntPtr setExp, IntPtr giveExp, IntPtr giveExpLevels, IntPtr setFoodLevel, IntPtr setSaturation, IntPtr setExhaustion) + { + try + { + NativeBridge.SetExperienceCallbacks(setLevel, setExp, giveExp, giveExpLevels, setFoodLevel, setSaturation, setExhaustion); + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"SetExperienceCallbacks error: {ex}"); + } + } + [UnmanagedCallersOnly] public static long FirePlayerDropItem(int entityId, int itemId, int itemCount, int itemAux, IntPtr outItemIdPtr, IntPtr outItemCountPtr, IntPtr outItemAuxPtr) @@ -966,12 +979,12 @@ public static class FourKitHost }; } - // double[21] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer, sneaking, sprinting, onGround, velocityX, velocityY, velocityZ, allowFlight, sleepingIgnored } + // double[27] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer, sneaking, sprinting, onGround, velocityX, velocityY, velocityZ, allowFlight, sleepingIgnored, experienceLevel, experienceProgress, totalExperience, foodLevel, saturation, exhaustion } private static void SyncPlayerFromNative(Player player) { if (NativeBridge.GetPlayerSnapshot == null) return; - double[] buf = new double[21]; + double[] buf = new double[27]; var gh = GCHandle.Alloc(buf, GCHandleType.Pinned); try { @@ -998,6 +1011,12 @@ public static class FourKitHost player.SetVelocityInternal(buf[16], buf[17], buf[18]); player.SetAllowFlightInternal(buf[19] != 0.0); player.SetSleepingIgnoredInternal(buf[20] != 0.0); + player.SetLevelInternal((int)buf[21]); + player.SetExpInternal((float)buf[22]); + player.SetTotalExperienceInternal((int)buf[23]); + player.SetFoodLevelInternal((int)buf[24]); + player.SetSaturationInternal((float)buf[25]); + player.SetExhaustionInternal((float)buf[26]); } private static void BroadcastNativeMessage(string message) diff --git a/Minecraft.Server.FourKit/NativeBridge.cs b/Minecraft.Server.FourKit/NativeBridge.cs index 425892a14..265c3b512 100644 --- a/Minecraft.Server.FourKit/NativeBridge.cs +++ b/Minecraft.Server.FourKit/NativeBridge.cs @@ -132,6 +132,27 @@ internal static class NativeBridge [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void NativeSetSleepingIgnoredDelegate(int entityId, int ignored); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void NativeSetLevelDelegate(int entityId, int level); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void NativeSetExpDelegate(int entityId, float exp); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void NativeGiveExpDelegate(int entityId, int amount); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void NativeGiveExpLevelsDelegate(int entityId, int amount); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void NativeSetFoodLevelDelegate(int entityId, int foodLevel); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void NativeSetSaturationDelegate(int entityId, float saturation); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void NativeSetExhaustionDelegate(int entityId, float exhaustion); + internal static NativeDamageDelegate? DamagePlayer; internal static NativeSetHealthDelegate? SetPlayerHealth; @@ -176,6 +197,13 @@ internal static class NativeBridge internal static NativeSetAllowFlightDelegate? SetAllowFlight; internal static NativePlaySoundDelegate? PlaySound; internal static NativeSetSleepingIgnoredDelegate? SetSleepingIgnored; + internal static NativeSetLevelDelegate? SetLevel; + internal static NativeSetExpDelegate? SetExp; + internal static NativeGiveExpDelegate? GiveExp; + internal static NativeGiveExpLevelsDelegate? GiveExpLevels; + internal static NativeSetFoodLevelDelegate? SetFoodLevel; + internal static NativeSetSaturationDelegate? SetSaturation; + internal static NativeSetExhaustionDelegate? SetExhaustion; internal static void SetCallbacks(IntPtr damage, IntPtr setHealth, IntPtr teleport, IntPtr setGameMode, IntPtr broadcastMessage, IntPtr setFallDistance, IntPtr getPlayerSnapshot, IntPtr sendMessage, IntPtr setWalkSpeed, IntPtr teleportEntity) { @@ -238,4 +266,15 @@ internal static class NativeBridge PlaySound = Marshal.GetDelegateForFunctionPointer(playSound); SetSleepingIgnored = Marshal.GetDelegateForFunctionPointer(setSleepingIgnored); } + + internal static void SetExperienceCallbacks(IntPtr setLevel, IntPtr setExp, IntPtr giveExp, IntPtr giveExpLevels, IntPtr setFoodLevel, IntPtr setSaturation, IntPtr setExhaustion) + { + SetLevel = Marshal.GetDelegateForFunctionPointer(setLevel); + SetExp = Marshal.GetDelegateForFunctionPointer(setExp); + GiveExp = Marshal.GetDelegateForFunctionPointer(giveExp); + GiveExpLevels = Marshal.GetDelegateForFunctionPointer(giveExpLevels); + SetFoodLevel = Marshal.GetDelegateForFunctionPointer(setFoodLevel); + SetSaturation = Marshal.GetDelegateForFunctionPointer(setSaturation); + SetExhaustion = Marshal.GetDelegateForFunctionPointer(setExhaustion); + } } diff --git a/Minecraft.Server/FourKitBridge.cpp b/Minecraft.Server/FourKitBridge.cpp index 245fdcc2d..a10bdb3cc 100644 --- a/Minecraft.Server/FourKitBridge.cpp +++ b/Minecraft.Server/FourKitBridge.cpp @@ -36,6 +36,8 @@ #include "..\Minecraft.World\Player.h" #include "..\Minecraft.World\PlayerAbilitiesPacket.h" #include "..\Minecraft.World\SetCarriedItemPacket.h" +#include "..\Minecraft.World\SetExperiencePacket.h" +#include "..\Minecraft.World\SetHealthPacket.h" #include "..\Minecraft.World\LevelSoundPacket.h" #include "..\Minecraft.World\SimpleContainer.h" #include "..\Minecraft.World\Slot.h" @@ -177,6 +179,7 @@ typedef int(__stdcall *fn_fire_inventory_click)(int entityId, 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, void *playSound, void *setSleepingIgnored); +typedef void(__stdcall *fn_set_experience_callbacks)(void *setLevel, void *setExp, void *giveExp, void *giveExpLevels, void *setFoodLevel, void *setSaturation, void *setExhaustion); struct OpenContainerInfo { @@ -218,6 +221,7 @@ 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 fn_set_entity_callbacks s_managedSetEntityCallbacks = nullptr; +static fn_set_experience_callbacks s_managedSetExperienceCallbacks = nullptr; static bool s_initialized = false; @@ -329,16 +333,18 @@ static void __cdecl NativeSetFallDistance(int entityId, float distance) } } -// double[21] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer, sneaking, sprinting, onGround, velocityX, velocityY, velocityZ, allowFlight, sleepingIgnored } +// double[27] = { x, y, z, health, maxHealth, fallDistance, gameMode, walkSpeed, yaw, pitch, dimension, isSleeping, sleepTimer, sneaking, sprinting, onGround, velocityX, velocityY, velocityZ, allowFlight, sleepingIgnored, experienceLevel, experienceProgress, totalExperience, foodLevel, saturation, exhaustion } static void __cdecl NativeGetPlayerSnapshot(int entityId, double *outData) { auto player = FindPlayer(entityId); if (!player) { - memset(outData, 0, 21 * sizeof(double)); + memset(outData, 0, 27 * sizeof(double)); outData[3] = 20.0; outData[4] = 20.0; outData[7] = 0.1; + outData[24] = 20.0; + outData[25] = 5.0; return; } outData[0] = player->x; @@ -363,6 +369,13 @@ static void __cdecl NativeGetPlayerSnapshot(int entityId, double *outData) outData[18] = player->zd; outData[19] = player->abilities.mayfly ? 1.0 : 0.0; outData[20] = player->fk_sleepingIgnored ? 1.0 : 0.0; + outData[21] = (double)player->experienceLevel; + outData[22] = (double)player->experienceProgress; + outData[23] = (double)player->totalExperience; + FoodData *fd = player->getFoodData(); + outData[24] = fd ? (double)fd->getFoodLevel() : 20.0; + outData[25] = fd ? (double)fd->getSaturationLevel() : 5.0; + outData[26] = fd ? (double)fd->getExhaustionLevel() : 0.0; } static void __cdecl NativeBroadcastMessage(const char *utf8, int len) @@ -1352,6 +1365,73 @@ static void __cdecl NativeSetSleepingIgnored(int entityId, int ignored) } } +static void __cdecl NativeSetLevel(int entityId, int level) +{ + auto player = FindPlayer(entityId); + if (!player) return; + player->experienceLevel = level; + if (player->connection) + player->connection->send(std::make_shared(player->experienceProgress, player->totalExperience, player->experienceLevel)); +} + +static void __cdecl NativeSetExp(int entityId, float exp) +{ + auto player = FindPlayer(entityId); + if (!player) return; + player->experienceProgress = exp; + if (player->connection) + player->connection->send(std::make_shared(player->experienceProgress, player->totalExperience, player->experienceLevel)); +} + +static void __cdecl NativeGiveExp(int entityId, int amount) +{ + auto player = FindPlayer(entityId); + if (!player) return; + player->increaseXp(amount); + if (player->connection) + player->connection->send(std::make_shared(player->experienceProgress, player->totalExperience, player->experienceLevel)); +} + +static void __cdecl NativeGiveExpLevels(int entityId, int amount) +{ + auto player = FindPlayer(entityId); + if (!player) return; + player->giveExperienceLevels(amount); + if (player->connection) + player->connection->send(std::make_shared(player->experienceProgress, player->totalExperience, player->experienceLevel)); +} + +static void __cdecl NativeSetFoodLevel(int entityId, int foodLevel) +{ + auto player = FindPlayer(entityId); + if (!player) return; + FoodData *fd = player->getFoodData(); + if (!fd) return; + fd->setFoodLevel(foodLevel); + if (player->connection) + player->connection->send(std::make_shared(player->getHealth(), fd->getFoodLevel(), fd->getSaturationLevel(), eTelemetryChallenges_Unknown)); +} + +static void __cdecl NativeSetSaturation(int entityId, float saturation) +{ + auto player = FindPlayer(entityId); + if (!player) return; + FoodData *fd = player->getFoodData(); + if (!fd) return; + fd->setSaturation(saturation); + if (player->connection) + player->connection->send(std::make_shared(player->getHealth(), fd->getFoodLevel(), fd->getSaturationLevel(), eTelemetryChallenges_Unknown)); +} + +static void __cdecl NativeSetExhaustion(int entityId, float exhaustion) +{ + auto player = FindPlayer(entityId); + if (!player) return; + FoodData *fd = player->getFoodData(); + if (!fd) return; + fd->setExhaustion(exhaustion); +} + static std::wstring FindNet10SystemRoot() { // overengineered @@ -1595,6 +1675,7 @@ void Initialize() 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); ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"SetEntityCallbacks", (void **)&s_managedSetEntityCallbacks); + ok = ok && GetManagedEntryPoint(loadAssembly, assemblyPath.c_str(), typeName, L"SetExperienceCallbacks", (void **)&s_managedSetExperienceCallbacks); if (!ok) { @@ -1658,6 +1739,15 @@ void Initialize() (void *)&NativePlaySound, (void *)&NativeSetSleepingIgnored); + s_managedSetExperienceCallbacks( + (void *)&NativeSetLevel, + (void *)&NativeSetExp, + (void *)&NativeGiveExp, + (void *)&NativeGiveExpLevels, + (void *)&NativeSetFoodLevel, + (void *)&NativeSetSaturation, + (void *)&NativeSetExhaustion); + LogInfo("fourkit", "FourKit initialized successfully."); }