XML comments
This commit is contained in:
@ -172,7 +172,7 @@ namespace RageCoop.Client
|
||||
}
|
||||
|
||||
}
|
||||
public enum MuzzleDir:byte
|
||||
internal enum MuzzleDir:byte
|
||||
{
|
||||
Forward=0,
|
||||
Right = 1,
|
||||
|
@ -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;
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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!
|
||||
|
@ -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 --
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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>
|
||||
|
Reference in New Issue
Block a user