XML comments

This commit is contained in:
Sardelka
2022-07-01 13:54:18 +08:00
parent d31799ab7b
commit 4165b757a5
20 changed files with 299 additions and 152 deletions

View File

@ -172,7 +172,7 @@ namespace RageCoop.Client
}
}
public enum MuzzleDir:byte
internal enum MuzzleDir:byte
{
Forward=0,
Right = 1,

View File

@ -160,7 +160,7 @@ namespace RageCoop.Client
}
}
public class DownloadFile: System.IDisposable
internal class DownloadFile: System.IDisposable
{
public int FileID { get; set; } = 0;
public string FileName { get; set; } = string.Empty;

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\publish\win-x64\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net6.0-windows</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>True</PublishSingleFile>
<PublishReadyToRun>True</PublishReadyToRun>
</PropertyGroup>
</Project>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<History>True|2022-06-27T04:41:08.2707244Z;True|2022-06-27T12:39:50.9236964+08:00;True|2022-06-27T12:37:12.3619963+08:00;True|2022-06-27T12:36:59.5077744+08:00;True|2022-06-27T12:35:49.2538484+08:00;</History>
</PropertyGroup>
</Project>

View File

@ -5,8 +5,8 @@
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<ProduceReferenceAssembly>False</ProduceReferenceAssembly>
<GenerateDocumentationFile>False</GenerateDocumentationFile>
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<DocumentationFile></DocumentationFile>
<DebugType>portable</DebugType>
<AssemblyVersion>0.5.0</AssemblyVersion>

View File

@ -6,9 +6,18 @@ using RageCoop.Core;
namespace RageCoop.Client.Scripting
{
/// <summary>
///
/// </summary>
public class CustomEventReceivedArgs : EventArgs
{
/// <summary>
/// The event hash
/// </summary>
public int Hash { get; set; }
/// <summary>
/// Arguments
/// </summary>
public List<object> Args { get; set; }
}
/// <summary>
@ -50,7 +59,15 @@ namespace RageCoop.Client.Scripting
public static class Events
{
#region DELEGATES
/// <summary>
///
/// </summary>
public delegate void EmptyEvent();
/// <summary>
///
/// </summary>
/// <param name="hash"></param>
/// <param name="args"></param>
public delegate void CustomEvent(int hash, List<object> args);
#endregion
/// <summary>

View File

@ -9,7 +9,9 @@ using System.Collections.Generic;
namespace RageCoop.Client.Scripting
{
/// <summary>
///
/// </summary>
public class ClientResource
{
/// <summary>
@ -20,7 +22,13 @@ namespace RageCoop.Client.Scripting
/// A resource-specific folder that can be used to store your files.
/// </summary>
public string DataFolder { get; internal set; }
/// <summary>
/// Get all <see cref="ClientScript"/> instance in this resource.
/// </summary>
public List<ClientScript> Scripts { get; internal set; } = new List<ClientScript>();
/// <summary>
/// Get the <see cref="ResourceFile"/> where this script is loaded from.
/// </summary>
public Dictionary<string, ResourceFile> Files { get; internal set; } = new Dictionary<string, ResourceFile>();
}
internal class Resources
@ -108,11 +116,7 @@ namespace RageCoop.Client.Scripting
private List<ClientResource> LoadedResources = new List<ClientResource>();
private string BaseScriptType;
public Logger Logger { get; set; }
/// <summary>
/// Load a resource from a zip
/// </summary>
/// <param name="file"></param>
private void LoadResource(ZipFile file, string dataFolderRoot)
{
var r = new ClientResource()
@ -146,11 +150,6 @@ namespace RageCoop.Client.Scripting
}
LoadedResources.Add(r);
}
/// <summary>
/// Loads scripts from the specified assembly file.
/// </summary>
/// <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(ResourceFile file, string path, ClientResource resource, bool shadowCopy = true)
{
lock (LoadedResources)
@ -185,12 +184,6 @@ namespace RageCoop.Client.Scripting
return LoadScriptsFromAssembly(file, assembly, path, resource);
}
}
/// <summary>
/// Loads scripts from the specified assembly object.
/// </summary>
/// <param name="filename">The path to the file associated with this assembly.</param>
/// <param name="assembly">The assembly to load.</param>
/// <returns><see langword="true" /> on success, <see langword="false" /> otherwise</returns>
private bool LoadScriptsFromAssembly(ResourceFile rfile, Assembly assembly, string filename, ClientResource toload)
{
int count = 0;

View File

@ -11,7 +11,9 @@ namespace RageCoop.Client
/// Don't use it!
/// </summary>
public string Username { get; set; } = "Player";
/// <summary>
/// The password used to authenticate when connecting to a server.
/// </summary>
public string Password { get; set; } = "";
/// <summary>
/// Don't use it!

View File

@ -11,6 +11,9 @@ using RageCoop.Core;
namespace RageCoop.Client
{
/// <summary>
/// A synchronized vehicle instance
/// </summary>
public class SyncedVehicle : SyncedEntity
{
@ -19,7 +22,7 @@ namespace RageCoop.Client
/// <summary>
/// Create a local entity (outgoing sync)
/// </summary>
/// <param name="p"></param>
/// <param name="v"></param>
internal SyncedVehicle(Vehicle v)
{
@ -54,7 +57,7 @@ namespace RageCoop.Client
private byte[] _lastVehicleColors = new byte[] { 0, 0 };
private Dictionary<int, int> _lastVehicleMods = new Dictionary<int, int>();
private byte _lastRadioIndex=255;
#endregion
#region -- CRITICAL STUFF --

View File

@ -335,7 +335,7 @@ namespace RageCoop.Client
if (p.MainProjectile.AttachedEntity==null)
{
/// Prevent projectiles from exploding next to vehicle
// Prevent projectiles from exploding next to vehicle
if (WeaponUtil.VehicleProjectileWeapons.Contains((VehicleWeaponHash)p.MainProjectile.WeaponHash))
{
if (p.MainProjectile.WeaponHash!=(WeaponHash)VehicleWeaponHash.Tank && p.Origin.DistanceTo(p.MainProjectile.Position)<2)

View File

@ -8,26 +8,56 @@ using System.Security.Cryptography;
namespace RageCoop.Server
{
/// <summary>
/// Represents a ped from a client
/// </summary>
public class ServerPed
{
/// <summary>
/// The <see cref="Client"/> that is responsible synchronizing for this ped.
/// </summary>
public Client Owner { get; internal set; }
/// <summary>
/// The ped's ID (not handle!).
/// </summary>
public int ID { get;internal set; }
/// <summary>
/// The ID of the ped's last vehicle.
/// </summary>
public int VehicleID { get; internal set; }
/// <summary>
/// Position of this ped
/// </summary>
public Vector3 Position { get; internal set; }
/// <summary>
/// Health
/// </summary>
public int Health { get; internal set; }
}
/// <summary>
///
/// </summary>
public class PlayerConfig
{
#region CLIENT
/// <summary>
/// Whether to enable automatic respawn for this player. if set to false, player will just lay on the ground when it's dead
/// </summary>
public bool EnableAutoRespawn { get; set; }=true;
#endregion
/// <summary>
/// Whether to show the player's blip on map.
/// </summary>
public bool ShowBlip { get; set; } = true;
/// <summary>
/// Whether the player's nametag is visible to other players.
/// </summary>
public bool ShowNameTag { get; set; } = true;
/// <summary>
/// The blip's color.
/// </summary>
public GTA.BlipColor BlipColor { get; set; } = GTA.BlipColor.White;
public PlayerConfigFlags GetFlags()
internal PlayerConfigFlags GetFlags()
{
var flag=PlayerConfigFlags.None;
if (ShowBlip)
@ -41,6 +71,9 @@ namespace RageCoop.Server
return flag;
}
}
/// <summary>
/// Represent a player connected to this server.
/// </summary>
public class Client
{
private readonly Server Server;
@ -49,16 +82,30 @@ namespace RageCoop.Server
Server=server;
}
internal long NetID = 0;
public NetConnection Connection { get;internal set; }
internal NetConnection Connection { get;set; }
/// <summary>
/// The <see cref="ServerPed"/> instance representing the client's main character.
/// </summary>
public ServerPed Player { get; internal set; }
/// <summary>
/// The client's latncy in seconds.
/// </summary>
public float Latency { get; internal set; }
public int ID { get; internal set; }
private PlayerConfig _config { get; set; }=new PlayerConfig();
/// <summary>
/// The client's configuration
/// </summary>
public PlayerConfig Config { get { return _config; }set { _config=value;Server.SendPlayerInfos(); } }
internal readonly Dictionary<int, Action<object>> Callbacks = new();
internal byte[] PublicKey { get; set; }
/// <summary>
/// Indicates whether the client has succefully loaded all resources.
/// </summary>
public bool IsReady { get; internal set; }=false;
/// <summary>
///
/// </summary>
public string Username { get;internal set; } = "N/A";
#region CUSTOMDATA FUNCTIONS
/*
@ -95,15 +142,28 @@ namespace RageCoop.Server
#endregion
#region FUNCTIONS
/// <summary>
/// Kick this client
/// </summary>
/// <param name="reason"></param>
public void Kick(string reason="You have been kicked!")
{
Connection?.Disconnect(reason);
}
/// <summary>
/// Kick this client
/// </summary>
/// <param name="reasons">Reasons to kick</param>
public void Kick(params string[] reasons)
{
Kick(string.Join(" ", reasons));
}
/// <summary>
/// Send a chat messsage to this client, not visible to others.
/// </summary>
/// <param name="message"></param>
/// <param name="from"></param>
public void SendChatMessage(string message, string from = "Server")
{
try
@ -167,8 +227,12 @@ namespace RageCoop.Server
}
return ID;
}
public void SendCustomEvent(int id,List<object> args)
/// <summary>
/// Trigger a CustomEvent for this client
/// </summary>
/// <param name="hash">An unique identifier of the event, you can use <see cref="CustomEvents.Hash(string)"/> to get it from a string</param>
/// <param name="args"></param>
public void SendCustomEvent(int hash,List<object> args)
{
if (!IsReady)
{
@ -181,7 +245,7 @@ namespace RageCoop.Server
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.CustomEvent()
{
Hash=id,
Hash=hash,
Args=args
}.Pack(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Event);

View File

@ -11,6 +11,9 @@ using System.Net;
namespace RageCoop.Server.Scripting
{
/// <summary>
///
/// </summary>
public class APIEvents
{
private readonly Server Server;
@ -21,6 +24,9 @@ namespace RageCoop.Server.Scripting
#region INTERNAL
internal Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = new();
#endregion
/// <summary>
/// Invoked when a chat message is received.
/// </summary>
public event EventHandler<ChatEventArgs> OnChatMessage;
/// <summary>
/// Will be invoked from main thread before registered handlers
@ -38,6 +44,9 @@ namespace RageCoop.Server.Scripting
/// Will be invoked after the client connected and all resources(if any) have been loaded.
/// </summary>
public event EventHandler<Client> OnPlayerReady;
/// <summary>
/// Invoked when a player disconnected, all method won't be effective in this scope.
/// </summary>
public event EventHandler<Client> OnPlayerDisconnected;
/// <summary>
/// Invoked everytime a player's main ped has been updated
@ -120,6 +129,9 @@ namespace RageCoop.Server.Scripting
}
#endregion
}
/// <summary>
/// An class that can be used to interact with RageCoop server.
/// </summary>
public class API
{
private readonly Server Server;
@ -128,6 +140,9 @@ namespace RageCoop.Server.Scripting
Server=server;
Events=new(server);
}
/// <summary>
/// Server side events
/// </summary>
public readonly APIEvents Events;
#region FUNCTIONS
/*
@ -188,8 +203,9 @@ namespace RageCoop.Server.Scripting
}
/// <summary>
/// Send a chat message to all players
/// Send a chat message to all players, use <see cref="Client.SendChatMessage(string, string)"/> to send to an individual client.
/// </summary>
/// <param name="targets">The clients to send message, leave it null to send to all clients</param>
/// <param name="message">The chat message</param>
/// <param name="username">The username which send this message (default = "Server")</param>
public void SendChatMessage(string message, List<Client> targets = null, string username = "Server")
@ -211,17 +227,6 @@ namespace RageCoop.Server.Scripting
Server.Logger?.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
}
public void SendChatMessage(string message, Client target, string username = "Server")
{
try
{
Server.SendChatMessage(username, message, target.Connection);
}
catch (Exception e)
{
Server.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
@ -272,11 +277,30 @@ namespace RageCoop.Server.Scripting
(ctx) => { method.Invoke(obj, new object[] { ctx }); });
}
}
/// <summary>
/// Send an event and data to the specified clients. Use <see cref="Client.SendCustomEvent(int, List{object})"/> if you want to send event to individual client.
/// </summary>
/// <param name="name">The name of the event, will be hashed to an int. For optimal performence, you should hash it in a static contructor inside the shared library, then call <see cref="SendCustomEvent(int, List{object}, List{Client})"/>.</param>
/// <param name="args">The objects conataing your data, supported types: byte, short, ushort, int, uint, long, ulong, float, bool, string.</param>
/// <param name="targets">The target clients to send. Leave it null to send to all clients</param>
public void SendCustomEvent(string name, List<object> args = null, List<Client> targets = null)
{
targets ??= new(Server.Clients.Values);
var p = new Packets.CustomEvent()
{
Args=args,
Hash=CustomEvents.Hash(name)
};
foreach (var c in targets)
{
Server.Send(p, c, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered);
}
}
/// <summary>
/// Send an event and data to the specified clients. Use <see cref="Client.SendCustomEvent(int, List{object})"/> if you want to send event to individual client.
/// </summary>
/// <param name="eventHash">An unique identifier of the event</param>
/// <param name="eventHash">An unique identifier of the event, you can use <see cref="CustomEvents.Hash(string)"/> to get it from a string</param>
/// <param name="args">The objects conataing your data, supported types: byte, short, ushort, int, uint, long, ulong, float, bool, string.</param>
/// <param name="targets">The target clients to send. Leave it null to send to all clients</param>
public void SendCustomEvent(int eventHash,List<object> args=null,List<Client> targets=null)
@ -296,7 +320,7 @@ namespace RageCoop.Server.Scripting
/// Register an handler to the specifed event hash, one event can have multiple handlers.
/// </summary>
/// <param name="hash">An unique identifier of the event, you can hash your event name with <see cref="Core.Scripting.CustomEvents.Hash(string)"/></param>
/// <param name="handler">An handler to be invoked when the event is received from the server. This will be invoked from main thread.
/// <param name="handler">An handler to be invoked when the event is received from the server. This will be invoked from main thread.</param>
public void RegisterCustomEventHandler(int hash,Action<CustomEventReceivedArgs> handler)
{
List<Action<CustomEventReceivedArgs>> handlers;
@ -309,14 +333,19 @@ namespace RageCoop.Server.Scripting
handlers.Add(handler);
}
}
/// <summary>
/// Register an event handler for specified event name.
/// </summary>
/// <param name="name">This value will be hashed to an int to reduce overhead</param>
/// <param name="handler">The handler to be invoked when the event is received</param>
public void RegisterCustomEventHandler(string name, Action<CustomEventReceivedArgs> handler)
{
RegisterCustomEventHandler(CustomEvents.Hash(name), handler);
}
public Logger GetLogger()
{
return Server.Logger;
}
/// <summary>
/// Get a <see cref="Logger"/> that the server is currently using, you should use <see cref="ServerResource.Logger"/> to display resource-specific information.
/// </summary>
public Logger Logger { get { return Server.Logger; } }
#endregion
}
}

View File

@ -37,8 +37,8 @@ namespace RageCoop.Server.Scripting
}
catch (Exception ex)
{
API.GetLogger().Error("Failed to parse NativeResponse");
API.GetLogger().Error(ex);
API.Logger.Error("Failed to parse NativeResponse");
API.Logger.Error(ex);
}
}
}

View File

@ -7,37 +7,87 @@ using System.Net;
namespace RageCoop.Server.Scripting
{
/// <summary>
///
/// </summary>
public class ChatEventArgs : EventArgs
{
/// <summary>
/// The client that sent this message
/// </summary>
public Client Sender { get; set; }
/// <summary>
/// Message
/// </summary>
public string Message { get; set; }
}
/// <summary>
///
/// </summary>
public class CustomEventReceivedArgs : EventArgs
{
/// <summary>
/// The <see cref="Client"/> that triggered this event
/// </summary>
public Client Sender { get; set; }
/// <summary>
/// The event hash
/// </summary>
public int Hash { get; set; }
/// <summary>
/// The arguments of this event
/// </summary>
public List<object> Args { get; set; }
}
/// <summary>
///
/// </summary>
public class OnCommandEventArgs : EventArgs
{
/// <summary>
/// The <see cref="Client"/> that executed this command.
/// </summary>
public Client Sender { get; set; }
/// <summary>
/// The name of executed command
/// </summary>
public string Name { get; set; }
/// <summary>
/// Arguments
/// </summary>
public string[] Args { get; set; }
/// <summary>
/// If this value was set to true, corresponding handler registered with <see cref="API.RegisterCommand(string, Action{CommandContext})"/> will not be invoked.
/// </summary>
public bool Cancel { get; set; } = false;
}
/// <summary>
///
/// </summary>
public class HandshakeEventArgs : EventArgs
{
/// <summary>
/// The player's ID
/// </summary>
public int ID { get; set; }
/// <summary>
/// The claimed username
/// </summary>
public string Username { get; set; }
/// <summary>
/// The client password hashed with SHA256 algorithm.
/// </summary>
public string PasswordHash { get; set; }
/// <summary>
/// The <see cref="EndPoint"/> that sent the handshake request.
/// </summary>
public IPEndPoint EndPoint { get; set; }
/// <summary>
/// Deny the connection attempt
/// </summary>
/// <param name="reason"></param>
public void Deny(string reason)
{
DenyReason=reason;

View File

@ -200,44 +200,5 @@ namespace RageCoop.Server.Scripting
}
});
}
/// <summary>
/// Load a resource from a zip
/// </summary>
/// <param name="file"></param>
/*
private void LoadResource(ZipFile file, string dataFolderRoot)
{
var r = new Resource()
{
Scripts = new List<Scriptable>(),
Name=Path.GetFileNameWithoutExtension(file.Name),
DataFolder=Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(file.Name))
};
Directory.CreateDirectory(r.DataFolder);
foreach (ZipEntry entry in file)
{
ResourceFile rFile;
r.Files.Add(entry.Name, rFile=new ResourceFile()
{
Name=entry.Name,
IsDirectory=entry.IsDirectory,
});
if (!entry.IsDirectory)
{
rFile.GetStream=() => { return file.GetInputStream(entry); };
if (entry.Name.EndsWith(".dll"))
{
var tmp = Path.GetTempFileName();
var f = File.OpenWrite(tmp);
rFile.GetStream().CopyTo(f);
f.Close();
LoadScriptsFromAssembly(rFile, tmp, r, false);
}
}
}
LoadedResources.Add(r.Name,r);
}
*/
}
}

View File

@ -10,7 +10,9 @@ using ICSharpCode.SharpZipLib.Zip;
namespace RageCoop.Server.Scripting
{
/// <summary>
/// A class representing a server side resource, each resource is isolated from another and will be started alongside the server.
/// </summary>
public class ServerResource : PluginLoader
{
private static readonly HashSet<string> ToIgnore = new()
@ -21,7 +23,6 @@ namespace RageCoop.Server.Scripting
"ScriptHookVDotNet3.dll",
"ScriptHookVDotNet.dll"
};
public Logger Logger;
internal ServerResource(PluginConfig config) : base(config) { }
internal static ServerResource LoadFrom(string resDir, string dataFolder, Logger logger = null, bool isTemp = false)
{
@ -85,15 +86,18 @@ namespace RageCoop.Server.Scripting
/// A resource-specific folder that can be used to store your files.
/// </summary>
public string DataFolder { get; internal set; }
public List<ServerScript> Scripts { get; internal set; } = new List<ServerScript>();
public Dictionary<string, ResourceFile> Files { get; internal set; } = new Dictionary<string, ResourceFile>();
/// <summary>
/// Loads scripts from the specified assembly object.
/// Get all <see cref="ServerScript"/> instance in this resource
/// </summary>
/// <param name="filename">The path to the file associated with this assembly.</param>
/// <param name="assembly">The assembly to load.</param>
/// <returns><see langword="true" /> on success, <see langword="false" /> otherwise</returns>
public List<ServerScript> Scripts { get; internal set; } = new List<ServerScript>();
/// <summary>
/// Get all <see cref="ResourceFile"/> that can be used to acces files in this resource
/// </summary>
public Dictionary<string, ResourceFile> Files { get; internal set; } = new Dictionary<string, ResourceFile>();
/// <summary>
/// Get a <see cref="Logger"/> instance that can be used to show information in console.
/// </summary>
public Logger Logger;
private bool LoadScriptsFromAssembly(ResourceFile rfile, Assembly assembly)
{
int count = 0;
@ -141,7 +145,8 @@ namespace RageCoop.Server.Scripting
Logger?.Info($"Loaded {count} script(s) in {rfile.Name}");
return count != 0;
}
public new void Dispose()
internal new void Dispose()
{
base.Dispose();
}

View File

@ -9,24 +9,32 @@ namespace RageCoop.Server.Scripting
public abstract class ServerScript
{
/// <summary>
/// This method would be called from main thread after all scripts have been loaded.
/// This method would be called from listener thread after all scripts have been loaded.
/// </summary>
public abstract void OnStart();
/// <summary>
/// This method would be called from main thread when the server is shutting down, you MUST terminate all background jobs/threads in this method.
/// This method would be called from listener thread when the server is shutting down, you MUST terminate all background jobs/threads in this method.
/// </summary>
public abstract void OnStop();
/// <summary>
/// Get the <see cref="API"/> instance that can be used to control the server.
/// </summary>
public API API { get; set; }
/// <summary>
/// Get the <see cref="ClientResource"/> object this script belongs to, this property will be initiated before <see cref="OnStart"/> (will be null if you access it in the constructor).
/// Get the <see cref="ServerResource"/> object this script belongs to, this property will be initiated before <see cref="OnStart"/> (will be null if you access it in the constructor).
/// </summary>
public ServerResource CurrentResource { get; internal set; }
/// <summary>
/// Get the <see cref="ResourceFile"/> that the script belongs to.
/// </summary>
public ResourceFile CurrentFile { get; internal set; }
}
/// <summary>
/// Decorate your method with this attribute and use <see cref="API.RegisterCommands{T}"/> or <see cref="API.RegisterCommands(object)"/> to register commands.
/// </summary>
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class Command : Attribute
{
@ -45,12 +53,15 @@ namespace RageCoop.Server.Scripting
/// </summary>
public short ArgsLength { get; set; }
public Command(string name)
internal Command(string name)
{
Name = name;
}
}
/// <summary>
/// The context containg command information.
/// </summary>
public class CommandContext
{
/// <summary>

View File

@ -18,14 +18,20 @@ using System.Threading.Tasks;
namespace RageCoop.Server
{
public struct IpInfo
internal struct IpInfo
{
[JsonProperty("ip")]
public string Address { get; set; }
}
/// <summary>
/// The instantiable RageCoop server class
/// </summary>
public class Server
{
/// <summary>
/// The API for controlling server and hooking events.
/// </summary>
public API API { get; private set; }
internal BaseScript BaseScript { get; set; }=new BaseScript();
internal readonly ServerSettings Settings;
@ -46,6 +52,12 @@ namespace RageCoop.Server
private Dictionary<int,Action<PacketType,byte[]>> PendingResponses=new();
private Dictionary<PacketType, Func<byte[],Packet>> RequestHandlers=new();
private readonly string _compatibleVersion = "V0_5";
/// <summary>
/// Instantiate a server.
/// </summary>
/// <param name="settings"></param>
/// <param name="logger"></param>
/// <exception cref="ArgumentNullException"></exception>
public Server(ServerSettings settings,Logger logger=null)
{
Settings = settings;
@ -57,6 +69,9 @@ namespace RageCoop.Server
Security=new Security(Logger);
}
/// <summary>
/// Spawn threads and start the server
/// </summary>
public void Start()
{
Logger?.Info("================");
@ -194,6 +209,9 @@ namespace RageCoop.Server
_listenerThread=new Thread(() => Listen());
_listenerThread.Start();
}
/// <summary>
/// Terminate threads and stop the server
/// </summary>
public void Stop()
{
_stopping = true;
@ -497,7 +515,7 @@ namespace RageCoop.Server
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
PedID=c.ID,
PedID=c.Player.ID,
Username=c.Username,
Latency=c.Latency,
Flags=c.Config.GetFlags(),
@ -585,7 +603,6 @@ namespace RageCoop.Server
NetID = connection.RemoteUniqueIdentifier,
Connection=connection,
Username=packet.Username,
ID=packet.PedID,
Player = new()
{
ID= packet.PedID,
@ -611,7 +628,7 @@ namespace RageCoop.Server
{
// NetHandle = targetNetHandle,
Username = target.Username,
PedID=target.ID,
PedID=target.Player.ID,
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, newClient.Connection, NetDeliveryMethod.ReliableOrdered, 0);
@ -623,7 +640,7 @@ namespace RageCoop.Server
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerConnect()
{
PedID=newClient.ID,
PedID=newClient.Player.ID,
Username = newClient.Username
}.Pack(outgoingMessage);
@ -648,13 +665,13 @@ namespace RageCoop.Server
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerDisconnect()
{
PedID=localClient.ID,
PedID=localClient.Player.ID,
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage,cons , NetDeliveryMethod.ReliableOrdered, 0);
}
_worker.QueueJob(() => API.Events.InvokePlayerDisconnected(localClient));
Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.ID}");
Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.Player.ID}");
Clients.Remove(localClient.NetID);
Security.RemoveConnection(localClient.Connection.RemoteEndPoint);
}
@ -677,10 +694,13 @@ namespace RageCoop.Server
private void VehicleStateSync(Packets.VehicleStateSync packet, Client client)
{
// Save the new data
if (packet.Passengers.ContainsValue(client.ID))
_worker.QueueJob(() =>
{
client.Player.VehicleID = packet.ID;
}
if (packet.Passengers.ContainsValue(client.Player.ID))
{
client.Player.VehicleID = packet.ID;
}
});
foreach (var c in Clients.Values)
{
@ -692,11 +712,12 @@ namespace RageCoop.Server
}
private void PedSync(Packets.PedSync packet, Client client)
{
bool isPlayer = packet.ID==client.ID;
bool isPlayer = packet.ID==client.Player.ID;
if (isPlayer)
{
client.Player.Position=packet.Position;
client.Player.Health=packet.Health ;
client.Player.Owner=client;
_worker.QueueJob(() => API.Events.InvokePlayerUpdate(client));
}
@ -951,13 +972,6 @@ namespace RageCoop.Server
}
return ID;
}
/// <summary>
/// Pack the packet then send to server.
/// </summary>
/// <param name="p"></param>
/// <param name="channel"></param>
/// <param name="method"></param>
internal void Send(Packet p,Client client, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();

View File

@ -1,14 +1,38 @@
namespace RageCoop.Server
{
/// <summary>
/// Settings for RageCoop Server
/// </summary>
public class ServerSettings
{
/// <summary>
/// Port to listen for incoming connections
/// </summary>
public int Port { get; set; } = 4499;
/// <summary>
/// Maximum number of players on this server
/// </summary>
public int MaxPlayers { get; set; } = 32;
/// <summary>
/// Maximum latency allowed for a client, a client will be kicked if it's latency it's higher than this value
/// </summary>
public int MaxLatency { get; set; } = 500;
/// <summary>
/// The server name to be shown on master server
/// </summary>
public string Name { get; set; } = "RAGECOOP server";
/// <summary>
/// The message to send when a client connected (not visible to others)
/// </summary>
public string WelcomeMessage { get; set; } = "Welcome on this server :)";
public bool HolePunch { get; set; } = true;
// public bool HolePunch { get; set; } = true;
/// <summary>
/// Whether or not to announce this server so that it'll appear on server list.
/// </summary>
public bool AnnounceSelf { get; set; } = false;
/// <summary>
/// Master server address, mostly doesn't to be changed.
/// </summary>
public string MasterServer { get; set; } = "[AUTO]";
/// <summary>
@ -18,7 +42,7 @@
/// <summary>
/// NPC data won't be sent to a player if their distance is greater than this value. -1 for unlimited.
/// </summary>
public float NpcStreamingDistance { get; set; } = 1000;
public float NpcStreamingDistance { get; set; } = 500;
/// <summary>
/// Player's data won't be sent to another player if their distance is greater than this value. -1 for unlimited.
/// </summary>