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;
}