Scripting prototype?
This commit is contained in:
@ -11,7 +11,7 @@ namespace RageCoop.Client.Scripting
|
||||
{
|
||||
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
|
||||
{
|
||||
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 ScriptingEngine(Type baseScriptType, Logging.Logger logger)
|
||||
public ScriptingEngine(string baseScriptType, Logging.Logger logger)
|
||||
{
|
||||
BaseScriptType = baseScriptType;
|
||||
Logger = logger;
|
||||
@ -22,29 +29,29 @@ namespace RageCoop.Core.Scripting
|
||||
/// <summary>
|
||||
/// Loads scripts from the specified assembly file.
|
||||
/// </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>
|
||||
private bool LoadScriptsFromAssembly(string filename)
|
||||
protected bool LoadScriptsFromAssembly(string path)
|
||||
{
|
||||
if (!IsManagedAssembly(filename))
|
||||
return false;
|
||||
if (!IsManagedAssembly(path)) { 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;
|
||||
|
||||
try
|
||||
{
|
||||
assembly = Assembly.LoadFrom(filename);
|
||||
assembly = Assembly.LoadFrom(path);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger?.Error( "Unable to load "+Path.GetFileName(filename));
|
||||
Logger?.Error( "Unable to load "+Path.GetFileName(path));
|
||||
Logger?.Error(ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return LoadScriptsFromAssembly(assembly, filename);
|
||||
return LoadScriptsFromAssembly(assembly, path);
|
||||
}
|
||||
/// <summary>
|
||||
/// Loads scripts from the specified assembly object.
|
||||
@ -59,7 +66,7 @@ namespace RageCoop.Core.Scripting
|
||||
try
|
||||
{
|
||||
// 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);
|
||||
if (constructor != null && constructor.IsPublic)
|
||||
@ -90,7 +97,7 @@ namespace RageCoop.Core.Scripting
|
||||
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;
|
||||
}
|
||||
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
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RageCoop.Core", "Core\RageCoop.Core.csproj", "{CC2E8102-E568-4524-AA1F-F8E0F1CFE58A}"
|
||||
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
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
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|x64.ActiveCfg = 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
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{9DC11623-8A8B-4D17-B18B-91852922163D} = {F49A2617-832B-44DA-9D42-29B5DD09A186}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6CC7EA75-E4FF-4534-8EB6-0AEECF2620B7}
|
||||
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 long NetID = 0;
|
||||
private float _currentLatency = 0f;
|
||||
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}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
internal NetConnection Connection { get; set; }
|
||||
public PlayerData Player;
|
||||
private readonly Dictionary<string, object> _customData = new();
|
||||
private long _callbacksCount = 0;
|
||||
@ -64,13 +50,13 @@ namespace RageCoop.Server
|
||||
#endregion
|
||||
|
||||
#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")
|
||||
@ -201,32 +187,6 @@ namespace RageCoop.Server
|
||||
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)
|
||||
{
|
||||
if (!FilesReceived)
|
||||
|
@ -105,7 +105,7 @@ namespace RageCoop.Server
|
||||
{
|
||||
lock (Server.Clients)
|
||||
{
|
||||
Client x = Util.GetClientByID(client.NetHandle);
|
||||
Client x = Util.GetClientByNetID(client.NetHandle);
|
||||
if (x != null)
|
||||
{
|
||||
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.Threading.Tasks;
|
||||
using RageCoop.Core.Scripting;
|
||||
using System.IO;
|
||||
namespace RageCoop.Server.Scripting
|
||||
{
|
||||
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 Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Server
|
||||
namespace RageCoop.Server.Scripting
|
||||
{
|
||||
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)]
|
||||
|
169
Server/Server.cs
169
Server/Server.cs
@ -12,6 +12,7 @@ using Newtonsoft.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Lidgren.Network;
|
||||
using System.Timers;
|
||||
using RageCoop.Server.Scripting;
|
||||
|
||||
namespace RageCoop.Server
|
||||
{
|
||||
@ -21,21 +22,20 @@ namespace RageCoop.Server
|
||||
public string Address { get; set; }
|
||||
}
|
||||
|
||||
public class Server
|
||||
internal class Server
|
||||
{
|
||||
private static readonly string _compatibleVersion = "V0_4";
|
||||
private static long _currentTick = 0;
|
||||
|
||||
public static readonly Settings MainSettings = Util.Read<Settings>("Settings.xml");
|
||||
private readonly Blocklist _mainBlocklist = Util.Read<Blocklist>("Blocklist.xml");
|
||||
public static NetServer MainNetServer;
|
||||
|
||||
public static Resource RunningResource = null;
|
||||
public static readonly Dictionary<Command, Action<CommandContext>> Commands = new();
|
||||
public static readonly Dictionary<TriggerEvent, Action<EventContext>> TriggerEvents = new();
|
||||
|
||||
public static readonly Dictionary<long,Client> Clients = new();
|
||||
private static System.Timers.Timer SendLatencyTimer = new System.Timers.Timer(5000);
|
||||
public static readonly Engine ScriptingEngine = new();
|
||||
public Server()
|
||||
{
|
||||
Program.Logger.Info("================");
|
||||
@ -169,40 +169,7 @@ namespace RageCoop.Server
|
||||
}).Start();
|
||||
#endregion
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
ScriptingEngine.LoadAll();
|
||||
Program.Logger.Info("Searching for client-side files...");
|
||||
DownloadManager.CheckForDirectoryAndFiles();
|
||||
|
||||
@ -224,10 +191,6 @@ namespace RageCoop.Server
|
||||
}), new SynchronizationContext());
|
||||
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"
|
||||
if (DownloadManager.AnyFileExists)
|
||||
@ -249,23 +212,15 @@ namespace RageCoop.Server
|
||||
|
||||
|
||||
|
||||
// 3 milliseconds to sleep to reduce CPU usage
|
||||
Thread.Sleep(3);
|
||||
// 15 milliseconds to sleep to reduce CPU usage
|
||||
Thread.Sleep(15);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
API.Events.InvokeOnStop();
|
||||
MainNetServer.Connections.ForEach(x => x.Disconnect("Server is shutting down!"));
|
||||
// We have to wait some time for all Disconnect() messages to be sent successfully
|
||||
// Sleep for 1 second
|
||||
@ -437,6 +392,7 @@ namespace RageCoop.Server
|
||||
Packets.ChatMessage packet = new();
|
||||
packet.Unpack(data);
|
||||
|
||||
API.Events.InvokeOnChatMessage(packet,message.SenderConnection);
|
||||
SendChatMessage(packet);
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -456,7 +412,7 @@ namespace RageCoop.Server
|
||||
Packets.NativeResponse packet = new();
|
||||
packet.Unpack(data);
|
||||
|
||||
Client client = Util.GetClientByID(message.SenderConnection.RemoteUniqueIdentifier);
|
||||
Client client = Util.GetClientByNetID(message.SenderConnection.RemoteUniqueIdentifier);
|
||||
if (client != null)
|
||||
{
|
||||
if (client.Callbacks.ContainsKey(packet.ID))
|
||||
@ -484,7 +440,7 @@ namespace RageCoop.Server
|
||||
Packets.FileTransferComplete packet = new();
|
||||
packet.Unpack(data);
|
||||
|
||||
Client client = Util.GetClientByID(message.SenderConnection.RemoteUniqueIdentifier);
|
||||
Client client = Util.GetClientByNetID(message.SenderConnection.RemoteUniqueIdentifier);
|
||||
if (client != null && !client.FilesReceived)
|
||||
{
|
||||
DownloadManager.TryToRemoveClient(client.NetID, packet.ID);
|
||||
@ -511,7 +467,7 @@ namespace RageCoop.Server
|
||||
Client client = null;
|
||||
lock (Clients)
|
||||
{
|
||||
client = Util.GetClientByID(senderNetHandle);
|
||||
client = Util.GetClientByNetID(senderNetHandle);
|
||||
}
|
||||
|
||||
if (client != null)
|
||||
@ -571,10 +527,10 @@ namespace RageCoop.Server
|
||||
break;
|
||||
case NetIncomingMessageType.ConnectionLatencyUpdated:
|
||||
{
|
||||
Client client = Util.GetClientByID(message.SenderConnection.RemoteUniqueIdentifier);
|
||||
Client client = Util.GetClientByNetID(message.SenderConnection.RemoteUniqueIdentifier);
|
||||
if (client != null)
|
||||
{
|
||||
client.Latency = message.ReadFloat();
|
||||
client.Player.Latency = message.ReadFloat();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -607,7 +563,7 @@ namespace RageCoop.Server
|
||||
{
|
||||
PedID=c.Player.PedID,
|
||||
Username=c.Player.Username,
|
||||
Latency=c.Player.Latency=c.Latency,
|
||||
Latency=c.Player.Latency,
|
||||
}.Pack(outgoingMessage);
|
||||
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
|
||||
});
|
||||
@ -625,38 +581,38 @@ namespace RageCoop.Server
|
||||
|
||||
#region -- PLAYER --
|
||||
// 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))
|
||||
{
|
||||
local.Deny($"RAGECOOP version {_compatibleVersion.Replace('_', '.')}.x required!");
|
||||
connection.Deny($"RAGECOOP version {_compatibleVersion.Replace('_', '.')}.x required!");
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(packet.Username))
|
||||
{
|
||||
local.Deny("Username is empty or contains spaces!");
|
||||
connection.Deny("Username is empty or contains spaces!");
|
||||
return;
|
||||
}
|
||||
if (packet.Username.Any(p => !char.IsLetterOrDigit(p) && !(p == '_') && !(p=='-')))
|
||||
{
|
||||
local.Deny("Username contains special chars!");
|
||||
connection.Deny("Username contains special chars!");
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -667,11 +623,11 @@ namespace RageCoop.Server
|
||||
// Add the player to Players
|
||||
lock (Clients)
|
||||
{
|
||||
Clients.Add(local.RemoteUniqueIdentifier,
|
||||
Clients.Add(connection.RemoteUniqueIdentifier,
|
||||
tmpClient = new Client()
|
||||
{
|
||||
NetID = local.RemoteUniqueIdentifier,
|
||||
Connection=local,
|
||||
NetID = connection.RemoteUniqueIdentifier,
|
||||
Connection=connection,
|
||||
Player = new()
|
||||
{
|
||||
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}");
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
|
||||
@ -690,20 +659,14 @@ namespace RageCoop.Server
|
||||
Username = string.Empty,
|
||||
ModVersion = string.Empty,
|
||||
}.Pack(outgoingMessage);
|
||||
|
||||
// Accept the connection and send back a new handshake packet with the connection ID
|
||||
local.Approve(outgoingMessage);
|
||||
|
||||
if (RunningResource != null)
|
||||
{
|
||||
RunningResource.InvokePlayerHandshake(tmpClient);
|
||||
}
|
||||
connection.Approve(outgoingMessage);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
Client localClient = Util.GetClientByID(local.RemoteUniqueIdentifier);
|
||||
Client localClient = Util.GetClientByNetID(local.RemoteUniqueIdentifier);
|
||||
if (localClient == null)
|
||||
{
|
||||
local.Disconnect("No data found!");
|
||||
@ -718,7 +681,7 @@ namespace RageCoop.Server
|
||||
{
|
||||
long targetNetHandle = targetPlayer.RemoteUniqueIdentifier;
|
||||
|
||||
Client targetClient = Util.GetClientByID(targetNetHandle);
|
||||
Client targetClient = Util.GetClientByNetID(targetNetHandle);
|
||||
if (targetClient != null)
|
||||
{
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
@ -743,15 +706,9 @@ namespace RageCoop.Server
|
||||
}.Pack(outgoingMessage);
|
||||
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!");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(MainSettings.WelcomeMessage))
|
||||
{
|
||||
@ -775,7 +732,7 @@ namespace RageCoop.Server
|
||||
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
|
||||
}
|
||||
|
||||
Client localClient = Util.GetClientByID( nethandle);
|
||||
Client localClient = Util.GetClientByNetID( nethandle);
|
||||
if (localClient == null)
|
||||
{
|
||||
return;
|
||||
@ -783,14 +740,9 @@ namespace RageCoop.Server
|
||||
|
||||
Clients.Remove(localClient.NetID);
|
||||
|
||||
if (RunningResource != null)
|
||||
{
|
||||
RunningResource.InvokePlayerDisconnected(localClient);
|
||||
}
|
||||
else
|
||||
{
|
||||
API.Events.InvokePlayerDisconnected(localClient);
|
||||
Program.Logger.Info($"Player {localClient.Player.Username} disconnected! ID:{playerPedID}");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#region SyncEntities
|
||||
@ -798,7 +750,7 @@ namespace RageCoop.Server
|
||||
{
|
||||
|
||||
|
||||
Client client = Util.GetClientByID(ClientID);
|
||||
Client client = Util.GetClientByNetID(ClientID);
|
||||
if (client == null)
|
||||
{
|
||||
return;
|
||||
@ -812,14 +764,10 @@ namespace RageCoop.Server
|
||||
if (c.NetID==client.NetID) { continue; }
|
||||
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)
|
||||
{
|
||||
Client client = Util.GetClientByID(ClientID);
|
||||
Client client = Util.GetClientByNetID(ClientID);
|
||||
if (client == null)
|
||||
{
|
||||
return;
|
||||
@ -841,7 +789,7 @@ namespace RageCoop.Server
|
||||
}
|
||||
private static void PedSync(Packets.PedSync packet, long ClientID)
|
||||
{
|
||||
Client client = Util.GetClientByID(ClientID);
|
||||
Client client = Util.GetClientByNetID(ClientID);
|
||||
if (client == null)
|
||||
{
|
||||
return;
|
||||
@ -870,15 +818,10 @@ namespace RageCoop.Server
|
||||
}
|
||||
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)
|
||||
{
|
||||
Client client = Util.GetClientByID(ClientID);
|
||||
Client client = Util.GetClientByNetID(ClientID);
|
||||
if (client == null)
|
||||
{
|
||||
return;
|
||||
@ -907,7 +850,7 @@ namespace RageCoop.Server
|
||||
}
|
||||
private static void ProjectileSync(Packets.ProjectileSync packet, long ClientID)
|
||||
{
|
||||
Client client = Util.GetClientByID(ClientID);
|
||||
Client client = Util.GetClientByNetID(ClientID);
|
||||
if (client == null)
|
||||
{
|
||||
return;
|
||||
@ -925,8 +868,6 @@ namespace RageCoop.Server
|
||||
#endregion
|
||||
// Send a message to targets or all players
|
||||
private static void SendChatMessage(Packets.ChatMessage packet, List<NetConnection> targets = null)
|
||||
{
|
||||
if (RunningResource != null)
|
||||
{
|
||||
if (packet.Message.StartsWith('/'))
|
||||
{
|
||||
@ -971,15 +912,7 @@ namespace RageCoop.Server
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (RunningResource.InvokeChatMessage(packet.Username, packet.Message))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
packet.Message = packet.Message.Replace("~", "");
|
||||
|
||||
SendChatMessage(packet.Username, packet.Message, targets);
|
||||
|
||||
Program.Logger.Info(packet.Username + ": " + packet.Message);
|
||||
|
@ -7,7 +7,6 @@
|
||||
public int MaxLatency { get; set; } = 500;
|
||||
public string Name { get; set; } = "RAGECOOP server";
|
||||
public string WelcomeMessage { get; set; } = "Welcome on this server :)";
|
||||
public string Resource { get; set; } = "";
|
||||
public bool UPnP { get; set; } = true;
|
||||
public bool AnnounceSelf { get; set; } = false;
|
||||
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;
|
||||
Server.Clients.TryGetValue(id,out result);
|
||||
|
Reference in New Issue
Block a user