Scripting prototype?
This commit is contained in:
@ -11,7 +11,7 @@ namespace RageCoop.Client.Scripting
|
|||||||
{
|
{
|
||||||
internal class Engine : Core.Scripting.ScriptingEngine
|
internal class Engine : Core.Scripting.ScriptingEngine
|
||||||
{
|
{
|
||||||
public Engine() : base(typeof(ClientScript), Main.Logger) { }
|
public Engine() : base("RageCoop.Client.Scripting.ClientScript", Main.Logger) { }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,16 @@ namespace RageCoop.Core.Scripting
|
|||||||
{
|
{
|
||||||
internal class ScriptingEngine
|
internal class ScriptingEngine
|
||||||
{
|
{
|
||||||
private Type BaseScriptType;
|
protected List<string> ToIgnore = new List<string>
|
||||||
|
{
|
||||||
|
"RageCoop.Client.dll",
|
||||||
|
"RageCoop.Core.dll",
|
||||||
|
"RageCoop.Server.dll",
|
||||||
|
"ScriptHookVDotNet3.dll"
|
||||||
|
};
|
||||||
|
private string BaseScriptType;
|
||||||
public Logging.Logger Logger { get; set; }
|
public Logging.Logger Logger { get; set; }
|
||||||
public ScriptingEngine(Type baseScriptType, Logging.Logger logger)
|
public ScriptingEngine(string baseScriptType, Logging.Logger logger)
|
||||||
{
|
{
|
||||||
BaseScriptType = baseScriptType;
|
BaseScriptType = baseScriptType;
|
||||||
Logger = logger;
|
Logger = logger;
|
||||||
@ -22,29 +29,29 @@ namespace RageCoop.Core.Scripting
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads scripts from the specified assembly file.
|
/// Loads scripts from the specified assembly file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filename">The path to the assembly file to load.</param>
|
/// <param name="path">The path to the assembly file to load.</param>
|
||||||
/// <returns><see langword="true" /> on success, <see langword="false" /> otherwise</returns>
|
/// <returns><see langword="true" /> on success, <see langword="false" /> otherwise</returns>
|
||||||
private bool LoadScriptsFromAssembly(string filename)
|
protected bool LoadScriptsFromAssembly(string path)
|
||||||
{
|
{
|
||||||
if (!IsManagedAssembly(filename))
|
if (!IsManagedAssembly(path)) { return false; }
|
||||||
return false;
|
if (ToIgnore.Contains(Path.GetFileName(path))) { return false; }
|
||||||
|
|
||||||
Logger?.Debug($"Loading assembly {Path.GetFileName(filename)} ...");
|
Logger?.Debug($"Loading assembly {Path.GetFileName(path)} ...");
|
||||||
|
|
||||||
Assembly assembly;
|
Assembly assembly;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
assembly = Assembly.LoadFrom(filename);
|
assembly = Assembly.LoadFrom(path);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger?.Error( "Unable to load "+Path.GetFileName(filename));
|
Logger?.Error( "Unable to load "+Path.GetFileName(path));
|
||||||
Logger?.Error(ex);
|
Logger?.Error(ex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return LoadScriptsFromAssembly(assembly, filename);
|
return LoadScriptsFromAssembly(assembly, path);
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads scripts from the specified assembly object.
|
/// Loads scripts from the specified assembly object.
|
||||||
@ -59,7 +66,7 @@ namespace RageCoop.Core.Scripting
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Find all script types in the assembly
|
// Find all script types in the assembly
|
||||||
foreach (var type in assembly.GetTypes().Where(x => IsSubclassOf(x,nameof(BaseScriptType))))
|
foreach (var type in assembly.GetTypes().Where(x => IsSubclassOf(x,BaseScriptType)))
|
||||||
{
|
{
|
||||||
ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes);
|
ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes);
|
||||||
if (constructor != null && constructor.IsPublic)
|
if (constructor != null && constructor.IsPublic)
|
||||||
@ -90,7 +97,7 @@ namespace RageCoop.Core.Scripting
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger?.Info($"Loaded {count.ToString()} script(s) in {Path.GetFileName(filename)}");
|
Logger?.Info($"Loaded {count} script(s) in {Path.GetFileName(filename)}");
|
||||||
return count != 0;
|
return count != 0;
|
||||||
}
|
}
|
||||||
private bool IsManagedAssembly(string filename)
|
private bool IsManagedAssembly(string filename)
|
||||||
|
15
RAGECOOP.sln
15
RAGECOOP.sln
@ -9,6 +9,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Client", "Client\R
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Core", "Core\RageCoop.Core.csproj", "{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Core", "Core\RageCoop.Core.csproj", "{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RageCoop.Resources.Base", "Resources\Base\RageCoop.Resources.Base.csproj", "{9DC11623-8A8B-4D17-B18B-91852922163D}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resources", "Resources", "{F49A2617-832B-44DA-9D42-29B5DD09A186}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -41,10 +45,21 @@ Global
|
|||||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|Any CPU.Build.0 = Release|Any CPU
|
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|x64.ActiveCfg = Release|Any CPU
|
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|x64.Build.0 = Release|Any CPU
|
{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{9DC11623-8A8B-4D17-B18B-91852922163D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9DC11623-8A8B-4D17-B18B-91852922163D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9DC11623-8A8B-4D17-B18B-91852922163D}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{9DC11623-8A8B-4D17-B18B-91852922163D}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{9DC11623-8A8B-4D17-B18B-91852922163D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9DC11623-8A8B-4D17-B18B-91852922163D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9DC11623-8A8B-4D17-B18B-91852922163D}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{9DC11623-8A8B-4D17-B18B-91852922163D}.Release|x64.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{9DC11623-8A8B-4D17-B18B-91852922163D} = {F49A2617-832B-44DA-9D42-29B5DD09A186}
|
||||||
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {6CC7EA75-E4FF-4534-8EB6-0AEECF2620B7}
|
SolutionGuid = {6CC7EA75-E4FF-4534-8EB6-0AEECF2620B7}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
|
15
Resources/Base/RageCoop.Resources.Base.csproj
Normal file
15
Resources/Base/RageCoop.Resources.Base.csproj
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="RageCoop.Server">
|
||||||
|
<HintPath>..\..\Server\bin\Debug\net6.0\RageCoop.Server.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
17
Resources/Base/ServerBase.cs
Normal file
17
Resources/Base/ServerBase.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using RageCoop.Server.Scripting;
|
||||||
|
namespace RageCoop.Resources.Base
|
||||||
|
{
|
||||||
|
public class ServerBase :ServerScript
|
||||||
|
{
|
||||||
|
public ServerBase()
|
||||||
|
{
|
||||||
|
API.RegisterCommand("kick", (ctx) =>
|
||||||
|
{
|
||||||
|
if (ctx.Args.Length<1) { return; }
|
||||||
|
var reason = "eat poop";
|
||||||
|
if(ctx.Args.Length>=2) { reason=ctx.Args[1]; }
|
||||||
|
API.GetClientByUsername(ctx.Args[0]).Kick(reason);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,21 +9,7 @@ namespace RageCoop.Server
|
|||||||
public class Client
|
public class Client
|
||||||
{
|
{
|
||||||
public long NetID = 0;
|
public long NetID = 0;
|
||||||
private float _currentLatency = 0f;
|
internal NetConnection Connection { get; set; }
|
||||||
public NetConnection Connection { get; set; }
|
|
||||||
public float Latency
|
|
||||||
{
|
|
||||||
get => _currentLatency;
|
|
||||||
internal set
|
|
||||||
{
|
|
||||||
_currentLatency = value;
|
|
||||||
|
|
||||||
if ((value * 1000f) > Server.MainSettings.MaxLatency)
|
|
||||||
{
|
|
||||||
Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID)?.Disconnect($"Too high latency [{value * 1000f}/{(float)Server.MainSettings.MaxLatency}]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public PlayerData Player;
|
public PlayerData Player;
|
||||||
private readonly Dictionary<string, object> _customData = new();
|
private readonly Dictionary<string, object> _customData = new();
|
||||||
private long _callbacksCount = 0;
|
private long _callbacksCount = 0;
|
||||||
@ -64,13 +50,13 @@ namespace RageCoop.Server
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region FUNCTIONS
|
#region FUNCTIONS
|
||||||
public void Kick(string reason)
|
public void Kick(string reason="You has been kicked!")
|
||||||
{
|
{
|
||||||
Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID)?.Disconnect(reason);
|
Connection?.Disconnect(reason);
|
||||||
}
|
}
|
||||||
public void Kick(string[] reason)
|
public void Kick(params string[] reasons)
|
||||||
{
|
{
|
||||||
Kick(string.Join(" ", reason));
|
Kick(string.Join(" ", reasons));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendChatMessage(string message, string from = "Server")
|
public void SendChatMessage(string message, string from = "Server")
|
||||||
@ -201,32 +187,6 @@ namespace RageCoop.Server
|
|||||||
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Default);
|
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendModPacket(string modName, byte customID, byte[] bytes)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID);
|
|
||||||
if (userConnection == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
|
||||||
new Packets.Mod()
|
|
||||||
{
|
|
||||||
Name = modName,
|
|
||||||
CustomPacketID = customID,
|
|
||||||
Bytes = bytes
|
|
||||||
}.Pack(outgoingMessage);
|
|
||||||
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Mod);
|
|
||||||
Server.MainNetServer.FlushSendQueue();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SendTriggerEvent(string eventName, params object[] args)
|
public void SendTriggerEvent(string eventName, params object[] args)
|
||||||
{
|
{
|
||||||
if (!FilesReceived)
|
if (!FilesReceived)
|
||||||
|
@ -105,7 +105,7 @@ namespace RageCoop.Server
|
|||||||
{
|
{
|
||||||
lock (Server.Clients)
|
lock (Server.Clients)
|
||||||
{
|
{
|
||||||
Client x = Util.GetClientByID(client.NetHandle);
|
Client x = Util.GetClientByNetID(client.NetHandle);
|
||||||
if (x != null)
|
if (x != null)
|
||||||
{
|
{
|
||||||
x.FilesReceived = true;
|
x.FilesReceived = true;
|
||||||
|
192
Server/Scripting/API.cs
Normal file
192
Server/Scripting/API.cs
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Lidgren.Network;
|
||||||
|
using RageCoop.Core;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace RageCoop.Server.Scripting
|
||||||
|
{
|
||||||
|
public static class API
|
||||||
|
{
|
||||||
|
public static class Events
|
||||||
|
{
|
||||||
|
#region DELEGATES
|
||||||
|
public delegate void EmptyEvent();
|
||||||
|
public delegate void PlayerConnect(int id,string name);
|
||||||
|
public delegate void PlayerDisconnect(int id, string name);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
public static event EmptyEvent OnStop;
|
||||||
|
public static event EventHandler<ChatEventArgs> OnChatMessage;
|
||||||
|
public static event EventHandler<HandshakeEventArgs> OnPlayerHandshake;
|
||||||
|
public static event PlayerConnect OnPlayerConnected;
|
||||||
|
public static event PlayerDisconnect OnPlayerDisconnected;
|
||||||
|
// public static event EventHandler OnPlayerUpdate;
|
||||||
|
|
||||||
|
#region INVOKE
|
||||||
|
internal static void InvokeOnStop() { OnStop?.Invoke(); }
|
||||||
|
internal static void InvokeOnChatMessage(Packets.ChatMessage p,NetConnection con)
|
||||||
|
{
|
||||||
|
OnChatMessage?.Invoke(null,new ChatEventArgs() {
|
||||||
|
Sender=Util.GetClientByNetID(con.RemoteUniqueIdentifier),
|
||||||
|
Message=p.Message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
internal static void InvokePlayerConnected(Client client)
|
||||||
|
{ OnPlayerConnected?.Invoke(client.Player.PedID,client.Player.Username); }
|
||||||
|
internal static void InvokePlayerDisconnected(Client client)
|
||||||
|
{ OnPlayerDisconnected?.Invoke(client.Player.PedID, client.Player.Username); }
|
||||||
|
internal static void InvokePlayerHandshake(HandshakeEventArgs args)
|
||||||
|
{
|
||||||
|
OnPlayerHandshake?.Invoke(null, args);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
#region FUNCTIONS
|
||||||
|
/// <summary>
|
||||||
|
/// Send a native call (Function.Call) to all players.
|
||||||
|
/// Keys = int, float, bool, string and lvector3
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hash">The hash (Example: 0x25223CA6B4D20B7F = GET_CLOCK_HOURS)</param>
|
||||||
|
/// <param name="args">The arguments (Example: string = int, object = 5)</param>
|
||||||
|
public static void SendNativeCallToAll(GTA.Native.Hash hash, params object[] args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Server.MainNetServer.ConnectionsCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args != null && args.Length == 0)
|
||||||
|
{
|
||||||
|
Program.Logger.Error($"[ServerScript->SendNativeCallToAll(ulong hash, params object[] args)]: args is not null!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Packets.NativeCall packet = new()
|
||||||
|
{
|
||||||
|
Hash = (ulong)hash,
|
||||||
|
Args = new List<object>(args) ?? new List<object>()
|
||||||
|
};
|
||||||
|
|
||||||
|
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
||||||
|
packet.Pack(outgoingMessage);
|
||||||
|
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Native);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a list of all Clients
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>All clients as a dictionary indexed by NetID</returns>
|
||||||
|
public static Dictionary<long, Client> GetAllClients()
|
||||||
|
{
|
||||||
|
return Server.Clients;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the client by its username
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="username">The username to search for (non case-sensitive)</param>
|
||||||
|
/// <returns>The Client from this user or null</returns>
|
||||||
|
public static Client GetClientByUsername(string username)
|
||||||
|
{
|
||||||
|
return Server.Clients.Values.FirstOrDefault(x => x.Player.Username.ToLower() == username.ToLower());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Send a chat message to all players
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">The chat message</param>
|
||||||
|
/// <param name="username">The username which send this message (default = "Server")</param>
|
||||||
|
/// <param name="netHandleList">The list of connections (players) who received this chat message</param>
|
||||||
|
public static void SendChatMessageToAll(string message, string username = "Server", List<long> netHandleList = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Server.MainNetServer.ConnectionsCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<NetConnection> connections = netHandleList == null
|
||||||
|
? Server.MainNetServer.Connections
|
||||||
|
: Server.MainNetServer.Connections.FindAll(c => netHandleList.Contains(c.RemoteUniqueIdentifier));
|
||||||
|
|
||||||
|
Server.SendChatMessage(username, message, connections);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Send CleanUpWorld to all players to delete all objects created by the server
|
||||||
|
/// </summary>
|
||||||
|
public static void SendCleanUpWorldToAll(List<long> netHandleList = null)
|
||||||
|
{
|
||||||
|
if (Server.MainNetServer.ConnectionsCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<NetConnection> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a new command chat command (Example: "/test")
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the command (Example: "test" for "/test")</param>
|
||||||
|
/// <param name="usage">How to use this message (argsLength required!)</param>
|
||||||
|
/// <param name="argsLength">The length of args (Example: "/message USERNAME MESSAGE" = 2) (usage required!)</param>
|
||||||
|
/// <param name="callback">Create a new function!</param>
|
||||||
|
public static void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback)
|
||||||
|
{
|
||||||
|
Server.RegisterCommand(name, usage, argsLength, callback);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Register a new command chat command (Example: "/test")
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the command (Example: "test" for "/test")</param>
|
||||||
|
/// <param name="callback">Create a new function!</param>
|
||||||
|
public static void RegisterCommand(string name, Action<CommandContext> callback)
|
||||||
|
{
|
||||||
|
Server.RegisterCommand(name, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a class of commands
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The name of your class with functions</typeparam>
|
||||||
|
public static void RegisterCommands<T>()
|
||||||
|
{
|
||||||
|
Server.RegisterCommands<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a class of events
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The name of your class with functions</typeparam>
|
||||||
|
public static void RegisterEvents<T>()
|
||||||
|
{
|
||||||
|
Server.RegisterEvents<T>();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -4,17 +4,30 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using RageCoop.Core.Scripting;
|
using RageCoop.Core.Scripting;
|
||||||
|
using System.IO;
|
||||||
namespace RageCoop.Server.Scripting
|
namespace RageCoop.Server.Scripting
|
||||||
{
|
{
|
||||||
internal class Engine:ScriptingEngine
|
internal class Engine:ScriptingEngine
|
||||||
{
|
{
|
||||||
public Engine() : base(typeof(ServerScript), Program.Logger)
|
public Engine() : base("RageCoop.Server.Scripting.ServerScript", Program.Logger)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
public static void Load()
|
public void LoadAll()
|
||||||
{
|
{
|
||||||
|
var path = Path.Combine("Resources", "Server");
|
||||||
|
Directory.CreateDirectory(path);
|
||||||
|
foreach (var resource in Directory.GetDirectories(path))
|
||||||
|
{
|
||||||
|
Logger.Info($"Loading resource: {Path.GetFileName(resource)}");
|
||||||
|
LoadResource(resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void LoadResource(string path)
|
||||||
|
{
|
||||||
|
foreach(var assembly in Directory.GetFiles(path,"*.dll"))
|
||||||
|
{
|
||||||
|
LoadScriptsFromAssembly(assembly);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
28
Server/Scripting/EventArgs/EventArgs.cs
Normal file
28
Server/Scripting/EventArgs/EventArgs.cs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace RageCoop.Server.Scripting
|
||||||
|
{
|
||||||
|
public class ChatEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public Client Sender { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
}
|
||||||
|
public class HandshakeEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public int ID { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public IPEndPoint EndPoint { get; set; }
|
||||||
|
public void Deny(string reason)
|
||||||
|
{
|
||||||
|
DenyReason=reason;
|
||||||
|
Cancel=true;
|
||||||
|
}
|
||||||
|
internal string DenyReason { get; set; }
|
||||||
|
internal bool Cancel { get; set; }=false;
|
||||||
|
}
|
||||||
|
}
|
@ -8,401 +8,10 @@ using System.Threading.Tasks;
|
|||||||
using RageCoop.Core;
|
using RageCoop.Core;
|
||||||
using Lidgren.Network;
|
using Lidgren.Network;
|
||||||
|
|
||||||
namespace RageCoop.Server
|
namespace RageCoop.Server.Scripting
|
||||||
{
|
{
|
||||||
public abstract class ServerScript
|
public abstract class ServerScript
|
||||||
{
|
{
|
||||||
public API API { get; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Resource
|
|
||||||
{
|
|
||||||
public bool ReadyToStop = false;
|
|
||||||
|
|
||||||
private static Thread _mainThread;
|
|
||||||
private static Queue _actionQueue;
|
|
||||||
private static ServerScript _script;
|
|
||||||
|
|
||||||
public Resource(ServerScript script)
|
|
||||||
{
|
|
||||||
_actionQueue = Queue.Synchronized(new Queue());
|
|
||||||
_mainThread = new(ThreadLoop) { IsBackground = true };
|
|
||||||
_mainThread.Start();
|
|
||||||
|
|
||||||
lock (_actionQueue.SyncRoot)
|
|
||||||
{
|
|
||||||
_actionQueue.Enqueue(() =>
|
|
||||||
{
|
|
||||||
_script = script;
|
|
||||||
_script.API.InvokeStart();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ThreadLoop()
|
|
||||||
{
|
|
||||||
while (!Program.ReadyToStop)
|
|
||||||
{
|
|
||||||
Queue localQueue;
|
|
||||||
lock (_actionQueue.SyncRoot)
|
|
||||||
{
|
|
||||||
localQueue = new(_actionQueue);
|
|
||||||
_actionQueue.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
while (localQueue.Count > 0)
|
|
||||||
{
|
|
||||||
(localQueue.Dequeue() as Action)?.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 15 milliseconds to sleep to reduce CPU usage
|
|
||||||
Thread.Sleep(15);
|
|
||||||
}
|
|
||||||
|
|
||||||
_script.API.InvokeStop();
|
|
||||||
ReadyToStop = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool InvokeModPacketReceived(long from, long target, string modName, byte customID, byte[] bytes)
|
|
||||||
{
|
|
||||||
Task<bool> task = new(() => _script.API.InvokeModPacketReceived(from, target, modName, customID, bytes));
|
|
||||||
task.Start();
|
|
||||||
task.Wait(5000);
|
|
||||||
|
|
||||||
return task.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvokePlayerHandshake(Client client)
|
|
||||||
{
|
|
||||||
lock (_actionQueue.SyncRoot)
|
|
||||||
{
|
|
||||||
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerHandshake(client)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvokePlayerConnected(Client client)
|
|
||||||
{
|
|
||||||
lock (_actionQueue.SyncRoot)
|
|
||||||
{
|
|
||||||
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerConnected(client)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvokePlayerDisconnected(Client client)
|
|
||||||
{
|
|
||||||
lock (_actionQueue.SyncRoot)
|
|
||||||
{
|
|
||||||
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerDisconnected(client)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool InvokeChatMessage(string username, string message)
|
|
||||||
{
|
|
||||||
Task<bool> task = new(() => _script.API.InvokeChatMessage(username, message));
|
|
||||||
task.Start();
|
|
||||||
task.Wait(5000);
|
|
||||||
|
|
||||||
return task.Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void InvokePlayerUpdate(Client client)
|
|
||||||
{
|
|
||||||
lock (_actionQueue.SyncRoot)
|
|
||||||
{
|
|
||||||
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerUpdate(client)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void InvokeTick(long tick)
|
|
||||||
{
|
|
||||||
lock (_actionQueue.SyncRoot)
|
|
||||||
{
|
|
||||||
_actionQueue.Enqueue(new Action(() => _script.API.InvokeTick(tick)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class API
|
|
||||||
{
|
|
||||||
#region DELEGATES
|
|
||||||
public delegate void EmptyEvent();
|
|
||||||
public delegate void OnTickEvent(long tick);
|
|
||||||
public delegate void ChatEvent(string username, string message, CancelEventArgs cancel);
|
|
||||||
public delegate void PlayerEvent(Client client);
|
|
||||||
public delegate void ModEvent(long from, long target, string modName, byte customID, byte[] bytes, CancelEventArgs args);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region EVENTS
|
|
||||||
/// <summary>
|
|
||||||
/// Called every tick
|
|
||||||
/// </summary>
|
|
||||||
public event OnTickEvent OnTick;
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the server has started
|
|
||||||
/// </summary>
|
|
||||||
public event EmptyEvent OnStart;
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the server has stopped
|
|
||||||
/// </summary>
|
|
||||||
public event EmptyEvent OnStop;
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the server receives a new chat message for players
|
|
||||||
/// </summary>
|
|
||||||
public event ChatEvent OnChatMessage;
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the server receives a new incoming connection
|
|
||||||
/// </summary>
|
|
||||||
public event PlayerEvent OnPlayerHandshake;
|
|
||||||
/// <summary>
|
|
||||||
/// Called when a new player has successfully connected
|
|
||||||
/// </summary>
|
|
||||||
public event PlayerEvent OnPlayerConnected;
|
|
||||||
/// <summary>
|
|
||||||
/// Called when a new player has successfully disconnected
|
|
||||||
/// </summary>
|
|
||||||
public event PlayerEvent OnPlayerDisconnected;
|
|
||||||
/// <summary>
|
|
||||||
/// Called when a new player sends data like health
|
|
||||||
/// </summary>
|
|
||||||
public event PlayerEvent OnPlayerUpdate;
|
|
||||||
public event ModEvent OnModPacketReceived;
|
|
||||||
|
|
||||||
public void InvokeTick(long tick)
|
|
||||||
{
|
|
||||||
OnTick?.Invoke(tick);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvokeStart()
|
|
||||||
{
|
|
||||||
OnStart?.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvokeStop()
|
|
||||||
{
|
|
||||||
OnStop?.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvokePlayerHandshake(Client client)
|
|
||||||
{
|
|
||||||
OnPlayerHandshake?.Invoke(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvokePlayerConnected(Client client)
|
|
||||||
{
|
|
||||||
OnPlayerConnected?.Invoke(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvokePlayerDisconnected(Client client)
|
|
||||||
{
|
|
||||||
OnPlayerDisconnected?.Invoke(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InvokePlayerUpdate(Client client)
|
|
||||||
{
|
|
||||||
OnPlayerUpdate?.Invoke(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool InvokeChatMessage(string username, string message)
|
|
||||||
{
|
|
||||||
CancelEventArgs args = new(false);
|
|
||||||
OnChatMessage?.Invoke(username, message, args);
|
|
||||||
return args.Cancel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool InvokeModPacketReceived(long from, long target, string modName, byte customID, byte[] bytes)
|
|
||||||
{
|
|
||||||
CancelEventArgs args = new(false);
|
|
||||||
OnModPacketReceived?.Invoke(from, target, modName, customID, bytes, args);
|
|
||||||
return args.Cancel;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region FUNCTIONS
|
|
||||||
/// <summary>
|
|
||||||
/// Send a mod packet to all players
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="modName">The name of the modification that will receive the data</param>
|
|
||||||
/// <param name="customID">The ID to check what this data is</param>
|
|
||||||
/// <param name="bytes">The serialized data</param>
|
|
||||||
/// <param name="playerList">The list of player ID (PedID) that will receive the data</param>
|
|
||||||
public static void SendModPacketToAll(string modName, byte customID, byte[] bytes, List<int> playerList = null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{// A resource can be calling this function on disconnect of the last player in the server and we will
|
|
||||||
// get an empty connection list, make sure connections has at least one handle in it
|
|
||||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
|
||||||
new Packets.Mod()
|
|
||||||
{
|
|
||||||
Name = modName,
|
|
||||||
CustomPacketID = customID,
|
|
||||||
Bytes = bytes
|
|
||||||
}.Pack(outgoingMessage);
|
|
||||||
if (playerList==null)
|
|
||||||
{
|
|
||||||
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Mod);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach(var c in Server.Clients.Values)
|
|
||||||
{
|
|
||||||
if (playerList.Contains(c.Player.PedID))
|
|
||||||
{
|
|
||||||
Server.MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Mod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Server.MainNetServer.FlushSendQueue();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Send a native call (Function.Call) to all players.
|
|
||||||
/// Keys = int, float, bool, string and lvector3
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="hash">The hash (Example: 0x25223CA6B4D20B7F = GET_CLOCK_HOURS)</param>
|
|
||||||
/// <param name="args">The arguments (Example: string = int, object = 5)</param>
|
|
||||||
public static void SendNativeCallToAll(ulong hash, params object[] args)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Server.MainNetServer.ConnectionsCount == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args != null && args.Length == 0)
|
|
||||||
{
|
|
||||||
Program.Logger.Error($"[ServerScript->SendNativeCallToAll(ulong hash, params object[] args)]: args is not null!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Packets.NativeCall packet = new()
|
|
||||||
{
|
|
||||||
Hash = hash,
|
|
||||||
Args = new List<object>(args) ?? new List<object>()
|
|
||||||
};
|
|
||||||
|
|
||||||
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
|
|
||||||
packet.Pack(outgoingMessage);
|
|
||||||
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Native);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a list of all Clients
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>All clients as a dictionary indexed by NetID</returns>
|
|
||||||
public static Dictionary<long,Client> GetAllClients()
|
|
||||||
{
|
|
||||||
return Server.Clients;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the client by its username
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="username">The username to search for</param>
|
|
||||||
/// <returns>The Client from this user or null</returns>
|
|
||||||
public static Client GetClientByUsername(string username)
|
|
||||||
{
|
|
||||||
return Server.Clients.Values.FirstOrDefault(x => x.Player.Username.ToLower() == username.ToLower());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Send a chat message to all players
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message">The chat message</param>
|
|
||||||
/// <param name="username">The username which send this message (default = "Server")</param>
|
|
||||||
/// <param name="netHandleList">The list of connections (players) who received this chat message</param>
|
|
||||||
public static void SendChatMessageToAll(string message, string username = "Server", List<long> netHandleList = null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (Server.MainNetServer.ConnectionsCount == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<NetConnection> connections = netHandleList == null
|
|
||||||
? Server.MainNetServer.Connections
|
|
||||||
: Server.MainNetServer.Connections.FindAll(c => netHandleList.Contains(c.RemoteUniqueIdentifier));
|
|
||||||
|
|
||||||
Server.SendChatMessage(username, message, connections);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Send CleanUpWorld to all players to delete all objects created by the server
|
|
||||||
/// </summary>
|
|
||||||
public static void SendCleanUpWorldToAll(List<long> netHandleList = null)
|
|
||||||
{
|
|
||||||
if (Server.MainNetServer.ConnectionsCount == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<NetConnection> 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Register a new command chat command (Example: "/test")
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The name of the command (Example: "test" for "/test")</param>
|
|
||||||
/// <param name="usage">How to use this message (argsLength required!)</param>
|
|
||||||
/// <param name="argsLength">The length of args (Example: "/message USERNAME MESSAGE" = 2) (usage required!)</param>
|
|
||||||
/// <param name="callback">Create a new function!</param>
|
|
||||||
public static void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback)
|
|
||||||
{
|
|
||||||
Server.RegisterCommand(name, usage, argsLength, callback);
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// Register a new command chat command (Example: "/test")
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">The name of the command (Example: "test" for "/test")</param>
|
|
||||||
/// <param name="callback">Create a new function!</param>
|
|
||||||
public static void RegisterCommand(string name, Action<CommandContext> callback)
|
|
||||||
{
|
|
||||||
Server.RegisterCommand(name, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Register a class of commands
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The name of your class with functions</typeparam>
|
|
||||||
public static void RegisterCommands<T>()
|
|
||||||
{
|
|
||||||
Server.RegisterCommands<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Register a class of events
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The name of your class with functions</typeparam>
|
|
||||||
public static void RegisterEvents<T>()
|
|
||||||
{
|
|
||||||
Server.RegisterEvents<T>();
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
|
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
|
||||||
|
169
Server/Server.cs
169
Server/Server.cs
@ -12,6 +12,7 @@ using Newtonsoft.Json;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Lidgren.Network;
|
using Lidgren.Network;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
|
using RageCoop.Server.Scripting;
|
||||||
|
|
||||||
namespace RageCoop.Server
|
namespace RageCoop.Server
|
||||||
{
|
{
|
||||||
@ -21,21 +22,20 @@ namespace RageCoop.Server
|
|||||||
public string Address { get; set; }
|
public string Address { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Server
|
internal class Server
|
||||||
{
|
{
|
||||||
private static readonly string _compatibleVersion = "V0_4";
|
private static readonly string _compatibleVersion = "V0_4";
|
||||||
private static long _currentTick = 0;
|
|
||||||
|
|
||||||
public static readonly Settings MainSettings = Util.Read<Settings>("Settings.xml");
|
public static readonly Settings MainSettings = Util.Read<Settings>("Settings.xml");
|
||||||
private readonly Blocklist _mainBlocklist = Util.Read<Blocklist>("Blocklist.xml");
|
private readonly Blocklist _mainBlocklist = Util.Read<Blocklist>("Blocklist.xml");
|
||||||
public static NetServer MainNetServer;
|
public static NetServer MainNetServer;
|
||||||
|
|
||||||
public static Resource RunningResource = null;
|
|
||||||
public static readonly Dictionary<Command, Action<CommandContext>> Commands = new();
|
public static readonly Dictionary<Command, Action<CommandContext>> Commands = new();
|
||||||
public static readonly Dictionary<TriggerEvent, Action<EventContext>> TriggerEvents = new();
|
public static readonly Dictionary<TriggerEvent, Action<EventContext>> TriggerEvents = new();
|
||||||
|
|
||||||
public static readonly Dictionary<long,Client> Clients = new();
|
public static readonly Dictionary<long,Client> Clients = new();
|
||||||
private static System.Timers.Timer SendLatencyTimer = new System.Timers.Timer(5000);
|
private static System.Timers.Timer SendLatencyTimer = new System.Timers.Timer(5000);
|
||||||
|
public static readonly Engine ScriptingEngine = new();
|
||||||
public Server()
|
public Server()
|
||||||
{
|
{
|
||||||
Program.Logger.Info("================");
|
Program.Logger.Info("================");
|
||||||
@ -169,40 +169,7 @@ namespace RageCoop.Server
|
|||||||
}).Start();
|
}).Start();
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
ScriptingEngine.LoadAll();
|
||||||
if (!string.IsNullOrEmpty(MainSettings.Resource))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string resourcepath = AppDomain.CurrentDomain.BaseDirectory + "resources" + Path.DirectorySeparatorChar + MainSettings.Resource + ".dll";
|
|
||||||
Program.Logger.Info($"Loading resource \"{MainSettings.Resource}.dll\"...");
|
|
||||||
|
|
||||||
Assembly asm = Assembly.LoadFrom(resourcepath);
|
|
||||||
Type[] types = asm.GetExportedTypes();
|
|
||||||
IEnumerable<Type> validTypes = types.Where(t => !t.IsInterface && !t.IsAbstract).Where(t => typeof(ServerScript).IsAssignableFrom(t));
|
|
||||||
Type[] enumerable = validTypes as Type[] ?? validTypes.ToArray();
|
|
||||||
|
|
||||||
if (!enumerable.Any())
|
|
||||||
{
|
|
||||||
Program.Logger.Error("ERROR: No classes that inherit from ServerScript have been found in the assembly. Starting freeroam.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Activator.CreateInstance(enumerable.ToArray()[0]) is ServerScript script)
|
|
||||||
{
|
|
||||||
RunningResource = new(script);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Program.Logger.Warning("Could not create resource: it is null.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Program.Logger.Error(e.InnerException.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Program.Logger.Info("Searching for client-side files...");
|
Program.Logger.Info("Searching for client-side files...");
|
||||||
DownloadManager.CheckForDirectoryAndFiles();
|
DownloadManager.CheckForDirectoryAndFiles();
|
||||||
|
|
||||||
@ -224,10 +191,6 @@ namespace RageCoop.Server
|
|||||||
}), new SynchronizationContext());
|
}), new SynchronizationContext());
|
||||||
while (!Program.ReadyToStop)
|
while (!Program.ReadyToStop)
|
||||||
{
|
{
|
||||||
if (RunningResource != null)
|
|
||||||
{
|
|
||||||
RunningResource.InvokeTick(++_currentTick);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only new clients that did not receive files on connection will receive the current files in "clientside"
|
// Only new clients that did not receive files on connection will receive the current files in "clientside"
|
||||||
if (DownloadManager.AnyFileExists)
|
if (DownloadManager.AnyFileExists)
|
||||||
@ -249,23 +212,15 @@ namespace RageCoop.Server
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 3 milliseconds to sleep to reduce CPU usage
|
// 15 milliseconds to sleep to reduce CPU usage
|
||||||
Thread.Sleep(3);
|
Thread.Sleep(15);
|
||||||
}
|
}
|
||||||
|
|
||||||
Program.Logger.Warning("Server is shutting down!");
|
Program.Logger.Warning("Server is shutting down!");
|
||||||
if (RunningResource != null)
|
|
||||||
{
|
|
||||||
// Waiting for resource...
|
|
||||||
while (!RunningResource.ReadyToStop)
|
|
||||||
{
|
|
||||||
// 16 milliseconds to sleep to reduce CPU usage
|
|
||||||
Thread.Sleep(1000 / 60);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (MainNetServer.Connections.Count > 0)
|
if (MainNetServer.Connections.Count > 0)
|
||||||
{
|
{
|
||||||
|
API.Events.InvokeOnStop();
|
||||||
MainNetServer.Connections.ForEach(x => x.Disconnect("Server is shutting down!"));
|
MainNetServer.Connections.ForEach(x => x.Disconnect("Server is shutting down!"));
|
||||||
// We have to wait some time for all Disconnect() messages to be sent successfully
|
// We have to wait some time for all Disconnect() messages to be sent successfully
|
||||||
// Sleep for 1 second
|
// Sleep for 1 second
|
||||||
@ -437,6 +392,7 @@ namespace RageCoop.Server
|
|||||||
Packets.ChatMessage packet = new();
|
Packets.ChatMessage packet = new();
|
||||||
packet.Unpack(data);
|
packet.Unpack(data);
|
||||||
|
|
||||||
|
API.Events.InvokeOnChatMessage(packet,message.SenderConnection);
|
||||||
SendChatMessage(packet);
|
SendChatMessage(packet);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -456,7 +412,7 @@ namespace RageCoop.Server
|
|||||||
Packets.NativeResponse packet = new();
|
Packets.NativeResponse packet = new();
|
||||||
packet.Unpack(data);
|
packet.Unpack(data);
|
||||||
|
|
||||||
Client client = Util.GetClientByID(message.SenderConnection.RemoteUniqueIdentifier);
|
Client client = Util.GetClientByNetID(message.SenderConnection.RemoteUniqueIdentifier);
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
if (client.Callbacks.ContainsKey(packet.ID))
|
if (client.Callbacks.ContainsKey(packet.ID))
|
||||||
@ -484,7 +440,7 @@ namespace RageCoop.Server
|
|||||||
Packets.FileTransferComplete packet = new();
|
Packets.FileTransferComplete packet = new();
|
||||||
packet.Unpack(data);
|
packet.Unpack(data);
|
||||||
|
|
||||||
Client client = Util.GetClientByID(message.SenderConnection.RemoteUniqueIdentifier);
|
Client client = Util.GetClientByNetID(message.SenderConnection.RemoteUniqueIdentifier);
|
||||||
if (client != null && !client.FilesReceived)
|
if (client != null && !client.FilesReceived)
|
||||||
{
|
{
|
||||||
DownloadManager.TryToRemoveClient(client.NetID, packet.ID);
|
DownloadManager.TryToRemoveClient(client.NetID, packet.ID);
|
||||||
@ -511,7 +467,7 @@ namespace RageCoop.Server
|
|||||||
Client client = null;
|
Client client = null;
|
||||||
lock (Clients)
|
lock (Clients)
|
||||||
{
|
{
|
||||||
client = Util.GetClientByID(senderNetHandle);
|
client = Util.GetClientByNetID(senderNetHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client != null)
|
if (client != null)
|
||||||
@ -571,10 +527,10 @@ namespace RageCoop.Server
|
|||||||
break;
|
break;
|
||||||
case NetIncomingMessageType.ConnectionLatencyUpdated:
|
case NetIncomingMessageType.ConnectionLatencyUpdated:
|
||||||
{
|
{
|
||||||
Client client = Util.GetClientByID(message.SenderConnection.RemoteUniqueIdentifier);
|
Client client = Util.GetClientByNetID(message.SenderConnection.RemoteUniqueIdentifier);
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
client.Latency = message.ReadFloat();
|
client.Player.Latency = message.ReadFloat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -607,7 +563,7 @@ namespace RageCoop.Server
|
|||||||
{
|
{
|
||||||
PedID=c.Player.PedID,
|
PedID=c.Player.PedID,
|
||||||
Username=c.Player.Username,
|
Username=c.Player.Username,
|
||||||
Latency=c.Player.Latency=c.Latency,
|
Latency=c.Player.Latency,
|
||||||
}.Pack(outgoingMessage);
|
}.Pack(outgoingMessage);
|
||||||
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
|
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
|
||||||
});
|
});
|
||||||
@ -625,38 +581,38 @@ namespace RageCoop.Server
|
|||||||
|
|
||||||
#region -- PLAYER --
|
#region -- PLAYER --
|
||||||
// Before we approve the connection, we must shake hands
|
// Before we approve the connection, we must shake hands
|
||||||
private void GetHandshake(NetConnection local, Packets.Handshake packet)
|
private void GetHandshake(NetConnection connection, Packets.Handshake packet)
|
||||||
{
|
{
|
||||||
Program.Logger.Debug("New handshake from: [Name: " + packet.Username + " | Address: " + local.RemoteEndPoint.Address.ToString() + "]");
|
Program.Logger.Debug("New handshake from: [Name: " + packet.Username + " | Address: " + connection.RemoteEndPoint.Address.ToString() + "]");
|
||||||
|
|
||||||
if (!packet.ModVersion.StartsWith(_compatibleVersion))
|
if (!packet.ModVersion.StartsWith(_compatibleVersion))
|
||||||
{
|
{
|
||||||
local.Deny($"RAGECOOP version {_compatibleVersion.Replace('_', '.')}.x required!");
|
connection.Deny($"RAGECOOP version {_compatibleVersion.Replace('_', '.')}.x required!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(packet.Username))
|
if (string.IsNullOrWhiteSpace(packet.Username))
|
||||||
{
|
{
|
||||||
local.Deny("Username is empty or contains spaces!");
|
connection.Deny("Username is empty or contains spaces!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (packet.Username.Any(p => !char.IsLetterOrDigit(p) && !(p == '_') && !(p=='-')))
|
if (packet.Username.Any(p => !char.IsLetterOrDigit(p) && !(p == '_') && !(p=='-')))
|
||||||
{
|
{
|
||||||
local.Deny("Username contains special chars!");
|
connection.Deny("Username contains special chars!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_mainBlocklist.Username.Contains(packet.Username.ToLower()))
|
if (_mainBlocklist.Username.Contains(packet.Username.ToLower()))
|
||||||
{
|
{
|
||||||
local.Deny("This Username has been blocked by this server!");
|
connection.Deny("This Username has been blocked by this server!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_mainBlocklist.IP.Contains(local.RemoteEndPoint.ToString().Split(":")[0]))
|
if (_mainBlocklist.IP.Contains(connection.RemoteEndPoint.ToString().Split(":")[0]))
|
||||||
{
|
{
|
||||||
local.Deny("This IP was blocked by this server!");
|
connection.Deny("This IP was blocked by this server!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Clients.Values.Any(x => x.Player.Username.ToLower() == packet.Username.ToLower()))
|
if (Clients.Values.Any(x => x.Player.Username.ToLower() == packet.Username.ToLower()))
|
||||||
{
|
{
|
||||||
local.Deny("Username is already taken!");
|
connection.Deny("Username is already taken!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,11 +623,11 @@ namespace RageCoop.Server
|
|||||||
// Add the player to Players
|
// Add the player to Players
|
||||||
lock (Clients)
|
lock (Clients)
|
||||||
{
|
{
|
||||||
Clients.Add(local.RemoteUniqueIdentifier,
|
Clients.Add(connection.RemoteUniqueIdentifier,
|
||||||
tmpClient = new Client()
|
tmpClient = new Client()
|
||||||
{
|
{
|
||||||
NetID = local.RemoteUniqueIdentifier,
|
NetID = connection.RemoteUniqueIdentifier,
|
||||||
Connection=local,
|
Connection=connection,
|
||||||
Player = new()
|
Player = new()
|
||||||
{
|
{
|
||||||
Username = packet.Username,
|
Username = packet.Username,
|
||||||
@ -680,6 +636,19 @@ namespace RageCoop.Server
|
|||||||
}
|
}
|
||||||
);;
|
);;
|
||||||
}
|
}
|
||||||
|
var args = new HandshakeEventArgs()
|
||||||
|
{
|
||||||
|
EndPoint=connection.RemoteEndPoint,
|
||||||
|
ID=packet.PedID,
|
||||||
|
Username=packet.Username
|
||||||
|
};
|
||||||
|
API.Events.InvokePlayerHandshake(args);
|
||||||
|
if (args.Cancel)
|
||||||
|
{
|
||||||
|
connection.Deny(args.DenyReason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Program.Logger.Info($"Handshake sucess, Player:{packet.Username} PedID:{packet.PedID}");
|
Program.Logger.Info($"Handshake sucess, Player:{packet.Username} PedID:{packet.PedID}");
|
||||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||||
|
|
||||||
@ -690,20 +659,14 @@ namespace RageCoop.Server
|
|||||||
Username = string.Empty,
|
Username = string.Empty,
|
||||||
ModVersion = string.Empty,
|
ModVersion = string.Empty,
|
||||||
}.Pack(outgoingMessage);
|
}.Pack(outgoingMessage);
|
||||||
|
|
||||||
// Accept the connection and send back a new handshake packet with the connection ID
|
// Accept the connection and send back a new handshake packet with the connection ID
|
||||||
local.Approve(outgoingMessage);
|
connection.Approve(outgoingMessage);
|
||||||
|
|
||||||
if (RunningResource != null)
|
|
||||||
{
|
|
||||||
RunningResource.InvokePlayerHandshake(tmpClient);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The connection has been approved, now we need to send all other players to the new player and the new player to all players
|
// 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 static void SendPlayerConnectPacket(NetConnection local)
|
||||||
{
|
{
|
||||||
Client localClient = Util.GetClientByID(local.RemoteUniqueIdentifier);
|
Client localClient = Util.GetClientByNetID(local.RemoteUniqueIdentifier);
|
||||||
if (localClient == null)
|
if (localClient == null)
|
||||||
{
|
{
|
||||||
local.Disconnect("No data found!");
|
local.Disconnect("No data found!");
|
||||||
@ -718,7 +681,7 @@ namespace RageCoop.Server
|
|||||||
{
|
{
|
||||||
long targetNetHandle = targetPlayer.RemoteUniqueIdentifier;
|
long targetNetHandle = targetPlayer.RemoteUniqueIdentifier;
|
||||||
|
|
||||||
Client targetClient = Util.GetClientByID(targetNetHandle);
|
Client targetClient = Util.GetClientByNetID(targetNetHandle);
|
||||||
if (targetClient != null)
|
if (targetClient != null)
|
||||||
{
|
{
|
||||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||||
@ -743,15 +706,9 @@ namespace RageCoop.Server
|
|||||||
}.Pack(outgoingMessage);
|
}.Pack(outgoingMessage);
|
||||||
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
|
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
|
||||||
}
|
}
|
||||||
|
API.Events.InvokePlayerConnected(localClient);
|
||||||
|
|
||||||
if (RunningResource != null)
|
|
||||||
{
|
|
||||||
RunningResource.InvokePlayerConnected(localClient);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Program.Logger.Info($"Player {localClient.Player.Username} connected!");
|
Program.Logger.Info($"Player {localClient.Player.Username} connected!");
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(MainSettings.WelcomeMessage))
|
if (!string.IsNullOrEmpty(MainSettings.WelcomeMessage))
|
||||||
{
|
{
|
||||||
@ -775,7 +732,7 @@ namespace RageCoop.Server
|
|||||||
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
|
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Client localClient = Util.GetClientByID( nethandle);
|
Client localClient = Util.GetClientByNetID( nethandle);
|
||||||
if (localClient == null)
|
if (localClient == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -783,14 +740,9 @@ namespace RageCoop.Server
|
|||||||
|
|
||||||
Clients.Remove(localClient.NetID);
|
Clients.Remove(localClient.NetID);
|
||||||
|
|
||||||
if (RunningResource != null)
|
API.Events.InvokePlayerDisconnected(localClient);
|
||||||
{
|
|
||||||
RunningResource.InvokePlayerDisconnected(localClient);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Program.Logger.Info($"Player {localClient.Player.Username} disconnected! ID:{playerPedID}");
|
Program.Logger.Info($"Player {localClient.Player.Username} disconnected! ID:{playerPedID}");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region SyncEntities
|
#region SyncEntities
|
||||||
@ -798,7 +750,7 @@ namespace RageCoop.Server
|
|||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
Client client = Util.GetClientByID(ClientID);
|
Client client = Util.GetClientByNetID(ClientID);
|
||||||
if (client == null)
|
if (client == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -812,14 +764,10 @@ namespace RageCoop.Server
|
|||||||
if (c.NetID==client.NetID) { continue; }
|
if (c.NetID==client.NetID) { continue; }
|
||||||
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
|
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
|
||||||
}
|
}
|
||||||
if (RunningResource != null && packet.ID==client.Player.PedID)
|
|
||||||
{
|
|
||||||
RunningResource.InvokePlayerUpdate(client);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private static void VehicleStateSync(Packets.VehicleStateSync packet, long ClientID)
|
private static void VehicleStateSync(Packets.VehicleStateSync packet, long ClientID)
|
||||||
{
|
{
|
||||||
Client client = Util.GetClientByID(ClientID);
|
Client client = Util.GetClientByNetID(ClientID);
|
||||||
if (client == null)
|
if (client == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -841,7 +789,7 @@ namespace RageCoop.Server
|
|||||||
}
|
}
|
||||||
private static void PedSync(Packets.PedSync packet, long ClientID)
|
private static void PedSync(Packets.PedSync packet, long ClientID)
|
||||||
{
|
{
|
||||||
Client client = Util.GetClientByID(ClientID);
|
Client client = Util.GetClientByNetID(ClientID);
|
||||||
if (client == null)
|
if (client == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -870,15 +818,10 @@ namespace RageCoop.Server
|
|||||||
}
|
}
|
||||||
MainNetServer.SendMessage(outgoingMessage,c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
|
MainNetServer.SendMessage(outgoingMessage,c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RunningResource != null && isPlayer)
|
|
||||||
{
|
|
||||||
RunningResource.InvokePlayerUpdate(client);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private static void VehicleSync(Packets.VehicleSync packet, long ClientID)
|
private static void VehicleSync(Packets.VehicleSync packet, long ClientID)
|
||||||
{
|
{
|
||||||
Client client = Util.GetClientByID(ClientID);
|
Client client = Util.GetClientByNetID(ClientID);
|
||||||
if (client == null)
|
if (client == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -907,7 +850,7 @@ namespace RageCoop.Server
|
|||||||
}
|
}
|
||||||
private static void ProjectileSync(Packets.ProjectileSync packet, long ClientID)
|
private static void ProjectileSync(Packets.ProjectileSync packet, long ClientID)
|
||||||
{
|
{
|
||||||
Client client = Util.GetClientByID(ClientID);
|
Client client = Util.GetClientByNetID(ClientID);
|
||||||
if (client == null)
|
if (client == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -925,8 +868,6 @@ namespace RageCoop.Server
|
|||||||
#endregion
|
#endregion
|
||||||
// Send a message to targets or all players
|
// Send a message to targets or all players
|
||||||
private static void SendChatMessage(Packets.ChatMessage packet, List<NetConnection> targets = null)
|
private static void SendChatMessage(Packets.ChatMessage packet, List<NetConnection> targets = null)
|
||||||
{
|
|
||||||
if (RunningResource != null)
|
|
||||||
{
|
{
|
||||||
if (packet.Message.StartsWith('/'))
|
if (packet.Message.StartsWith('/'))
|
||||||
{
|
{
|
||||||
@ -971,15 +912,7 @@ namespace RageCoop.Server
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RunningResource.InvokeChatMessage(packet.Username, packet.Message))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
packet.Message = packet.Message.Replace("~", "");
|
packet.Message = packet.Message.Replace("~", "");
|
||||||
|
|
||||||
SendChatMessage(packet.Username, packet.Message, targets);
|
SendChatMessage(packet.Username, packet.Message, targets);
|
||||||
|
|
||||||
Program.Logger.Info(packet.Username + ": " + packet.Message);
|
Program.Logger.Info(packet.Username + ": " + packet.Message);
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
public int MaxLatency { get; set; } = 500;
|
public int MaxLatency { get; set; } = 500;
|
||||||
public string Name { get; set; } = "RAGECOOP server";
|
public string Name { get; set; } = "RAGECOOP server";
|
||||||
public string WelcomeMessage { get; set; } = "Welcome on this server :)";
|
public string WelcomeMessage { get; set; } = "Welcome on this server :)";
|
||||||
public string Resource { get; set; } = "";
|
|
||||||
public bool UPnP { get; set; } = true;
|
public bool UPnP { get; set; } = true;
|
||||||
public bool AnnounceSelf { get; set; } = false;
|
public bool AnnounceSelf { get; set; } = false;
|
||||||
public string MasterServer { get; set; } = "[AUTO]";
|
public string MasterServer { get; set; } = "[AUTO]";
|
||||||
|
@ -47,7 +47,7 @@ namespace RageCoop.Server
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Client GetClientByID(long id)
|
public static Client GetClientByNetID(long id)
|
||||||
{
|
{
|
||||||
Client result = null;
|
Client result = null;
|
||||||
Server.Clients.TryGetValue(id,out result);
|
Server.Clients.TryGetValue(id,out result);
|
||||||
|
Reference in New Issue
Block a user