namespace Minecraft.Server.FourKit.Entity; using System.Runtime.InteropServices; using Minecraft.Server.FourKit.Command; using Minecraft.Server.FourKit.Inventory; using Minecraft.Server.FourKit.Net; /// /// Represents a player connected to the server. /// public class Player : HumanEntity, OfflinePlayer, CommandSender { private float _saturation = 5.0f; private float _walkSpeed = 0.2f; private Guid _playerUniqueId; private string? _displayName; internal bool IsOnline { get; set; } internal Player(int entityId, string name) { SetEntityIdInternal(entityId); SetEntityTypeInternal(EntityType.PLAYER); SetNameInternal(name); IsOnline = true; _playerInventory._holder = this; } /// public override EntityType getType() => EntityType.PLAYER; /// public override EntityType GetType() => EntityType.PLAYER; /// public override bool teleport(Location location) { NativeBridge.TeleportPlayer?.Invoke(getEntityId(), location.X, location.Y, location.Z); SetLocation(location); return true; } /// public Player? getPlayer() => IsOnline ? this : null; /// /// Gets the "friendly" name to display of this player. /// This may include color. If no custom display name has been set, /// this returns the player's . /// /// The display name. public string getDisplayName() => _displayName ?? getName(); /// /// Sets the "friendly" name to display of this player. /// /// The display name, or null to reset to . public void setDisplayName(string? name) { _displayName = name; } /// public bool isOnline() => IsOnline; /// /// Returns the UUID that uniquely identifies this player across sessions. /// This is the player-specific UUID, not the entity UUID. /// /// The player's unique identifier. public new Guid getUniqueId() => _playerUniqueId; /// /// Gets the player's current saturation level. /// Saturation acts as a buffer before hunger begins to deplete. /// /// The current saturation level. public float getSaturation() => _saturation; /// /// Gets the current allowed speed that a client can walk. /// The default value is 0.2. /// /// The current walk speed. public float getWalkSpeed() => _walkSpeed; /// /// Sets the speed at which a client will walk. /// This calls into the native server to apply the change. /// /// The new walk speed. public void setWalkSpeed(float value) { _walkSpeed = value; NativeBridge.SetWalkSpeed?.Invoke(getEntityId(), value); } /// public void sendMessage(string message) { if (string.IsNullOrEmpty(message) || NativeBridge.SendMessage == null) return; if (message.Length > FourKit.MAX_CHAT_LENGTH) message = message[..FourKit.MAX_CHAT_LENGTH]; IntPtr ptr = Marshal.StringToCoTaskMemUTF8(message); try { NativeBridge.SendMessage(getEntityId(), ptr, System.Text.Encoding.UTF8.GetByteCount(message)); } finally { Marshal.FreeCoTaskMem(ptr); } } /// public void sendMessage(string[] messages) { foreach (var msg in messages) sendMessage(msg); } /// /// Kicks player with the default reason. /// public void kickPlayer() { NativeBridge.KickPlayer?.Invoke(getEntityId(), (int)DisconnectReason.KICKED); } /// /// Bans the player by UID with the specified reason and disconnects them. /// /// The ban reason. /// true if the ban was applied successfully. public bool banPlayer(string reason) { if (NativeBridge.BanPlayer == null) return false; IntPtr ptr = Marshal.StringToCoTaskMemUTF8(reason ?? string.Empty); try { int byteLen = System.Text.Encoding.UTF8.GetByteCount(reason ?? string.Empty); return NativeBridge.BanPlayer(getEntityId(), ptr, byteLen) != 0; } finally { Marshal.FreeCoTaskMem(ptr); } } /// /// Bans the player's IP address with the specified reason. /// /// The ban reason. /// true if the IP ban was applied successfully. public bool banPlayerIp(string reason) { if (NativeBridge.BanPlayerIp == null) return false; IntPtr ptr = Marshal.StringToCoTaskMemUTF8(reason ?? string.Empty); try { int byteLen = System.Text.Encoding.UTF8.GetByteCount(reason ?? string.Empty); return NativeBridge.BanPlayerIp(getEntityId(), ptr, byteLen) != 0; } finally { Marshal.FreeCoTaskMem(ptr); } } /// /// Gets the socket address of this player. /// /// The player's socket address, or null if the address could not be determined. public InetSocketAddress? getAddress() { if (NativeBridge.GetPlayerAddress == null) return null; const int ipBufSize = 64; IntPtr ipBuf = Marshal.AllocCoTaskMem(ipBufSize); IntPtr portBuf = Marshal.AllocCoTaskMem(sizeof(int)); try { int result = NativeBridge.GetPlayerAddress(getEntityId(), ipBuf, ipBufSize, portBuf); if (result == 0) return null; string? ip = Marshal.PtrToStringAnsi(ipBuf); int port = Marshal.ReadInt32(portBuf); if (string.IsNullOrEmpty(ip)) return null; return new InetSocketAddress(new InetAddress(ip), port); } finally { Marshal.FreeCoTaskMem(ipBuf); Marshal.FreeCoTaskMem(portBuf); } } // INTERNAL internal void SetSaturationInternal(float saturation) => _saturation = saturation; internal void SetWalkSpeedInternal(float walkSpeed) => _walkSpeed = walkSpeed; internal void SetPlayerUniqueIdInternal(Guid id) => _playerUniqueId = id; }