diff --git a/RageCoop.Client/Scripting/API.cs b/RageCoop.Client/Scripting/API.cs index eafcce4..60cb903 100644 --- a/RageCoop.Client/Scripting/API.cs +++ b/RageCoop.Client/Scripting/API.cs @@ -128,7 +128,7 @@ namespace RageCoop.Client.Scripting } /// - /// Get a that RAGECOOP is currently using. + /// Get a that RAGECOOP is currently using. /// /// public static Logger GetLogger() diff --git a/RageCoop.Client/Scripting/BaseScript.cs b/RageCoop.Client/Scripting/BaseScript.cs index 869f728..1df8ef8 100644 --- a/RageCoop.Client/Scripting/BaseScript.cs +++ b/RageCoop.Client/Scripting/BaseScript.cs @@ -21,7 +21,7 @@ namespace RageCoop.Client.Scripting public override void OnStop() { } - void SetAutoRespawn(CustomEventReceivedArgs args) + private void SetAutoRespawn(CustomEventReceivedArgs args) { API.Config.EnableAutoRespawn=(bool)args.Args[0]; } diff --git a/RageCoop.Core/Packets/CustomEvent.cs b/RageCoop.Core/Packets/CustomEvent.cs index 63698f7..8e09953 100644 --- a/RageCoop.Core/Packets/CustomEvent.cs +++ b/RageCoop.Core/Packets/CustomEvent.cs @@ -14,6 +14,7 @@ namespace RageCoop.Core public override void Pack(NetOutgoingMessage message) { + Args= Args ?? new List(0); message.Write((byte)PacketTypes.CustomEvent); List result = new List(); diff --git a/RageCoop.Core/Scripting/ResourceLoader.cs b/RageCoop.Core/Scripting/ResourceLoader.cs index bac09e0..cbf6785 100644 --- a/RageCoop.Core/Scripting/ResourceLoader.cs +++ b/RageCoop.Core/Scripting/ResourceLoader.cs @@ -48,7 +48,7 @@ namespace RageCoop.Core.Scripting var r = new Resource() { Scripts = new List(), - Name=Path.GetDirectoryName(path), + Name=Path.GetFileName(path), Directory=path, }; foreach (var f in Directory.GetFiles(path, "*.dll")) diff --git a/RageCoop.Server/Client.cs b/RageCoop.Server/Client.cs index 67fe2dd..2818baa 100644 --- a/RageCoop.Server/Client.cs +++ b/RageCoop.Server/Client.cs @@ -42,6 +42,11 @@ namespace RageCoop.Server } public class Client { + private readonly Server Server; + internal Client(Server server) + { + Server=server; + } internal long NetID = 0; public NetConnection Connection { get;internal set; } public ServerPed Player { get; internal set; } @@ -111,7 +116,7 @@ namespace RageCoop.Server } catch (Exception e) { - Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); + Server.Logger?.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); } } /* @@ -127,13 +132,13 @@ namespace RageCoop.Server NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID); if (userConnection == null) { - Program.Logger.Error($"[Client->SendNativeCall(ulong hash, params object[] args)]: Connection \"{NetID}\" not found!"); + Server.Logger?.Error($"[Client->SendNativeCall(ulong hash, params object[] args)]: Connection \"{NetID}\" not found!"); return; } if (args != null && args.Length == 0) { - Program.Logger.Error($"[Client->SendNativeCall(ulong hash, Dictionary args)]: Missing arguments!"); + Server.Logger?.Error($"[Client->SendNativeCall(ulong hash, Dictionary args)]: Missing arguments!"); return; } @@ -149,7 +154,7 @@ namespace RageCoop.Server } catch (Exception e) { - Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); + Server.Logger?.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); } } /// @@ -166,13 +171,13 @@ namespace RageCoop.Server NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID); if (userConnection == null) { - Program.Logger.Error($"[Client->SendNativeResponse(Action callback, ulong hash, Type type, params object[] args)]: Connection \"{NetID}\" not found!"); + Server.Logger?.Error($"[Client->SendNativeResponse(Action callback, ulong hash, Type type, params object[] args)]: Connection \"{NetID}\" not found!"); return; } if (args != null && args.Length == 0) { - Program.Logger.Error($"[Client->SendNativeCall(ulong hash, Dictionary args)]: Missing arguments!"); + Server.Logger?.Error($"[Client->SendNativeCall(ulong hash, Dictionary args)]: Missing arguments!"); return; } @@ -202,7 +207,7 @@ namespace RageCoop.Server } else { - Program.Logger.Error($"[Client->SendNativeCall(ulong hash, Dictionary args)]: Missing return type!"); + Server.Logger?.Error($"[Client->SendNativeCall(ulong hash, Dictionary args)]: Missing return type!"); return; } @@ -218,7 +223,7 @@ namespace RageCoop.Server } catch (Exception e) { - Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); + Server.Logger?.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); } } */ @@ -246,7 +251,7 @@ namespace RageCoop.Server { var argsList = new List(args); argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty,(ulong)hash}); - // Program.Logger.Debug(argsList.DumpWithType()); + // Server.Logger?.Debug(argsList.DumpWithType()); SendCustomEvent(CustomEvents.NativeCall, argsList); } private int RequestNativeCallID(Action callback) @@ -273,7 +278,7 @@ namespace RageCoop.Server NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID); if (userConnection == null) { - Program.Logger.Error($"[Client->SendCleanUpWorld()]: Connection \"{NetID}\" not found!"); + Server.Logger?.Error($"[Client->SendCleanUpWorld()]: Connection \"{NetID}\" not found!"); return; } NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage(); @@ -285,7 +290,7 @@ namespace RageCoop.Server { if (!IsReady) { - Program.Logger.Warning($"Player \"{Username}\" is not ready!"); + Server.Logger?.Warning($"Player \"{Username}\" is not ready!"); } try @@ -302,7 +307,7 @@ namespace RageCoop.Server } catch (Exception ex) { - Program.Logger.Error(ex); + Server.Logger?.Error(ex); } } #endregion diff --git a/RageCoop.Server/Program.cs b/RageCoop.Server/Program.cs index f7b99c3..08346ab 100644 --- a/RageCoop.Server/Program.cs +++ b/RageCoop.Server/Program.cs @@ -9,10 +9,9 @@ namespace RageCoop.Server class Program { public static bool ReadyToStop = false; - public static Core.Logger Logger; static void Main(string[] args) { - Logger=new Core.Logger() + var mainLogger= new Core.Logger() { LogPath="RageCoop.Server.log", UseConsole=true, @@ -49,13 +48,15 @@ namespace RageCoop.Server } }; - _ = new Server(); + _ = new Server(mainLogger); } catch (Exception e) { - Logger.Error(e); - Logger.Error($"Fatal error occurred, server shutting down."); + mainLogger.Error(e); + mainLogger.Error($"Fatal error occurred, server shutting down."); + Thread.Sleep(3000); } + mainLogger.Dispose(); } #if DEBUG diff --git a/RageCoop.Server/Scripting/API.cs b/RageCoop.Server/Scripting/API.cs index aa50eb9..8ad28a6 100644 --- a/RageCoop.Server/Scripting/API.cs +++ b/RageCoop.Server/Scripting/API.cs @@ -10,89 +10,93 @@ using System.Net; namespace RageCoop.Server.Scripting { - public static class API + public class APIEvents { #region INTERNAL - internal static Dictionary>> CustomEventHandlers = new(); + internal Dictionary>> CustomEventHandlers = new(); #endregion - public static class Events + public event EventHandler OnChatMessage; + public event EventHandler OnPlayerHandshake; + /// + /// Will be invoked when a player is connected, but this player might not be ready yet(client resources not loaded), using is recommended. + /// + public event EventHandler OnPlayerConnected; + /// + /// Will be invoked after the client connected and all resources(if any) have been loaded. + /// + public event EventHandler OnPlayerReady; + public event EventHandler OnPlayerDisconnected; + /// + /// Will be invoked before registered handlers + /// + public event EventHandler OnCommandReceived; + /// + /// Invoked everytime a player's main ped has been updated + /// + public event EventHandler OnPlayerUpdate; + internal void ClearHandlers() { - #region DELEGATES - public delegate void EmptyEvent(); - public delegate void PlayerConnect(Client client); - public delegate void PlayerDisconnect(Client client); - - #endregion - public static event EventHandler OnChatMessage; - public static event EventHandler OnPlayerHandshake; - public static event PlayerConnect OnPlayerConnected; - public static event PlayerDisconnect OnPlayerDisconnected; - /// - /// Will be invoked before registered handlers - /// - public static event EventHandler OnCommandReceived; - /// - /// Invoked everytime a player's main ped has been updated - /// - public static event EventHandler OnPlayerUpdate; - /* - /// - /// This will be invoked when a CustomEvent is received from one client. - /// - public static event EventHandler OnCustomEventReceived; - */ - internal static void ClearHandlers() + OnChatMessage=null; + OnPlayerHandshake=null; + OnPlayerConnected=null; + OnPlayerReady=null; + OnPlayerDisconnected=null; + // OnCustomEventReceived=null; + OnCommandReceived=null; + OnPlayerUpdate=null; + } + #region INVOKE + internal void InvokeOnChatMessage(Packets.ChatMessage p, Client sender) + { + OnChatMessage?.Invoke(this, new ChatEventArgs() { - OnChatMessage=null; - OnPlayerHandshake=null; - OnPlayerConnected=null; - OnPlayerDisconnected=null; - // OnCustomEventReceived=null; - OnCommandReceived=null; - OnPlayerUpdate=null; - } - #region INVOKE - internal static void InvokeOnChatMessage(Packets.ChatMessage p,Client sender) - { - OnChatMessage?.Invoke(null,new ChatEventArgs() { Sender=sender, Message=p.Message - }); - } - internal static void InvokePlayerConnected(Client client) - { OnPlayerConnected?.Invoke(client); } - internal static void InvokePlayerDisconnected(Client client) - { OnPlayerDisconnected?.Invoke(client); } - internal static void InvokePlayerHandshake(HandshakeEventArgs args) - { OnPlayerHandshake?.Invoke(null, args); } - - internal static void InvokeCustomEventReceived(Packets.CustomEvent p,Client sender) - { - var args = new CustomEventReceivedArgs() { Hash=p.Hash, Args=p.Args, Sender=sender }; - List> handlers; - if (CustomEventHandlers.TryGetValue(p.Hash,out handlers)) - { - handlers.ForEach((x) => { x.Invoke(args); }); - } - } - internal static bool InvokeOnCommandReceived(string cname,string[] cargs,Client sender) - { - var args = new OnCommandEventArgs() - { - Name=cname, - Args=cargs, - Sender=sender - }; - OnCommandReceived?.Invoke(null,args); - return args.Cancel; - } - internal static void InvokePlayerUpdate(Client client) - { - OnPlayerUpdate?.Invoke(null,client); - } - #endregion + }); } + internal void InvokePlayerConnected(Client client) + { OnPlayerConnected?.Invoke(this,client); } + internal void InvokePlayerReady(Client client) + { OnPlayerReady?.Invoke(this, client); } + internal void InvokePlayerDisconnected(Client client) + { OnPlayerDisconnected?.Invoke(this,client); } + internal void InvokePlayerHandshake(HandshakeEventArgs args) + { OnPlayerHandshake?.Invoke(this, args); } + internal void InvokeCustomEventReceived(Packets.CustomEvent p, Client sender) + { + var args = new CustomEventReceivedArgs() { Hash=p.Hash, Args=p.Args, Sender=sender }; + List> handlers; + if (CustomEventHandlers.TryGetValue(p.Hash, out handlers)) + { + handlers.ForEach((x) => { x.Invoke(args); }); + } + } + internal bool InvokeOnCommandReceived(string cname, string[] cargs, Client sender) + { + var args = new OnCommandEventArgs() + { + Name=cname, + Args=cargs, + Sender=sender + }; + OnCommandReceived?.Invoke(this, args); + return args.Cancel; + } + internal void InvokePlayerUpdate(Client client) + { + OnPlayerUpdate?.Invoke(this, client); + } + #endregion + } + public class API + { + private readonly Server Server; + internal API(Server server) + { + Server=server; + } + public APIEvents Events { get; set; }=new APIEvents(); #region FUNCTIONS /* /// @@ -101,7 +105,7 @@ namespace RageCoop.Server.Scripting /// /// The hash (Example: 0x25223CA6B4D20B7F = GET_CLOCK_HOURS) /// The arguments (Example: string = int, object = 5) - public static void SendNativeCallToAll(GTA.Native.Hash hash, params object[] args) + public void SendNativeCallToAll(GTA.Native.Hash hash, params object[] args) { try { @@ -112,7 +116,7 @@ namespace RageCoop.Server.Scripting if (args != null && args.Length == 0) { - Program.Logger.Error($"[ServerScript->SendNativeCallToAll(ulong hash, params object[] args)]: args is not null!"); + Server.Logger?.Error($"[ServerScript->SendNativeCallToAll(ulong hash, params object[] args)]: args is not null!"); return; } @@ -128,7 +132,7 @@ namespace RageCoop.Server.Scripting } catch (Exception e) { - Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); + Server.Logger?.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); } } */ @@ -136,7 +140,7 @@ namespace RageCoop.Server.Scripting /// Get a list of all Clients /// /// All clients as a dictionary indexed by NetID - public static Dictionary GetAllClients() + public Dictionary GetAllClients() { return new(Server.Clients); } @@ -146,7 +150,7 @@ namespace RageCoop.Server.Scripting /// /// The username to search for (non case-sensitive) /// The Client from this user or null - public static Client GetClientByUsername(string username) + public Client GetClientByUsername(string username) { return Server.Clients.Values.FirstOrDefault(x => x.Username.ToLower() == username.ToLower()); } @@ -156,7 +160,7 @@ namespace RageCoop.Server.Scripting /// /// The chat message /// The username which send this message (default = "Server") - public static void SendChatMessage(string message, List targets = null, string username = "Server") + public void SendChatMessage(string message, List targets = null, string username = "Server") { try { @@ -172,10 +176,10 @@ namespace RageCoop.Server.Scripting } catch (Exception e) { - Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); + Server.Logger?.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); } } - public static void SendChatMessage(string message, Client target, string username = "Server") + public void SendChatMessage(string message, Client target, string username = "Server") { try { @@ -183,27 +187,29 @@ namespace RageCoop.Server.Scripting } catch (Exception e) { - Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); + Server.Logger?.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<"); } } /// /// Send CleanUpWorld to all players to delete all objects created by the server /// - public static void SendCleanUpWorldToAll(List netHandleList = null) + public void SendCleanUpWorldToAll(List clients = null) { if (Server.MainNetServer.ConnectionsCount == 0) { return; } - - List connections = netHandleList == null - ? Server.MainNetServer.Connections - : Server.MainNetServer.Connections.FindAll(c => netHandleList.Contains(c.RemoteUniqueIdentifier)); - NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage(); outgoingMessage.Write((byte)PacketTypes.CleanUpWorld); - Server.MainNetServer.SendMessage(outgoingMessage, connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Default); + if (clients == null) + { + Server.MainNetServer.SendToAll(outgoingMessage, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Default); + } + else + { + clients.ForEach(client => { Server.MainNetServer.SendMessage(outgoingMessage,client.Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Default); }); + } } /// @@ -213,7 +219,7 @@ namespace RageCoop.Server.Scripting /// How to use this message (argsLength required!) /// The length of args (Example: "/message USERNAME MESSAGE" = 2) (usage required!) /// Create a new function! - public static void RegisterCommand(string name, string usage, short argsLength, Action callback) + public void RegisterCommand(string name, string usage, short argsLength, Action callback) { Server.RegisterCommand(name, usage, argsLength, callback); } @@ -222,7 +228,7 @@ namespace RageCoop.Server.Scripting /// /// The name of the command (Example: "test" for "/test") /// Create a new function! - public static void RegisterCommand(string name, Action callback) + public void RegisterCommand(string name, Action callback) { Server.RegisterCommand(name, callback); } @@ -231,18 +237,18 @@ namespace RageCoop.Server.Scripting /// Register a class of commands /// /// The name of your class with functions - public static void RegisterCommands() + public void RegisterCommands() { Server.RegisterCommands(); } /// - /// Send an event and data to the specified clients. + /// Send an event and data to the specified clients. Use if you want to send event to individual client. /// /// An unique identifier of the event /// The objects conataing your data, supported types: byte, short, ushort, int, uint, long, ulong, float, bool, string. - /// The target clients to send. - public static void SendCustomEvent(int eventHash,List args,List targets=null) + /// The target clients to send. Leave it null to send to all clients + public void SendCustomEvent(int eventHash,List args,List targets=null) { targets ??= new(Server.Clients.Values); var p = new Packets.CustomEvent() @@ -260,25 +266,25 @@ namespace RageCoop.Server.Scripting /// /// An unique identifier of the event, you can hash your event name with /// An handler to be invoked when the event is received from the server. This will be invoked from main thread. - public static void RegisterCustomEventHandler(int hash,Action handler) + public void RegisterCustomEventHandler(int hash,Action handler) { List> handlers; - lock (CustomEventHandlers) + lock (Events.CustomEventHandlers) { - if (!CustomEventHandlers.TryGetValue(hash,out handlers)) + if (!Events.CustomEventHandlers.TryGetValue(hash,out handlers)) { - CustomEventHandlers.Add(hash, handlers = new List>()); + Events.CustomEventHandlers.Add(hash, handlers = new List>()); } handlers.Add(handler); } } - public static void RegisterCustomEventHandler(string name, Action handler) + public void RegisterCustomEventHandler(string name, Action handler) { RegisterCustomEventHandler(CustomEvents.Hash(name), handler); } - public static Logger GetLogger() + public Logger GetLogger() { - return Program.Logger; + return Server.Logger; } #endregion } diff --git a/RageCoop.Server/Scripting/Resources.cs b/RageCoop.Server/Scripting/Resources.cs index 324c1d6..5e718ab 100644 --- a/RageCoop.Server/Scripting/Resources.cs +++ b/RageCoop.Server/Scripting/Resources.cs @@ -11,9 +11,13 @@ namespace RageCoop.Server.Scripting { internal class Resources : ResourceLoader { - public Resources() : base("RageCoop.Server.Scripting.ServerScript", Program.Logger) { } + private readonly Server Server; + public Resources(Server server) : base("RageCoop.Server.Scripting.ServerScript", server.Logger) + { + Server = server; + } - public static bool HasClientResources = false; + public bool HasClientResources = false; public void LoadAll() { #region CLIENT @@ -23,7 +27,7 @@ namespace RageCoop.Server.Scripting if (clientResources.Length!=0) { // Pack client side resources as a zip file - Logger.Info("Packing client-side resources"); + Logger?.Info("Packing client-side resources"); try { @@ -41,8 +45,8 @@ namespace RageCoop.Server.Scripting } catch (Exception ex) { - Logger.Error("Failed to pack client resources"); - Logger.Error(ex); + Logger?.Error("Failed to pack client resources"); + Logger?.Error(ex); } } #endregion @@ -52,7 +56,7 @@ namespace RageCoop.Server.Scripting Directory.CreateDirectory(path); foreach (var resource in Directory.GetDirectories(path)) { - Logger.Info($"Loading resource: {Path.GetFileName(resource)}"); + Logger?.Info($"Loading resource: {Path.GetFileName(resource)}"); LoadResource(resource); } @@ -61,14 +65,15 @@ namespace RageCoop.Server.Scripting { foreach (var d in LoadedResources) { - foreach (var s in d.Scripts) + foreach (ServerScript s in d.Scripts) { - (s as ServerScript).CurrentResource = d; + s.CurrentResource = d; + s.API=Server.API; try { s.OnStart(); } - catch(Exception ex) {Logger.Error($"Failed to start resource: {d.Name}"); Logger.Error(ex); } + catch(Exception ex) {Logger?.Error($"Failed to start resource: {d.Name}"); Logger?.Error(ex); } } } } @@ -104,9 +109,9 @@ namespace RageCoop.Server.Scripting { Task.Run(() => { - Logger.Info($"Sending resources to client:{client.Username}"); + Logger?.Info($"Sending resources to client:{client.Username}"); Server.SendFile(path, "Resources.zip", client); - Logger.Info($"Resources sent to:{client.Username}"); + Logger?.Info($"Resources sent to:{client.Username}"); }); } diff --git a/RageCoop.Server/Scripting/ServerScript.cs b/RageCoop.Server/Scripting/ServerScript.cs index ada4fdf..6ed7088 100644 --- a/RageCoop.Server/Scripting/ServerScript.cs +++ b/RageCoop.Server/Scripting/ServerScript.cs @@ -3,7 +3,7 @@ namespace RageCoop.Server.Scripting { /// - /// Inherit from this class, constructor will be called automatically, but other scripts might have yet been loaded, you should use . to initiate your script. + /// Inherit from this class, constructor will be called automatically, but other scripts might have yet been loaded and will be null, you should use . to initiate your script. /// public abstract class ServerScript :Core.Scripting.IScriptable { @@ -20,6 +20,8 @@ namespace RageCoop.Server.Scripting /// Get the object this script belongs to, this property will be initiated before (will be null if you access it in the constructor). /// public Core.Scripting.Resource CurrentResource { get;internal set; } + + public API API { get; set; } } [AttributeUsage(AttributeTargets.Method, Inherited = false)] diff --git a/RageCoop.Server/Server.cs b/RageCoop.Server/Server.cs index 5aba8f2..7f8dadf 100644 --- a/RageCoop.Server/Server.cs +++ b/RageCoop.Server/Server.cs @@ -26,24 +26,29 @@ namespace RageCoop.Server internal class Server { - private static readonly string _compatibleVersion = "V0_5"; - public static BaseScript BaseScript { get; set; }=new BaseScript(); - public static readonly Settings MainSettings = Util.Read("Settings.xml"); - public static NetServer MainNetServer; + private readonly string _compatibleVersion = "V0_5"; + public BaseScript BaseScript { get; set; }=new BaseScript(); + public readonly Settings MainSettings = Util.Read("Settings.xml"); + public NetServer MainNetServer; - public static readonly Dictionary> Commands = new(); - public static readonly Dictionary Clients = new(); - private static System.Timers.Timer SendPlayerTimer = new System.Timers.Timer(5000); + public readonly Dictionary> Commands = new(); + public readonly Dictionary Clients = new(); + private System.Timers.Timer SendPlayerTimer = new System.Timers.Timer(5000); - private static Dictionary InProgressFileTransfers=new(); - private static Resources Resources = new Resources(); - public Server() + private Dictionary InProgressFileTransfers=new(); + private Resources Resources; + public API API; + public Logger Logger; + public Server(Logger logger=null) { - Program.Logger.Info("================"); - Program.Logger.Info($"Server bound to: 0.0.0.0:{MainSettings.Port}"); - Program.Logger.Info($"Server version: {Assembly.GetCallingAssembly().GetName().Version}"); - Program.Logger.Info($"Compatible RAGECOOP versions: {_compatibleVersion.Replace('_', '.')}.x"); - Program.Logger.Info("================"); + Logger=logger; + API=new API(this); + Resources=new Resources(this); + Logger?.Info("================"); + Logger?.Info($"Server bound to: 0.0.0.0:{MainSettings.Port}"); + Logger?.Info($"Server version: {Assembly.GetCallingAssembly().GetName().Version}"); + Logger?.Info($"Compatible RAGECOOP versions: {_compatibleVersion.Replace('_', '.')}.x"); + Logger?.Info("================"); // 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP NetPeerConfiguration config = new("623c92c287cc392406e7aaaac1c0f3b0") @@ -62,7 +67,7 @@ namespace RageCoop.Server SendPlayerTimer.Elapsed+=(s,e) => { SendPlayerInfos(); }; SendPlayerTimer.AutoReset=true; SendPlayerTimer.Enabled=true; - Program.Logger.Info(string.Format("Server listening on {0}:{1}", config.LocalAddress.ToString(), config.Port)); + Logger?.Info(string.Format("Server listening on {0}:{1}", config.LocalAddress.ToString(), config.Port)); if (MainSettings.AnnounceSelf) { @@ -89,11 +94,11 @@ namespace RageCoop.Server string content = await response.Content.ReadAsStringAsync(); info = JsonConvert.DeserializeObject(content); - Program.Logger.Info($"Your public IP is {info.Address}, announcing to master server..."); + Logger?.Info($"Your public IP is {info.Address}, announcing to master server..."); } catch (Exception ex) { - Program.Logger.Error(ex.InnerException?.Message ?? ex.Message); + Logger?.Error(ex.InnerException?.Message ?? ex.Message); return; } var realMaster = MainSettings.MasterServer=="[AUTO]" ? Util.DownloadString("https://ragecoop.online/stuff/masterserver") : MainSettings.MasterServer; @@ -115,7 +120,7 @@ namespace RageCoop.Server } catch (Exception ex) { - Program.Logger.Error($"MasterServer: {ex.Message}"); + Logger?.Error($"MasterServer: {ex.Message}"); // Sleep for 5s Thread.Sleep(5000); @@ -124,19 +129,19 @@ namespace RageCoop.Server if (response == null) { - Program.Logger.Error("MasterServer: Something went wrong!"); + Logger?.Error("MasterServer: Something went wrong!"); } else if (response.StatusCode != HttpStatusCode.OK) { if (response.StatusCode == HttpStatusCode.BadRequest) { string requestContent = await response.Content.ReadAsStringAsync(); - Program.Logger.Error($"MasterServer: [{(int)response.StatusCode}], {requestContent}"); + Logger?.Error($"MasterServer: [{(int)response.StatusCode}], {requestContent}"); } else { - Program.Logger.Error($"MasterServer: [{(int)response.StatusCode}]"); - Program.Logger.Error($"MasterServer: [{await response.Content.ReadAsStringAsync()}]"); + Logger?.Error($"MasterServer: [{(int)response.StatusCode}]"); + Logger?.Error($"MasterServer: [{await response.Content.ReadAsStringAsync()}]"); } } @@ -146,15 +151,16 @@ namespace RageCoop.Server } catch (HttpRequestException ex) { - Program.Logger.Error($"MasterServer: {ex.InnerException.Message}"); + Logger?.Error($"MasterServer: {ex.InnerException.Message}"); } catch (Exception ex) { - Program.Logger.Error($"MasterServer: {ex.Message}"); + Logger?.Error($"MasterServer: {ex.Message}"); } }).Start(); #endregion } + BaseScript.API=API; BaseScript.OnStart(); Resources.LoadAll(); Listen(); @@ -162,20 +168,27 @@ namespace RageCoop.Server private void Listen() { - Program.Logger.Info("Listening for clients"); - Program.Logger.Info("Please use CTRL + C if you want to stop the server!"); + Logger?.Info("Listening for clients"); + Logger?.Info("Please use CTRL + C if you want to stop the server!"); while (!Program.ReadyToStop) { - ProcessMessage(MainNetServer.WaitMessage(10)); + try + { + ProcessMessage(MainNetServer.WaitMessage(200)); + } + catch(Exception ex) + { + Logger?.Error("Error processing message"); + Logger?.Error(ex); + } } - Program.Logger.Warning("Server is shutting down!"); + Logger?.Warning("Server is shutting down!"); MainNetServer.Shutdown("Server is shutting down!"); - Program.Logger.Info("Waiting for resources to stop...Press Ctrl+C again to forcibly terminate the program."); + Logger?.Info("Waiting for resources to stop...Press Ctrl+C again to forcibly terminate the program."); BaseScript.OnStop(); Resources.StopAll(); - Program.Logger.Dispose(); } Client sender; @@ -186,10 +199,10 @@ namespace RageCoop.Server { case NetIncomingMessageType.ConnectionApproval: { - Program.Logger.Info($"New incoming connection from: [{message.SenderConnection.RemoteEndPoint}]"); + Logger?.Info($"New incoming connection from: [{message.SenderConnection.RemoteEndPoint}]"); if (message.ReadByte() != (byte)PacketTypes.Handshake) { - Program.Logger.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: Wrong packet!"); + Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: Wrong packet!"); message.SenderConnection.Deny("Wrong packet!"); } else @@ -206,7 +219,7 @@ namespace RageCoop.Server } catch (Exception e) { - Program.Logger.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: {e.Message}"); + Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: {e.Message}"); message.SenderConnection.Deny(e.Message); } } @@ -214,19 +227,27 @@ namespace RageCoop.Server } case NetIncomingMessageType.StatusChanged: { + // Get sender client + if (!Clients.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender)) + { + break; + } NetConnectionStatus status = (NetConnectionStatus)message.ReadByte(); if (status == NetConnectionStatus.Disconnected) { - long nethandle = message.SenderConnection.RemoteUniqueIdentifier; - - SendPlayerDisconnectPacket(nethandle); + SendPlayerDisconnectPacket(sender); } else if (status == NetConnectionStatus.Connected) { - SendPlayerConnectPacket(message.SenderConnection); - Resources.SendTo(Util.GetClientByNetID(message.SenderConnection.RemoteUniqueIdentifier)); + SendPlayerConnectPacket(sender); + Resources.SendTo(sender); + API.Events.InvokePlayerConnected(sender); + if (sender.IsReady) + { + API.Events.InvokePlayerReady(sender); + } } break; } @@ -291,7 +312,7 @@ namespace RageCoop.Server Packets.ProjectileSync packet = new(); packet.Unpack(data); - ProjectileSync(packet, message.SenderConnection.RemoteUniqueIdentifier); + ProjectileSync(packet, sender); } break; @@ -329,7 +350,8 @@ namespace RageCoop.Server toRemove.Cancel=true; if (toRemove.Name=="Resources.zip") { - Clients[message.SenderConnection.RemoteUniqueIdentifier].IsReady=true; + sender.IsReady=true; + API.Events.InvokePlayerReady(sender); } } } @@ -340,13 +362,13 @@ namespace RageCoop.Server // Sync Events try { - var outgoingMessage = MainNetServer.CreateMessage(); - outgoingMessage.Write(btype); - outgoingMessage.Write(len); - outgoingMessage.Write(data); var toSend = MainNetServer.Connections.Exclude(message.SenderConnection); if (toSend.Count!=0) { + var outgoingMessage = MainNetServer.CreateMessage(); + outgoingMessage.Write(btype); + outgoingMessage.Write(len); + outgoingMessage.Write(data); MainNetServer.SendMessage(outgoingMessage, toSend, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.SyncEvents); } } @@ -357,7 +379,7 @@ namespace RageCoop.Server } else { - Program.Logger.Error("Unhandled Data / Packet type"); + Logger?.Error("Unhandled Data / Packet type"); } break; } @@ -370,32 +392,36 @@ namespace RageCoop.Server } case NetIncomingMessageType.ConnectionLatencyUpdated: { - Client client = Util.GetClientByNetID(message.SenderConnection.RemoteUniqueIdentifier); - if (client != null) + // Get sender client + if (!Clients.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender)) { - client.Latency = message.ReadFloat(); + break; + } + if (sender != null) + { + sender.Latency = message.ReadFloat(); } } break; case NetIncomingMessageType.ErrorMessage: - Program.Logger.Error(message.ReadString()); + Logger?.Error(message.ReadString()); break; case NetIncomingMessageType.WarningMessage: - Program.Logger.Warning(message.ReadString()); + Logger?.Warning(message.ReadString()); break; case NetIncomingMessageType.DebugMessage: case NetIncomingMessageType.VerboseDebugMessage: - Program.Logger.Debug(message.ReadString()); + Logger?.Debug(message.ReadString()); break; default: - Program.Logger.Error(string.Format("Unhandled type: {0} {1} bytes {2} | {3}", message.MessageType, message.LengthBytes, message.DeliveryMethod, message.SequenceChannel)); + Logger?.Error(string.Format("Unhandled type: {0} {1} bytes {2} | {3}", message.MessageType, message.LengthBytes, message.DeliveryMethod, message.SequenceChannel)); break; } MainNetServer.Recycle(message); } - static object _sendPlayersLock=new object(); - public static void SendPlayerInfos() + object _sendPlayersLock=new object(); + public void SendPlayerInfos() { lock (_sendPlayersLock) { @@ -422,9 +448,9 @@ namespace RageCoop.Server private void DisconnectAndLog(NetConnection senderConnection,PacketTypes type, Exception e) { - Program.Logger.Error($"Error receiving a packet of type {type}"); - Program.Logger.Error(e.Message); - Program.Logger.Error(e.StackTrace); + Logger?.Error($"Error receiving a packet of type {type}"); + Logger?.Error(e.Message); + Logger?.Error(e.StackTrace); senderConnection.Disconnect(e.Message); } @@ -432,7 +458,7 @@ namespace RageCoop.Server // Before we approve the connection, we must shake hands private void GetHandshake(NetConnection connection, Packets.Handshake packet) { - Program.Logger.Debug("New handshake from: [Name: " + packet.Username + " | Address: " + connection.RemoteEndPoint.Address.ToString() + "]"); + Logger?.Debug("New handshake from: [Name: " + packet.Username + " | Address: " + connection.RemoteEndPoint.Address.ToString() + "]"); if (!packet.ModVersion.StartsWith(_compatibleVersion)) { @@ -476,7 +502,7 @@ namespace RageCoop.Server lock (Clients) { Clients.Add(connection.RemoteUniqueIdentifier, - tmpClient = new Client() + tmpClient = new Client(this) { NetID = connection.RemoteUniqueIdentifier, Connection=connection, @@ -489,7 +515,7 @@ namespace RageCoop.Server );; } - Program.Logger.Debug($"Handshake sucess, Player:{packet.Username} PedID:{packet.PedID}"); + Logger?.Debug($"Handshake sucess, Player:{packet.Username} PedID:{packet.PedID}"); NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); // Create a new handshake packet @@ -504,93 +530,68 @@ namespace RageCoop.Server } // The connection has been approved, now we need to send all other players to the new player and the new player to all players - private static void SendPlayerConnectPacket(NetConnection local) + private void SendPlayerConnectPacket(Client newClient) { - Client localClient = Util.GetClientByNetID(local.RemoteUniqueIdentifier); - if (localClient == null) + + // Send other players to this client + Clients.Values.ForEach(target => { - local.Disconnect("No data found!"); - return; - } - - List clients=MainNetServer.Connections.Exclude(local); - // Send all players to local - - - if (clients.Count > 0) - { - clients.ForEach(targetPlayer => - { - long targetNetHandle = targetPlayer.RemoteUniqueIdentifier; - NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); - - Client targetClient = Util.GetClientByNetID(targetNetHandle); - if (targetClient != null) - { - new Packets.PlayerConnect() - { - // NetHandle = targetNetHandle, - Username = targetClient.Username, - PedID=targetClient.ID, - - }.Pack(outgoingMessage); - MainNetServer.SendMessage(outgoingMessage, local, NetDeliveryMethod.ReliableOrdered, 0); - } - }); - - // Send local to all players + if (target==newClient) { return; } NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); new Packets.PlayerConnect() { - PedID=localClient.ID, - Username = localClient.Username + // NetHandle = targetNetHandle, + Username = target.Username, + PedID=target.ID, + }.Pack(outgoingMessage); - if(clients.Count > 0) + MainNetServer.SendMessage(outgoingMessage, newClient.Connection, NetDeliveryMethod.ReliableOrdered, 0); + }); + // Send new client to all players + var cons = MainNetServer.Connections.Exclude(newClient.Connection); + if (cons.Count!=0) + { + NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); + new Packets.PlayerConnect() { - MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0); - } + PedID=newClient.ID, + Username = newClient.Username + }.Pack(outgoingMessage); + + MainNetServer.SendMessage(outgoingMessage,cons , NetDeliveryMethod.ReliableOrdered, 0); + } - API.Events.InvokePlayerConnected(localClient); - Program.Logger.Info($"Player {localClient.Username} connected!"); + Logger?.Info($"Player {newClient.Username} connected!"); if (!string.IsNullOrEmpty(MainSettings.WelcomeMessage)) { - SendChatMessage(new Packets.ChatMessage() { Username = "Server", Message = MainSettings.WelcomeMessage }, null,new List() { local }); + SendChatMessage(new Packets.ChatMessage() { Username = "Server", Message = MainSettings.WelcomeMessage }, null,new List() { newClient.Connection }); } } // Send all players a message that someone has left the server - private static void SendPlayerDisconnectPacket(long nethandle) + private void SendPlayerDisconnectPacket(Client localClient) { - List clients = MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != nethandle); - int playerPedID = Clients[nethandle].ID; - if (clients.Count > 0) + var cons = MainNetServer.Connections.Exclude(localClient.Connection); + if (cons.Count!=0) { NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); new Packets.PlayerDisconnect() { - PedID=playerPedID, - + PedID=localClient.ID, + }.Pack(outgoingMessage); - MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0); + MainNetServer.SendMessage(outgoingMessage,cons , NetDeliveryMethod.ReliableOrdered, 0); } - - Client localClient = Util.GetClientByNetID( nethandle); - if (localClient == null) - { - return; - } - - Clients.Remove(localClient.NetID); - API.Events.InvokePlayerDisconnected(localClient); - Program.Logger.Info($"Player {localClient.Username} disconnected! ID:{playerPedID}"); - + Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.ID}"); + Clients.Remove(localClient.NetID); + } #region SyncEntities - private static void PedStateSync(Packets.PedStateSync packet, Client client) + private void PedStateSync(Packets.PedStateSync packet, Client client) { @@ -604,7 +605,7 @@ namespace RageCoop.Server MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync); } } - private static void VehicleStateSync(Packets.VehicleStateSync packet, Client client) + private void VehicleStateSync(Packets.VehicleStateSync packet, Client client) { // Save the new data if (packet.Passengers.ContainsValue(client.ID)) @@ -620,7 +621,7 @@ namespace RageCoop.Server MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync); } } - private static void PedSync(Packets.PedSync packet, Client client) + private void PedSync(Packets.PedSync packet, Client client) { bool isPlayer = packet.ID==client.ID; if (isPlayer) @@ -654,7 +655,7 @@ namespace RageCoop.Server MainNetServer.SendMessage(outgoingMessage,c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync); } } - private static void VehicleSync(Packets.VehicleSync packet, Client client) + private void VehicleSync(Packets.VehicleSync packet, Client client) { bool isPlayer = packet.ID==client.Player.VehicleID; foreach (var c in Clients.Values) @@ -678,13 +679,8 @@ namespace RageCoop.Server MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync); } } - private static void ProjectileSync(Packets.ProjectileSync packet, long ClientID) + private void ProjectileSync(Packets.ProjectileSync packet, Client client) { - Client client = Util.GetClientByNetID(ClientID); - if (client == null) - { - return; - } foreach (var c in Clients.Values) { @@ -697,7 +693,7 @@ namespace RageCoop.Server #endregion // Send a message to targets or all players - private static void SendChatMessage(Packets.ChatMessage packet,Client sender=null, List targets = null) + private void SendChatMessage(Packets.ChatMessage packet,Client sender=null, List targets = null) { if (packet.Message.StartsWith('/')) { @@ -722,13 +718,8 @@ namespace RageCoop.Server if (command.Key.Usage != null && command.Key.ArgsLength != argsWithoutCmd.Length) { - NetConnection userConnection = Util.GetConnectionByUsername(packet.Username); - if (userConnection == default) - { - return; - } - SendChatMessage("Server", command.Key.Usage, userConnection); + SendChatMessage("Server", command.Key.Usage,sender.Connection); return; } @@ -736,13 +727,8 @@ namespace RageCoop.Server } else { - NetConnection userConnection = Util.GetConnectionByUsername(packet.Username); - if (userConnection == default) - { - return; - } - SendChatMessage("Server", "Command not found!", userConnection); + SendChatMessage("Server", "Command not found!", sender.Connection); } return; @@ -750,25 +736,26 @@ namespace RageCoop.Server packet.Message = packet.Message.Replace("~", ""); SendChatMessage(packet.Username, packet.Message, targets); - Program.Logger.Info(packet.Username + ": " + packet.Message); + Logger?.Info(packet.Username + ": " + packet.Message); } - public static void SendChatMessage(string username, string message, List targets = null) + public void SendChatMessage(string username, string message, List targets = null) { + if (MainNetServer.Connections.Count==0) { return; } NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); new Packets.ChatMessage() { Username = username, Message = message }.Pack(outgoingMessage); - + MainNetServer.SendMessage(outgoingMessage, targets ?? MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Chat); } - public static void SendChatMessage(string username, string message, NetConnection target) + public void SendChatMessage(string username, string message, NetConnection target) { SendChatMessage(username, message, new List() { target }); } #endregion - public static void RegisterCommand(string name, string usage, short argsLength, Action callback) + public void RegisterCommand(string name, string usage, short argsLength, Action callback) { Command command = new(name) { Usage = usage, ArgsLength = argsLength }; @@ -779,7 +766,7 @@ namespace RageCoop.Server Commands.Add(command, callback); } - public static void RegisterCommand(string name, Action callback) + public void RegisterCommand(string name, Action callback) { Command command = new(name); @@ -791,7 +778,7 @@ namespace RageCoop.Server Commands.Add(command, callback); } - public static void RegisterCommands() + public void RegisterCommands() { IEnumerable commands = typeof(T).GetMethods().Where(method => method.GetCustomAttributes(typeof(Command), false).Any()); @@ -803,13 +790,13 @@ namespace RageCoop.Server } } - public static void SendFile(string path,string name,Client client,Action updateCallback=null) + public void SendFile(string path,string name,Client client,Action updateCallback=null) { int id = RequestFileID(); var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); fs.Seek(0, SeekOrigin.Begin); var total = fs.Length; - Program.Logger.Debug($"Initiating file transfer:{name}, {total}"); + Logger?.Debug($"Initiating file transfer:{name}, {total}"); FileTransfer transfer = new() { ID=id, @@ -835,7 +822,7 @@ namespace RageCoop.Server if (thisRead!=chunk.Length) { if (thisRead==0) { break; } - Program.Logger.Trace($"Purging chunk:{thisRead}"); + Logger?.Trace($"Purging chunk:{thisRead}"); Array.Resize(ref chunk, thisRead); } Send( @@ -858,10 +845,10 @@ namespace RageCoop.Server ); fs.Close(); fs.Dispose(); - Program.Logger.Debug($"All file chunks sent:{name}"); + Logger?.Debug($"All file chunks sent:{name}"); InProgressFileTransfers.Remove(id); } - private static int RequestFileID() + private int RequestFileID() { int ID = 0; while ((ID==0) @@ -883,7 +870,7 @@ namespace RageCoop.Server /// /// /// - public static void Send(Packet p,Client client, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced) + public void Send(Packet p,Client client, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced) { NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage(); p.Pack(outgoingMessage); diff --git a/RageCoop.Server/Util.cs b/RageCoop.Server/Util.cs index c2bd1fd..ff631ee 100644 --- a/RageCoop.Server/Util.cs +++ b/RageCoop.Server/Util.cs @@ -47,7 +47,7 @@ namespace RageCoop.Server _ => (0x0, null), }; } - + /* public static Client GetClientByNetID(long id) { Client result = null; @@ -75,17 +75,18 @@ namespace RageCoop.Server return Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == client.NetID); } - + */ // Return a list of all connections but not the local connection public static List Exclude(this IEnumerable connections,NetConnection toExclude) { return new(connections.Where(e => e != toExclude)); } + /* public static List FilterAllLocal(long local) { return new(Server.MainNetServer.Connections.Where(e => e.RemoteUniqueIdentifier != local)); } - + */ public static T Read(string file) where T : new() { XmlSerializer ser = new(typeof(T));