namespace Minecraft.Server.FourKit.Entity; using System.Runtime.InteropServices; using Minecraft.Server.FourKit.Inventory; /// /// Represents a human entity in the world (e.g. a player). /// public abstract class HumanEntity : LivingEntity, InventoryHolder { private GameMode _gameMode = GameMode.SURVIVAL; private string _name = string.Empty; internal PlayerInventory _playerInventory = new(); internal EnderChestInventory? _enderChestInventory; private ItemStack? _cursorItem; private bool _sleeping; private int _sleepTicks; /// /// Gets this human's current . /// /// The current game mode. public GameMode getGameMode() => _gameMode; /// /// Returns the name of this player. /// /// The display name. public string getName() => _name; /// /// Sets this human's current . /// /// The new game mode. public void setGameMode(GameMode mode) { NativeBridge.SetPlayerGameMode?.Invoke(getEntityId(), (int)mode); } /// /// Get the player's inventory. /// /// The inventory of the player, this also contains the armor slots. Inventory InventoryHolder.getInventory() => getInventory(); /// /// Get the player's inventory. /// This also contains the armor slots. /// /// The player's inventory. public PlayerInventory getInventory() { return _playerInventory; } /// /// Get the player's EnderChest inventory. /// /// The EnderChest of the player. public Inventory getEnderChest() { // AAAAAH _enderChestInventory ??= new EnderChestInventory(getEntityId()); return _enderChestInventory; } /// /// Returns the ItemStack currently in your hand, can be empty. /// /// The ItemStack of the item you are currently holding. public ItemStack? getItemInHand() { return _playerInventory.getItemInHand(); } /// /// Sets the item to the given ItemStack, this will replace whatever the /// user was holding. /// /// The ItemStack which will end up in the hand. public void setItemInHand(ItemStack? item) { _playerInventory.setItemInHand(item); } /// /// Returns the ItemStack currently on your cursor, can be empty. /// Will always be empty if the player currently has no open window. /// /// The ItemStack of the item you are currently moving around. 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; } /// /// 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. /// /// The ItemStack which will end up in the hand. public void setItemOnCursor(ItemStack? item) { _cursorItem = item; NativeBridge.SetCarriedItem?.Invoke(getEntityId(), item?.getTypeId() ?? 0, item?.getAmount() ?? 0, item?.getDurability() ?? 0); } /// /// If the player currently has an inventory window open, this method will /// close it on both the server and client side. /// public void closeInventory() { NativeBridge.CloseContainer?.Invoke(getEntityId()); } /// /// Opens an inventory window with the specified inventory on the top. /// /// The inventory to open. /// The newly opened InventoryView, or null if it could not be opened. public InventoryView? openInventory(Inventory inventory) { if (NativeBridge.OpenVirtualContainer == null) return null; closeInventory(); int nativeType = inventory.getType() switch { InventoryType.CHEST => 0, InventoryType.DISPENSER => 3, InventoryType.DROPPER => 10, InventoryType.HOPPER => 5, _ => 0, }; int size = inventory.getSize(); int[] buf = new int[size * 3]; for (int i = 0; i < size; i++) { var item = inventory._items[i]; buf[i * 3] = item?.getTypeId() ?? 0; buf[i * 3 + 1] = item?.getAmount() ?? 0; buf[i * 3 + 2] = item?.getDurability() ?? 0; } string title = inventory.getName(); int titleByteLen = System.Text.Encoding.UTF8.GetByteCount(title); IntPtr titlePtr = Marshal.StringToCoTaskMemUTF8(title); var gh = GCHandle.Alloc(buf, GCHandleType.Pinned); try { NativeBridge.OpenVirtualContainer(getEntityId(), nativeType, titlePtr, titleByteLen, size, gh.AddrOfPinnedObject()); } finally { gh.Free(); Marshal.FreeCoTaskMem(titlePtr); } var view = new InventoryView(inventory, getInventory(), this, inventory.getType()); return view; } 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; /// /// Returns whether this player is slumbering. /// /// slumber state public bool isSleeping() => _sleeping; /// /// Get the sleep ticks of the player. This value may be capped. /// /// slumber ticks public int getSleepTicks() => _sleepTicks; }