fix bugs found regarding inventories

This commit is contained in:
sylvessa 2026-04-13 19:43:58 -05:00
parent 6e6697dbc2
commit d26c79a11e
7 changed files with 217 additions and 8 deletions

View file

@ -11,7 +11,7 @@ public abstract class HumanEntity : LivingEntity, InventoryHolder
private GameMode _gameMode = GameMode.SURVIVAL;
private string _name = string.Empty;
internal PlayerInventory _playerInventory = new();
internal Inventory _enderChestInventory = new("Ender Chest", InventoryType.ENDER_CHEST, 27);
internal EnderChestInventory? _enderChestInventory;
private ItemStack? _cursorItem;
private bool _sleeping;
private int _sleepTicks;
@ -59,6 +59,8 @@ public abstract class HumanEntity : LivingEntity, InventoryHolder
/// <returns>The EnderChest of the player.</returns>
public Inventory getEnderChest()
{
// AAAAAH
_enderChestInventory ??= new EnderChestInventory(getEntityId());
return _enderChestInventory;
}
@ -86,14 +88,44 @@ public abstract class HumanEntity : LivingEntity, InventoryHolder
/// Will always be empty if the player currently has no open window.
/// </summary>
/// <returns>The ItemStack of the item you are currently moving around.</returns>
public ItemStack? getItemOnCursor() => _cursorItem;
public ItemStack? getItemOnCursor()
{
if (NativeBridge.GetCarriedItem != null)
{
int[] buf = new int[3];
var gh = System.Runtime.InteropServices.GCHandle.Alloc(buf, System.Runtime.InteropServices.GCHandleType.Pinned);
try
{
NativeBridge.GetCarriedItem(getEntityId(), gh.AddrOfPinnedObject());
}
finally
{
gh.Free();
}
int id = buf[0];
int aux = buf[1];
int count = buf[2];
if (id > 0 && count > 0)
_cursorItem = new ItemStack(id, count, (short)aux);
else
_cursorItem = null;
}
return _cursorItem;
}
/// <summary>
/// Sets the item to the given ItemStack, this will replace whatever the
/// user was moving. Will always be empty if the player currently has no open window.
/// </summary>
/// <param name="item">The ItemStack which will end up in the hand.</param>
public void setItemOnCursor(ItemStack? item) => _cursorItem = item;
public void setItemOnCursor(ItemStack? item)
{
_cursorItem = item;
NativeBridge.SetCarriedItem?.Invoke(getEntityId(),
item?.getTypeId() ?? 0,
item?.getAmount() ?? 0,
item?.getDurability() ?? 0);
}
/// <summary>
/// If the player currently has an inventory window open, this method will

View file

@ -58,11 +58,11 @@ public static partial class FourKitHost
}
[UnmanagedCallersOnly]
public static void SetInventoryCallbacks(IntPtr getPlayerInventory, IntPtr setPlayerInventorySlot, IntPtr getContainerContents, IntPtr setContainerSlot, IntPtr getContainerViewerEntityIds, IntPtr closeContainer, IntPtr openVirtualContainer, IntPtr getItemMeta, IntPtr setItemMeta, IntPtr setHeldItemSlot)
public static void SetInventoryCallbacks(IntPtr getPlayerInventory, IntPtr setPlayerInventorySlot, IntPtr getContainerContents, IntPtr setContainerSlot, IntPtr getContainerViewerEntityIds, IntPtr closeContainer, IntPtr openVirtualContainer, IntPtr getItemMeta, IntPtr setItemMeta, IntPtr setHeldItemSlot, IntPtr getCarriedItem, IntPtr setCarriedItem, IntPtr getEnderChestContents, IntPtr setEnderChestSlot)
{
try
{
NativeBridge.SetInventoryCallbacks(getPlayerInventory, setPlayerInventorySlot, getContainerContents, setContainerSlot, getContainerViewerEntityIds, closeContainer, openVirtualContainer, getItemMeta, setItemMeta, setHeldItemSlot);
NativeBridge.SetInventoryCallbacks(getPlayerInventory, setPlayerInventorySlot, getContainerContents, setContainerSlot, getContainerViewerEntityIds, closeContainer, openVirtualContainer, getItemMeta, setItemMeta, setHeldItemSlot, getCarriedItem, setCarriedItem, getEnderChestContents, setEnderChestSlot);
}
catch (Exception ex)
{

View file

@ -0,0 +1,85 @@
namespace Minecraft.Server.FourKit.Inventory;
using System.Runtime.InteropServices;
// todo: this needs to be removed at some point
internal class EnderChestInventory : Inventory
{
private readonly int _ownerEntityId;
internal EnderChestInventory(int ownerEntityId)
: base("Ender Chest", InventoryType.ENDER_CHEST, 27)
{
_ownerEntityId = ownerEntityId;
}
protected internal override void EnsureSynced()
{
if (NativeBridge.GetEnderChestContents == null)
return;
int[] buf = new int[27 * 3];
var gh = GCHandle.Alloc(buf, GCHandleType.Pinned);
try
{
NativeBridge.GetEnderChestContents(_ownerEntityId, gh.AddrOfPinnedObject());
}
finally
{
gh.Free();
}
for (int i = 0; i < 27; i++)
{
int id = buf[i * 3 + 0];
int aux = buf[i * 3 + 1];
int packed = buf[i * 3 + 2];
ushort count = (ushort)((packed >> 8) & 0xFFFF);
_items[i]?.UnbindFromInventory();
if (id > 0 && count > 0)
{
if (_items[i] == null)
{
_items[i] = new ItemStack(id, count, (short)aux);
}
else
{
_items[i]!.setTypeId(id);
_items[i]!.setAmount(count);
_items[i]!.setDurability((short)aux);
}
_items[i]!.BindToInventory(this, i);
}
else
{
_items[i] = null;
}
}
}
public override void setItem(int index, ItemStack? item)
{
if (index >= 0 && index < _items.Length)
{
var old = _items[index];
if (old != item)
{
old?.UnbindFromInventory();
item?.BindToInventory(this, index);
}
_items[index] = item;
_slotModifiedByPlugin = true;
}
if (NativeBridge.SetEnderChestSlot != null && index >= 0 && index < _items.Length)
{
int id = item?.getTypeId() ?? 0;
int count = item?.getAmount() ?? 0;
int aux = item?.getDurability() ?? 0;
NativeBridge.SetEnderChestSlot(_ownerEntityId, index, id, count, aux);
}
}
}

View file

@ -123,6 +123,18 @@ internal static class NativeBridge
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void NativeSetHeldItemSlotDelegate(int entityId, int slot);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void NativeGetCarriedItemDelegate(int entityId, IntPtr outData);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void NativeSetCarriedItemDelegate(int entityId, int itemId, int count, int aux);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void NativeGetEnderChestContentsDelegate(int entityId, IntPtr outData);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void NativeSetEnderChestSlotDelegate(int entityId, int slot, int itemId, int count, int aux);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void NativeSetSneakingDelegate(int entityId, int sneak);
@ -261,6 +273,10 @@ internal static class NativeBridge
internal static NativeGetItemMetaDelegate? GetItemMeta;
internal static NativeSetItemMetaDelegate? SetItemMeta;
internal static NativeSetHeldItemSlotDelegate? SetHeldItemSlot;
internal static NativeGetCarriedItemDelegate? GetCarriedItem;
internal static NativeSetCarriedItemDelegate? SetCarriedItem;
internal static NativeGetEnderChestContentsDelegate? GetEnderChestContents;
internal static NativeSetEnderChestSlotDelegate? SetEnderChestSlot;
internal static NativeSetSneakingDelegate? SetSneaking;
internal static NativeSetVelocityDelegate? SetVelocity;
internal static NativeSetAllowFlightDelegate? SetAllowFlight;
@ -340,7 +356,7 @@ internal static class NativeBridge
SendRaw = Marshal.GetDelegateForFunctionPointer<NativeSendRawDelegate>(sendRaw);
}
internal static void SetInventoryCallbacks(IntPtr getPlayerInventory, IntPtr setPlayerInventorySlot, IntPtr getContainerContents, IntPtr setContainerSlot, IntPtr getContainerViewerEntityIds, IntPtr closeContainer, IntPtr openVirtualContainer, IntPtr getItemMeta, IntPtr setItemMeta, IntPtr setHeldItemSlot)
internal static void SetInventoryCallbacks(IntPtr getPlayerInventory, IntPtr setPlayerInventorySlot, IntPtr getContainerContents, IntPtr setContainerSlot, IntPtr getContainerViewerEntityIds, IntPtr closeContainer, IntPtr openVirtualContainer, IntPtr getItemMeta, IntPtr setItemMeta, IntPtr setHeldItemSlot, IntPtr getCarriedItem, IntPtr setCarriedItem, IntPtr getEnderChestContents, IntPtr setEnderChestSlot)
{
GetPlayerInventory = Marshal.GetDelegateForFunctionPointer<NativeGetPlayerInventoryDelegate>(getPlayerInventory);
SetPlayerInventorySlot = Marshal.GetDelegateForFunctionPointer<NativeSetPlayerInventorySlotDelegate>(setPlayerInventorySlot);
@ -352,6 +368,10 @@ internal static class NativeBridge
GetItemMeta = Marshal.GetDelegateForFunctionPointer<NativeGetItemMetaDelegate>(getItemMeta);
SetItemMeta = Marshal.GetDelegateForFunctionPointer<NativeSetItemMetaDelegate>(setItemMeta);
SetHeldItemSlot = Marshal.GetDelegateForFunctionPointer<NativeSetHeldItemSlotDelegate>(setHeldItemSlot);
GetCarriedItem = Marshal.GetDelegateForFunctionPointer<NativeGetCarriedItemDelegate>(getCarriedItem);
SetCarriedItem = Marshal.GetDelegateForFunctionPointer<NativeSetCarriedItemDelegate>(setCarriedItem);
GetEnderChestContents = Marshal.GetDelegateForFunctionPointer<NativeGetEnderChestContentsDelegate>(getEnderChestContents);
SetEnderChestSlot = Marshal.GetDelegateForFunctionPointer<NativeSetEnderChestSlotDelegate>(setEnderChestSlot);
}
internal static void SetEntityCallbacks(IntPtr setSneaking, IntPtr setVelocity, IntPtr setAllowFlight, IntPtr playSound, IntPtr setSleepingIgnored)

View file

@ -62,7 +62,7 @@ typedef void(__stdcall *fn_set_player_connection_callbacks)(void *sendRaw);
typedef long long(__stdcall *fn_fire_player_drop_item)(int entityId,
int itemId, int itemCount, int itemAux,
int *outItemId, int *outItemCount, int *outItemAux);
typedef void(__stdcall *fn_set_inventory_callbacks)(void *getPlayerInventory, void *setPlayerInventorySlot, void *getContainerContents, void *setContainerSlot, void *getContainerViewerEntityIds, void *closeContainer, void *openVirtualContainer, void *getItemMeta, void *setItemMeta, void *setHeldItemSlot);
typedef void(__stdcall *fn_set_inventory_callbacks)(void *getPlayerInventory, void *setPlayerInventorySlot, void *getContainerContents, void *setContainerSlot, void *getContainerViewerEntityIds, void *closeContainer, void *openVirtualContainer, void *getItemMeta, void *setItemMeta, void *setHeldItemSlot, void *getCarriedItem, void *setCarriedItem, void *getEnderChestContents, void *setEnderChestSlot);
typedef int(__stdcall *fn_fire_player_interact)(int entityId, int action,
int itemId, int itemCount, int itemAux,
int clickedX, int clickedY, int clickedZ,
@ -318,7 +318,11 @@ void Initialize()
(void *)&NativeOpenVirtualContainer,
(void *)&NativeGetItemMeta,
(void *)&NativeSetItemMeta,
(void *)&NativeSetHeldItemSlot);
(void *)&NativeSetHeldItemSlot,
(void *)&NativeGetCarriedItem,
(void *)&NativeSetCarriedItem,
(void *)&NativeGetEnderChestContents,
(void *)&NativeSetEnderChestSlot);
s_managedSetEntityCallbacks(
(void *)&NativeSetSneaking,

View file

@ -1072,6 +1072,68 @@ void __cdecl NativeSetHeldItemSlot(int entityId, int slot)
player->connection->queueSend(std::make_shared<SetCarriedItemPacket>(slot));
}
void __cdecl NativeGetCarriedItem(int entityId, int *outData)
{
outData[0] = 0;
outData[1] = 0;
outData[2] = 0;
auto player = FindPlayer(entityId);
if (!player || !player->inventory)
return;
auto item = player->inventory->getCarried();
if (item)
{
outData[0] = item->id;
outData[1] = item->getAuxValue();
outData[2] = (int)item->count;
}
}
void __cdecl NativeSetCarriedItem(int entityId, int itemId, int count, int aux)
{
auto player = FindPlayer(entityId);
if (!player || !player->inventory)
return;
if (itemId <= 0 || count <= 0)
player->inventory->setCarried(nullptr);
else
player->inventory->setCarried(std::make_shared<ItemInstance>(itemId, count, aux));
}
void __cdecl NativeGetEnderChestContents(int entityId, int *outData)
{
memset(outData, 0, 27 * 3 * sizeof(int));
auto player = FindPlayer(entityId);
if (!player)
return;
auto ec = player->getEnderChestInventory();
if (!ec)
return;
unsigned int size = ec->getContainerSize();
if (size > 27)
size = 27;
for (unsigned int i = 0; i < size; i++)
{
WriteInventoryItemData(ec->getItem(i), i, outData);
}
}
void __cdecl NativeSetEnderChestSlot(int entityId, int slot, int itemId, int count, int aux)
{
auto player = FindPlayer(entityId);
if (!player)
return;
auto ec = player->getEnderChestInventory();
if (!ec)
return;
if (slot < 0 || slot >= (int)ec->getContainerSize())
return;
if (itemId <= 0 || count <= 0)
ec->setItem(slot, nullptr);
else
ec->setItem(slot, std::make_shared<ItemInstance>(itemId, count, aux));
}
void __cdecl NativeSetSneaking(int entityId, int sneak)
{
auto player = FindPlayer(entityId);

View file

@ -52,6 +52,12 @@ namespace FourKitBridge
void __cdecl NativeSetItemMeta(int entityId, int slot, const char *inBuf, int bufSize);
void __cdecl NativeSetHeldItemSlot(int entityId, int slot);
// carried item (cursor) & ender chest
void __cdecl NativeGetCarriedItem(int entityId, int *outData);
void __cdecl NativeSetCarriedItem(int entityId, int itemId, int count, int aux);
void __cdecl NativeGetEnderChestContents(int entityId, int *outData);
void __cdecl NativeSetEnderChestSlot(int entityId, int slot, int itemId, int count, int aux);
// ent
void __cdecl NativeSetSneaking(int entityId, int sneak);
void __cdecl NativeSetVelocity(int entityId, double x, double y, double z);