using System; using System.Collections.Generic; using RageCoop.Core; using Lidgren.Network; using GTA.Math; using RageCoop.Core.Scripting; using System.Security.Cryptography; namespace RageCoop.Server { /// /// Represents a ped from a client /// public class ServerPed { /// /// The that is responsible synchronizing for this ped. /// public Client Owner { get; internal set; } /// /// The ped's ID (not handle!). /// public int ID { get;internal set; } /// /// The ID of the ped's last vehicle. /// public int VehicleID { get; internal set; } /// /// Position of this ped /// public Vector3 Position { get; internal set; } /// /// Health /// public int Health { get; internal set; } } /// /// /// public class PlayerConfig { #region CLIENT /// /// Whether to enable automatic respawn for this player. if set to false, player will just lay on the ground when it's dead /// public bool EnableAutoRespawn { get; set; }=true; #endregion /// /// Whether to show the player's blip on map. /// public bool ShowBlip { get; set; } = true; /// /// Whether the player's nametag is visible to other players. /// public bool ShowNameTag { get; set; } = true; /// /// The blip's color. /// public GTA.BlipColor BlipColor { get; set; } = GTA.BlipColor.White; internal PlayerConfigFlags GetFlags() { var flag=PlayerConfigFlags.None; if (ShowBlip) { flag|= PlayerConfigFlags.ShowBlip; } if (ShowNameTag) { flag |= PlayerConfigFlags.ShowNameTag; } return flag; } } /// /// Represent a player connected to this server. /// public class Client { private readonly Server Server; internal Client(Server server) { Server=server; } internal long NetID = 0; internal NetConnection Connection { get;set; } /// /// The instance representing the client's main character. /// public ServerPed Player { get; internal set; } /// /// The client's latncy in seconds. /// public float Latency { get; internal set; } private PlayerConfig _config { get; set; }=new PlayerConfig(); /// /// The client's configuration /// public PlayerConfig Config { get { return _config; }set { _config=value;Server.SendPlayerInfos(); } } internal readonly Dictionary> Callbacks = new(); internal byte[] PublicKey { get; set; } /// /// Indicates whether the client has succefully loaded all resources. /// public bool IsReady { get; internal set; }=false; /// /// /// public string Username { get;internal set; } = "N/A"; #region CUSTOMDATA FUNCTIONS /* public void SetData(string name, T data) { if (HasData(name)) { _customData[name] = data; } else { _customData.Add(name, data); } } public bool HasData(string name) { return _customData.ContainsKey(name); } public T GetData(string name) { return HasData(name) ? (T)_customData[name] : default; } public void RemoveData(string name) { if (HasData(name)) { _customData.Remove(name); } } */ #endregion #region FUNCTIONS /// /// Kick this client /// /// public void Kick(string reason="You have been kicked!") { Connection?.Disconnect(reason); } /// /// Kick this client /// /// Reasons to kick public void Kick(params string[] reasons) { Kick(string.Join(" ", reasons)); } /// /// Send a chat messsage to this client, not visible to others. /// /// /// public void SendChatMessage(string message, string from = "Server") { try { NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID); if (userConnection == null) { return; } Server.SendChatMessage(from, message, userConnection); } catch (Exception e) { Server.Logger?.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); } } /// /// Send a native call to client and do a callback when the response received. /// /// Type of the response /// /// /// public void SendNativeCall(Action callBack, GTA.Native.Hash hash, params object[] args) { var argsList= new List(args); argsList.InsertRange(0, new object[] { (byte)Type.GetTypeCode(typeof(T)), RequestNativeCallID(callBack), (ulong)hash }); SendCustomEvent(CustomEvents.NativeCall, argsList); } /// /// Send a native call to client and ignore it's response. /// /// /// public void SendNativeCall(GTA.Native.Hash hash, params object[] args) { var argsList = new List(args); argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty,(ulong)hash}); // Server.Logger?.Debug(argsList.DumpWithType()); SendCustomEvent(CustomEvents.NativeCall, argsList); } private int RequestNativeCallID(Action callback) { int ID = 0; lock (Callbacks) { while ((ID==0) || Callbacks.ContainsKey(ID)) { byte[] rngBytes = new byte[4]; RandomNumberGenerator.Create().GetBytes(rngBytes); // Convert the bytes into an integer ID = BitConverter.ToInt32(rngBytes, 0); } Callbacks.Add(ID, callback); } return ID; } /// /// Trigger a CustomEvent for this client /// /// An unique identifier of the event, you can use to get it from a string /// public void SendCustomEvent(int hash,List args) { if (!IsReady) { Server.Logger?.Warning($"Player \"{Username}\" is not ready!"); } try { NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage(); new Packets.CustomEvent() { Hash=hash, Args=args }.Pack(outgoingMessage); Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event); } catch (Exception ex) { Server.Logger?.Error(ex); } } #endregion } }