This commit is contained in:
sardelka9515
2022-10-23 19:02:39 +08:00
parent 6b34ab6e36
commit 2828b9b74f
114 changed files with 7374 additions and 7205 deletions

View File

@ -1,12 +1,11 @@
using GTA.Math;
using System.IO;
using System.IO;
using System.Text;
using GTA.Math;
namespace RageCoop.Core
{
internal class BitReader : BinaryReader
{
public BitReader(byte[] array) : base(new MemoryStream(array))
{
}
@ -21,6 +20,7 @@ namespace RageCoop.Core
{
return base.ReadBytes(ReadInt32());
}
public override string ReadString()
{
return Encoding.UTF8.GetString(ReadBytes(ReadInt32()));
@ -28,24 +28,26 @@ namespace RageCoop.Core
public Vector3 ReadVector3()
{
return new Vector3()
return new Vector3
{
X = ReadSingle(),
Y = ReadSingle(),
Z = ReadSingle()
};
}
public Vector2 ReadVector2()
{
return new Vector2()
return new Vector2
{
X = ReadSingle(),
Y = ReadSingle()
};
}
public Quaternion ReadQuaternion()
{
return new Quaternion()
return new Quaternion
{
X = ReadSingle(),
Y = ReadSingle(),
@ -54,4 +56,4 @@ namespace RageCoop.Core
};
}
}
}
}

View File

@ -1,8 +1,4 @@
using GTA.Math;
using Lidgren.Network;
using Newtonsoft.Json;
using RageCoop.Core.Scripting;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@ -15,38 +11,26 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using GTA;
using GTA.Math;
using Lidgren.Network;
using Newtonsoft.Json;
using RageCoop.Core.Scripting;
using Console = System.Console;
[assembly: InternalsVisibleTo("RageCoop.Server")]
[assembly: InternalsVisibleTo("RageCoop.Client")]
[assembly: InternalsVisibleTo("RageCoop.Client.Installer")]
[assembly: InternalsVisibleTo("RageCoop.Client.DataDumper")]
[assembly: InternalsVisibleTo("RageCoop.ResourceBuilder")]
namespace RageCoop.Core
{
internal static class CoreUtils
{
private static Random random = new Random();
public static string FormatToSharpStyle(string input,int offset=14)
{
var ss = input.Substring(offset).Split("_".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
// Replace first character with upper case
for (int i = 0; i < ss.Length; i++)
{
var sec = ss[i].ToLower();
var head = sec[0];
ss[i] = head.ToString().ToUpper() + sec.Remove(0, 1);
}
return string.Join("", ss);
}
public static string ToHex(this int value)
{
return String.Format("0x{0:X}", value);
}
public static string ToHex(this uint value)
{
return String.Format("0x{0:X}", value);
}
private static readonly HashSet<string> ToIgnore = new HashSet<string>()
private static readonly Random random = new Random();
private static readonly HashSet<string> ToIgnore = new HashSet<string>
{
"RageCoop.Client",
"RageCoop.Client.Loader",
@ -57,10 +41,36 @@ namespace RageCoop.Core
"ScriptHookVDotNet3",
"ScriptHookVDotNet"
};
public static int RandInt(int start,int end)
public static string FormatToSharpStyle(string input, int offset = 14)
{
var ss = input.Substring(offset).Split("_".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
// Replace first character with upper case
for (var i = 0; i < ss.Length; i++)
{
var sec = ss[i].ToLower();
var head = sec[0];
ss[i] = head.ToString().ToUpper() + sec.Remove(0, 1);
}
return string.Join("", ss);
}
public static string ToHex(this int value)
{
return string.Format("0x{0:X}", value);
}
public static string ToHex(this uint value)
{
return string.Format("0x{0:X}", value);
}
public static int RandInt(int start, int end)
{
return random.Next(start, end);
}
public static string GetTempDirectory(string dir = null)
{
dir = dir ?? Path.GetTempPath();
@ -72,109 +82,151 @@ namespace RageCoop.Core
return path;
}
public static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
public static void GetDependencies(Assembly assembly, ref HashSet<string> existing)
{
if (assembly.FullName.StartsWith("System")) { return; }
if (assembly.FullName.StartsWith("System")) return;
foreach (var name in assembly.GetReferencedAssemblies())
{
if (name.FullName.StartsWith("System")) { continue; }
if (name.FullName.StartsWith("System")) continue;
try
{
var asm = Assembly.Load(name);
GetDependencies(asm, ref existing);
}
catch { }
catch
{
}
}
if (!existing.Contains(assembly.FullName))
{
Console.WriteLine(assembly.FullName);
existing.Add(assembly.FullName);
}
}
public static Version GetLatestVersion(string branch = "dev-nightly")
{
var url = $"https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/{branch}/RageCoop.Server/Properties/AssemblyInfo.cs";
var versionLine = HttpHelper.DownloadString(url).Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries).Where(x => x.Contains("[assembly: AssemblyVersion(")).First();
var url =
$"https://raw.githubusercontent.com/RAGECOOP/RAGECOOP-V/{branch}/RageCoop.Server/Properties/AssemblyInfo.cs";
var versionLine = HttpHelper.DownloadString(url)
.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Where(x => x.Contains("[assembly: AssemblyVersion(")).First();
var start = versionLine.IndexOf('\"') + 1;
var end = versionLine.LastIndexOf('\"');
return Version.Parse(versionLine.Substring(start, end - start));
}
public static bool CanBeIgnored(this string name)
{
return ToIgnore.Contains(Path.GetFileNameWithoutExtension(name));
}
public static string ToFullPath(this string path)
{
return Path.GetFullPath(path);
}
public static void GetBytesFromObject(object obj, NetOutgoingMessage m)
{
switch (obj)
{
case byte value:
m.Write((byte)0x01); m.Write(value); break;
m.Write((byte)0x01);
m.Write(value);
break;
case short value:
m.Write((byte)0x02); m.Write(value); break;
m.Write((byte)0x02);
m.Write(value);
break;
case ushort value:
m.Write((byte)0x03); m.Write(value); break;
m.Write((byte)0x03);
m.Write(value);
break;
case int value:
m.Write((byte)0x04); m.Write(value); break;
m.Write((byte)0x04);
m.Write(value);
break;
case uint value:
m.Write((byte)0x05); m.Write(value); break;
m.Write((byte)0x05);
m.Write(value);
break;
case long value:
m.Write((byte)0x06); m.Write(value); break;
m.Write((byte)0x06);
m.Write(value);
break;
case ulong value:
m.Write((byte)0x07); m.Write(value); break;
m.Write((byte)0x07);
m.Write(value);
break;
case float value:
m.Write((byte)0x08); m.Write(value); break;
m.Write((byte)0x08);
m.Write(value);
break;
case bool value:
m.Write((byte)0x09); m.Write(value); break;
m.Write((byte)0x09);
m.Write(value);
break;
case string value:
m.Write((byte)0x10); m.Write(value); break;
m.Write((byte)0x10);
m.Write(value);
break;
case Vector3 value:
m.Write((byte)0x11); m.Write(value); break;
m.Write((byte)0x11);
m.Write(value);
break;
case Quaternion value:
m.Write((byte)0x12); m.Write(value); break;
case GTA.Model value:
m.Write((byte)0x13); m.Write(value); break;
m.Write((byte)0x12);
m.Write(value);
break;
case Model value:
m.Write((byte)0x13);
m.Write(value);
break;
case Vector2 value:
m.Write((byte)0x14); m.Write(value); break;
m.Write((byte)0x14);
m.Write(value);
break;
case byte[] value:
m.Write((byte)0x15); m.WriteByteArray(value); break;
m.Write((byte)0x15);
m.WriteByteArray(value);
break;
case Tuple<byte, byte[]> value:
m.Write(value.Item1); m.Write(value.Item2); break;
m.Write(value.Item1);
m.Write(value.Item2);
break;
default:
throw new Exception("Unsupported object type: " + obj.GetType());
}
}
public static IPEndPoint StringToEndPoint(string endpointstring)
{
return StringToEndPoint(endpointstring, -1);
}
public static IPEndPoint StringToEndPoint(string endpointstring, int defaultport)
{
if (string.IsNullOrEmpty(endpointstring)
|| endpointstring.Trim().Length == 0)
{
throw new ArgumentException("Endpoint descriptor may not be empty.");
}
if (defaultport != -1 &&
(defaultport < IPEndPoint.MinPort
|| defaultport > IPEndPoint.MaxPort))
{
|| defaultport > IPEndPoint.MaxPort))
throw new ArgumentException(string.Format("Invalid default port '{0}'", defaultport));
}
string[] values = endpointstring.Split(new char[] { ':' });
var values = endpointstring.Split(':');
IPAddress ipaddy;
int port = -1;
var port = -1;
//check if we have an IPv6 or ports
if (values.Length <= 2) // ipv4 or hostname
@ -194,7 +246,7 @@ namespace RageCoop.Core
//could [a:b:c]:d
if (values[0].StartsWith("[") && values[values.Length - 2].EndsWith("]"))
{
string ipaddressstring = string.Join(":", values.Take(values.Length - 1).ToArray());
var ipaddressstring = string.Join(":", values.Take(values.Length - 1).ToArray());
ipaddy = IPAddress.Parse(ipaddressstring);
port = getPort(values[values.Length - 1]);
}
@ -217,25 +269,24 @@ namespace RageCoop.Core
private static int getPort(string p)
{
if (!int.TryParse(p, out int port)
|| port < IPEndPoint.MinPort
|| port > IPEndPoint.MaxPort)
{
if (!int.TryParse(p, out var port)
|| port < IPEndPoint.MinPort
|| port > IPEndPoint.MaxPort)
throw new FormatException(string.Format("Invalid end point port '{0}'", p));
}
return port;
}
public static IPAddress GetLocalAddress(string target = "8.8.8.8")
{
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
{
socket.Connect(target, 65530);
IPEndPoint endPoint = socket.LocalEndPoint as IPEndPoint;
var endPoint = socket.LocalEndPoint as IPEndPoint;
return endPoint.Address;
}
}
public static IPAddress GetIPfromHost(string p)
{
var hosts = Dns.GetHostAddresses(p);
@ -245,121 +296,124 @@ namespace RageCoop.Core
return hosts[0];
}
public static IpInfo GetIPInfo()
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
var httpClient = new HttpClient();
HttpResponseMessage response = httpClient.GetAsync("https://ipinfo.io/json").GetAwaiter().GetResult();
var response = httpClient.GetAsync("https://ipinfo.io/json").GetAwaiter().GetResult();
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception($"IPv4 request failed! [{(int)response.StatusCode}/{response.ReasonPhrase}]");
}
string content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return JsonConvert.DeserializeObject<IpInfo>(content);
}
public static void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target)
{
foreach (DirectoryInfo dir in source.GetDirectories())
foreach (var dir in source.GetDirectories())
CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name));
foreach (FileInfo file in source.GetFiles())
foreach (var file in source.GetFiles())
file.CopyTo(Path.Combine(target.FullName, file.Name), true);
}
public static string GetInvariantRID()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return "win-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return "linux-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return "osx-" + RuntimeInformation.OSArchitecture.ToString().ToLower();
}
return "unknown";
}
/// <summary>
/// Get local ip addresses on all network interfaces
/// Get local ip addresses on all network interfaces
/// </summary>
/// <returns></returns>
public static List<IPAddress> GetLocalAddress()
{
var addresses = new List<IPAddress>();
foreach (NetworkInterface netInterface in NetworkInterface.GetAllNetworkInterfaces())
foreach (var netInterface in NetworkInterface.GetAllNetworkInterfaces())
{
IPInterfaceProperties ipProps = netInterface.GetIPProperties();
foreach (UnicastIPAddressInformation addr in ipProps.UnicastAddresses)
{
addresses.Add(addr.Address);
}
var ipProps = netInterface.GetIPProperties();
foreach (var addr in ipProps.UnicastAddresses) addresses.Add(addr.Address);
}
return addresses;
}
public static StreamWriter OpenWriter(string path, FileMode mode = FileMode.Create, FileAccess access = FileAccess.Write, FileShare share = FileShare.ReadWrite)
public static StreamWriter OpenWriter(string path, FileMode mode = FileMode.Create,
FileAccess access = FileAccess.Write, FileShare share = FileShare.ReadWrite)
{
return new StreamWriter(File.Open(path, mode, access, share));
}
}
internal class IpInfo
{
[JsonProperty("ip")]
public string Address { get; set; }
[JsonProperty("ip")] public string Address { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("country")] public string Country { get; set; }
}
internal static class Extensions
{
public static byte[] GetBytes(this string s)
{
return Encoding.UTF8.GetBytes(s);
}
public static string GetString(this byte[] data)
{
return Encoding.UTF8.GetString(data);
}
public static byte[] GetBytes(this Vector3 vec)
{
// 12 bytes
return new List<byte[]>() { BitConverter.GetBytes(vec.X), BitConverter.GetBytes(vec.Y), BitConverter.GetBytes(vec.Z) }.Join(4);
return new List<byte[]>
{ BitConverter.GetBytes(vec.X), BitConverter.GetBytes(vec.Y), BitConverter.GetBytes(vec.Z) }.Join(4);
}
public static byte[] GetBytes(this Vector2 vec)
{
// 8 bytes
return new List<byte[]>() { BitConverter.GetBytes(vec.X), BitConverter.GetBytes(vec.Y) }.Join(4);
return new List<byte[]> { BitConverter.GetBytes(vec.X), BitConverter.GetBytes(vec.Y) }.Join(4);
}
/// <summary>
///
/// </summary>
/// <param name="qua"></param>
/// <returns>An array of bytes with length 16</returns>
public static byte[] GetBytes(this Quaternion qua)
{
// 16 bytes
return new List<byte[]>() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4);
return new List<byte[]>
{
BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z),
BitConverter.GetBytes(qua.W)
}.Join(4);
}
public static T GetPacket<T>(this NetIncomingMessage msg) where T : Packet, new()
{
var p = new T();
p.Deserialize(msg);
return p;
}
public static bool HasPedFlag(this PedDataFlags flags, PedDataFlags flag)
{
return (flags & flag) != 0;
}
public static bool HasProjDataFlag(this ProjectileDataFlags flags, ProjectileDataFlags flag)
{
return (flags & flag) != 0;
@ -369,18 +423,19 @@ namespace RageCoop.Core
{
return (flags & flag) != 0;
}
public static bool HasConfigFlag(this PlayerConfigFlags flags, PlayerConfigFlags flag)
{
return (flags & flag) != 0;
}
public static bool HasEventFlag(this CustomEventFlags flags, CustomEventFlags flag)
{
return (flags & flag) != 0;
}
public static Type GetActualType(this TypeCode code)
{
switch (code)
{
case TypeCode.Boolean:
@ -423,43 +478,41 @@ namespace RageCoop.Core
return typeof(sbyte);
case TypeCode.Single:
return typeof(Single);
return typeof(float);
case TypeCode.String:
return typeof(string);
case TypeCode.UInt16:
return typeof(UInt16);
return typeof(ushort);
case TypeCode.UInt32:
return typeof(UInt32);
return typeof(uint);
case TypeCode.UInt64:
return typeof(UInt64);
return typeof(ulong);
}
return null;
}
public static string DumpWithType(this IEnumerable<object> objects)
{
StringBuilder sb = new StringBuilder();
foreach (var obj in objects)
{
sb.Append(obj.GetType() + ":" + obj.ToString() + "\n");
}
var sb = new StringBuilder();
foreach (var obj in objects) sb.Append(obj.GetType() + ":" + obj + "\n");
return sb.ToString();
}
public static string Dump<T>(this IEnumerable<T> objects)
{
return $"{{{string.Join(",", objects)}}}";
}
public static void ForEach<T>(this IEnumerable<T> objects, Action<T> action)
{
foreach (var obj in objects)
{
action(obj);
}
foreach (var obj in objects) action(obj);
}
public static byte[] ReadToEnd(this Stream stream)
{
if (stream is MemoryStream)
@ -471,22 +524,27 @@ namespace RageCoop.Core
return memoryStream.ToArray();
}
}
public static MemoryStream ToMemStream(this Stream stream)
{
var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
return memoryStream;
}
public static byte[] Join(this List<byte[]> arrays, int lengthPerArray = -1)
{
if (arrays.Count == 1) { return arrays[0]; }
var output = lengthPerArray == -1 ? new byte[arrays.Sum(arr => arr.Length)] : new byte[arrays.Count * lengthPerArray];
int writeIdx = 0;
if (arrays.Count == 1) return arrays[0];
var output = lengthPerArray == -1
? new byte[arrays.Sum(arr => arr.Length)]
: new byte[arrays.Count * lengthPerArray];
var writeIdx = 0;
foreach (var byteArr in arrays)
{
byteArr.CopyTo(output, writeIdx);
writeIdx += byteArr.Length;
}
return output;
}
@ -494,37 +552,38 @@ namespace RageCoop.Core
{
return !type.IsAbstract && type.IsSubclassOf(scriptType);
}
}
/// <summary>
/// Some extension methods provided by RageCoop
/// Some extension methods provided by RageCoop
/// </summary>
public static class PublicExtensions
{
/// <summary>
/// Get a SHA256 hashed byte array of the input string, internally used to hash password at client side.
/// Get a SHA256 hashed byte array of the input string, internally used to hash password at client side.
/// </summary>
/// <param name="inputString"></param>
/// <returns></returns>
public static byte[] GetSHA256Hash(this string inputString)
{
using (HashAlgorithm algorithm = SHA256.Create())
{
return algorithm.ComputeHash(Encoding.UTF8.GetBytes(inputString));
}
}
/// <summary>
/// Convert a byte array to hex-encoded string, internally used to trigger handshake event
/// Convert a byte array to hex-encoded string, internally used to trigger handshake event
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static string ToHexString(this byte[] data)
{
return BitConverter.ToString(data).Replace("-", String.Empty);
return BitConverter.ToString(data).Replace("-", string.Empty);
}
/// <summary>
/// Convert a string to IP address
/// Convert a string to IP address
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
@ -532,6 +591,5 @@ namespace RageCoop.Core
{
return IPAddress.Parse(ip);
}
}
}
}

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<Costura />
<Costura />
</Weavers>

View File

@ -7,7 +7,6 @@ using System.Threading;
namespace RageCoop.Core
{
public enum LogLevel
{
Trace = 0,
@ -18,83 +17,92 @@ namespace RageCoop.Core
}
/// <summary>
///
/// </summary>
public class Logger : MarshalByRefObject, IDisposable
{
public class LogLine
{
internal LogLine() { }
public DateTime TimeStamp;
public LogLevel LogLevel;
public string Message;
}
/// <summary>
/// 0:Trace, 1:Debug, 2:Info, 3:Warning, 4:Error
/// </summary>
public int LogLevel = 0;
/// <summary>
/// Name of this logger
/// </summary>
public string Name = "Logger";
public readonly string DateTimeFormat = "HH:mm:ss";
/// <summary>
/// Whether to use UTC time for timestamping the log
/// </summary>
public readonly bool UseUtc = false;
public List<StreamWriter> Writers = new List<StreamWriter> { new StreamWriter(Console.OpenStandardOutput()) };
public int FlushInterval = 1000;
public event FlushDelegate OnFlush;
public bool FlushImmediately = false;
public delegate void FlushDelegate(LogLine line, string fomatted);
private readonly Thread LoggerThread;
private bool Stopping = false;
private readonly ConcurrentQueue<LogLine> _queuedLines = new ConcurrentQueue<LogLine>();
public readonly string DateTimeFormat = "HH:mm:ss";
private readonly Thread LoggerThread;
/// <summary>
/// Whether to use UTC time for timestamping the log
/// </summary>
public readonly bool UseUtc = false;
public bool FlushImmediately = false;
public int FlushInterval = 1000;
/// <summary>
/// 0:Trace, 1:Debug, 2:Info, 3:Warning, 4:Error
/// </summary>
public int LogLevel = 0;
/// <summary>
/// Name of this logger
/// </summary>
public string Name = "Logger";
private bool Stopping;
public List<StreamWriter> Writers = new List<StreamWriter> { new StreamWriter(Console.OpenStandardOutput()) };
internal Logger()
{
Name = Process.GetCurrentProcess().Id.ToString();
if (!FlushImmediately)
{
LoggerThread = new Thread(() =>
{
while (!Stopping)
{
Flush();
Thread.Sleep(1000);
}
Flush();
});
{
while (!Stopping)
{
Flush();
Thread.Sleep(1000);
}
Flush();
});
LoggerThread.Start();
}
}
/// <summary>
/// Stop backdround thread and flush all pending messages.
/// </summary>
public void Dispose()
{
Stopping = true;
LoggerThread?.Join();
}
public event FlushDelegate OnFlush;
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public void Info(string message)
{
Enqueue(2, message);
}
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public void Warning(string message)
{
Enqueue(3, message);
}
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public void Error(string message)
{
Enqueue(4, message);
}
/// <summary>
///
/// </summary>
/// <param name="message"></param>
/// <param name="error"></param>
@ -102,51 +110,50 @@ namespace RageCoop.Core
{
Enqueue(4, $"{message}:\n {error}");
}
/// <summary>
///
/// </summary>
/// <param name="ex"></param>
public void Error(Exception ex)
{
Enqueue(4, ex.ToString());
}
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public void Debug(string message)
{
Enqueue(1, message);
}
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public void Trace(string message)
{
Enqueue(0, message);
}
public void Enqueue(int level, string message)
{
if (level < LogLevel) { return; }
_queuedLines.Enqueue(new LogLine()
if (level < LogLevel) return;
_queuedLines.Enqueue(new LogLine
{
Message = message,
TimeStamp = UseUtc ? DateTime.UtcNow : DateTime.Now,
LogLevel = (LogLevel)level
});
if (FlushImmediately)
{
Flush();
}
if (FlushImmediately) Flush();
}
private string Format(LogLine line)
{
return string.Format("[{0}][{2}] [{3}] {1}", line.TimeStamp.ToString(DateTimeFormat), line.Message, Name, line.LogLevel.ToString());
return string.Format("[{0}][{2}] [{3}] {1}", line.TimeStamp.ToString(DateTimeFormat), line.Message, Name,
line.LogLevel.ToString());
}
/// <summary>
///
/// </summary>
public void Flush()
{
@ -157,20 +164,29 @@ namespace RageCoop.Core
while (_queuedLines.TryDequeue(out var line))
{
var formatted = Format(line);
Writers.ForEach(x => { x.WriteLine(formatted); x.Flush(); });
Writers.ForEach(x =>
{
x.WriteLine(formatted);
x.Flush();
});
OnFlush?.Invoke(line, formatted);
}
}
catch { }
catch
{
}
}
}
/// <summary>
/// Stop backdround thread and flush all pending messages.
/// </summary>
public void Dispose()
public class LogLine
{
Stopping = true;
LoggerThread?.Join();
public LogLevel LogLevel;
public string Message;
public DateTime TimeStamp;
internal LogLine()
{
}
}
}
}
}

View File

@ -1,5 +1,5 @@
using GTA.Math;
using System;
using System;
using GTA.Math;
namespace RageCoop.Core
{
@ -10,9 +10,9 @@ namespace RageCoop.Core
public static Vector3 ToDirection(this Vector3 rotation)
{
double z = DegToRad(rotation.Z);
double x = DegToRad(rotation.X);
double num = Math.Abs(Math.Cos(x));
var z = DegToRad(rotation.Z);
var x = DegToRad(rotation.X);
var num = Math.Abs(Math.Cos(x));
return new Vector3
{
@ -23,11 +23,10 @@ namespace RageCoop.Core
}
/// <summary>
///
/// </summary>
public static Vector3 ToVector(this Quaternion vec)
{
return new Vector3()
return new Vector3
{
X = vec.X,
Y = vec.Y,
@ -36,11 +35,10 @@ namespace RageCoop.Core
}
/// <summary>
///
/// </summary>
public static Quaternion ToQuaternion(this Vector3 vec, float vW = 0.0f)
{
return new Quaternion()
return new Quaternion
{
X = vec.X,
Y = vec.Y,
@ -61,35 +59,35 @@ namespace RageCoop.Core
public static Vector3 ToRadians(this Vector3 i)
{
return new Vector3()
return new Vector3
{
X = ToRadians(i.X),
Y = ToRadians(i.Y),
Z = ToRadians(i.Z),
Z = ToRadians(i.Z)
};
}
public static Quaternion ToQuaternion(this Vector3 vect)
{
vect = new Vector3()
vect = new Vector3
{
X = vect.X.Denormalize() * -1,
Y = vect.Y.Denormalize() - 180f,
Z = vect.Z.Denormalize() - 180f,
Z = vect.Z.Denormalize() - 180f
};
vect = vect.ToRadians();
float rollOver2 = vect.Z * 0.5f;
float sinRollOver2 = (float)Math.Sin(rollOver2);
float cosRollOver2 = (float)Math.Cos(rollOver2);
float pitchOver2 = vect.Y * 0.5f;
float sinPitchOver2 = (float)Math.Sin(pitchOver2);
float cosPitchOver2 = (float)Math.Cos(pitchOver2);
float yawOver2 = vect.X * 0.5f; // pitch
float sinYawOver2 = (float)Math.Sin(yawOver2);
float cosYawOver2 = (float)Math.Cos(yawOver2);
Quaternion result = new Quaternion()
var rollOver2 = vect.Z * 0.5f;
var sinRollOver2 = (float)Math.Sin(rollOver2);
var cosRollOver2 = (float)Math.Cos(rollOver2);
var pitchOver2 = vect.Y * 0.5f;
var sinPitchOver2 = (float)Math.Sin(pitchOver2);
var cosPitchOver2 = (float)Math.Cos(pitchOver2);
var yawOver2 = vect.X * 0.5f; // pitch
var sinYawOver2 = (float)Math.Sin(yawOver2);
var cosYawOver2 = (float)Math.Cos(yawOver2);
var result = new Quaternion
{
X = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2,
Y = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2,
@ -98,27 +96,31 @@ namespace RageCoop.Core
};
return result;
}
public static double DegToRad(double deg)
{
return deg * Math.PI / 180.0;
}
public static Vector3 ToEulerRotation(this Vector3 dir, Vector3 up)
{
var rot = Quaternion.LookRotation(dir.Normalized, up).ToEulerAngles().ToDegree();
return rot;
}
public static Vector3 ToDegree(this Vector3 radian)
{
return radian * (float)(180 / Math.PI);
}
public static Vector3 ToEulerDegrees(this Quaternion q)
{
return q.ToEulerAngles().ToDegree();
}
public static Vector3 ToEulerAngles(this Quaternion q)
{
Vector3 angles = new Vector3();
var angles = new Vector3();
// roll / x
double sinr_cosp = 2 * (q.W * q.X + q.Y * q.Z);
@ -128,13 +130,9 @@ namespace RageCoop.Core
// pitch / y
double sinp = 2 * (q.W * q.Y - q.Z * q.X);
if (Math.Abs(sinp) >= 1)
{
angles.Y = CopySign(Math.PI / 2, sinp);
}
else
{
angles.Y = (float)Math.Asin(sinp);
}
// yaw / z
double siny_cosp = 2 * (q.W * q.Z + q.X * q.Y);
@ -143,23 +141,22 @@ namespace RageCoop.Core
return angles;
}
private static float CopySign(double x, double y)
{
if (y >= 0)
{
return x >= 0 ? (float)x : (float)-x;
}
if (y >= 0) return x >= 0 ? (float)x : (float)-x;
return x >= 0 ? (float)-x : (float)x;
}
public static double AngelTo(this Vector3 v1, Vector3 v2)
{
return Math.Acos(v1.GetCosTheta(v2));
}
public static float GetCosTheta(this Vector3 v1, Vector3 v2)
{
return Vector3.Dot(v1, v2) / (v1.Length() * v2.Length());
}
}
}
}

View File

@ -1,35 +1,33 @@
using Lidgren.Network;
using System;
using System;
using System.Collections.Generic;
using System.Threading;
using Lidgren.Network;
namespace RageCoop.Core
{
internal class CoopPeer : NetPeer, IDisposable
{
public EventHandler<NetIncomingMessage> OnMessageReceived;
private readonly Thread ListenerThread;
private bool _stopping = false;
private bool _stopping;
public EventHandler<NetIncomingMessage> OnMessageReceived;
public CoopPeer(NetPeerConfiguration config) : base(config)
{
Start();
NetIncomingMessage msg;
ListenerThread = new Thread(() =>
{
while (!_stopping)
{
msg = WaitMessage(200);
if (msg != null)
{
OnMessageReceived?.Invoke(this, msg);
}
}
});
{
while (!_stopping)
{
msg = WaitMessage(200);
if (msg != null) OnMessageReceived?.Invoke(this, msg);
}
});
ListenerThread.Start();
}
/// <summary>
/// Terminate all connections and background thread
/// Terminate all connections and background thread
/// </summary>
public void Dispose()
{
@ -37,24 +35,30 @@ namespace RageCoop.Core
Shutdown("Bye!");
ListenerThread.Join();
}
public void SendTo(Packet p, NetConnection connection, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
public void SendTo(Packet p, NetConnection connection, ConnectionChannel channel = ConnectionChannel.Default,
NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = CreateMessage();
var outgoingMessage = CreateMessage();
p.Pack(outgoingMessage);
SendMessage(outgoingMessage, connection, method, (int)channel);
}
public void SendTo(Packet p, IList<NetConnection> connections, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = CreateMessage();
public void SendTo(Packet p, IList<NetConnection> connections,
ConnectionChannel channel = ConnectionChannel.Default,
NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
var outgoingMessage = CreateMessage();
p.Pack(outgoingMessage);
SendMessage(outgoingMessage, connections, method, (int)channel);
}
public void Send(Packet p, IList<NetConnection> cons, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
public void Send(Packet p, IList<NetConnection> cons, ConnectionChannel channel = ConnectionChannel.Default,
NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = CreateMessage();
var outgoingMessage = CreateMessage();
p.Pack(outgoingMessage);
SendMessage(outgoingMessage, cons, method, (int)channel);
}
}
}
}

View File

@ -9,9 +9,9 @@ namespace RageCoop.Core
{
public static void DownloadFile(string url, string destination, Action<int> progressCallback = null)
{
if (File.Exists(destination)) { File.Delete(destination); }
AutoResetEvent ae = new AutoResetEvent(false);
WebClient client = new WebClient();
if (File.Exists(destination)) File.Delete(destination);
var ae = new AutoResetEvent(false);
var client = new WebClient();
// TLS only
ServicePointManager.Expect100Continue = true;
@ -19,13 +19,11 @@ namespace RageCoop.Core
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
client.DownloadProgressChanged += (s, e1) => progressCallback?.Invoke(e1.ProgressPercentage);
client.DownloadFileCompleted += (s, e2) =>
{
ae.Set();
};
client.DownloadFileCompleted += (s, e2) => { ae.Set(); };
client.DownloadFileAsync(new Uri(url), destination);
ae.WaitOne();
}
public static string DownloadString(string url)
{
// TLS only
@ -35,8 +33,8 @@ namespace RageCoop.Core
SecurityProtocolType.Tls;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
WebClient client = new WebClient();
var client = new WebClient();
return client.DownloadString(url);
}
}
}
}

View File

@ -4,10 +4,9 @@ namespace RageCoop.Core
{
internal class PublicKey
{
public PublicKey()
{
public byte[] Exponent;
public byte[] Modulus;
}
public static PublicKey FromServerInfo(ServerInfo info)
{
return new PublicKey
@ -16,7 +15,5 @@ namespace RageCoop.Core
Exponent = Convert.FromBase64String(info.publicKeyExponent)
};
}
public byte[] Modulus;
public byte[] Exponent;
}
}
}

View File

@ -3,7 +3,7 @@
namespace RageCoop.Core
{
/// <summary>
/// A json object representing a server's information as annouced to master server.
/// A json object representing a server's information as annouced to master server.
/// </summary>
[Serializable]
public class ServerInfo
@ -30,6 +30,5 @@ namespace RageCoop.Core
public string ztAddress { get; set; }
public string publicKeyModulus { get; set; }
public string publicKeyExponent { get; set; }
}
}
}

View File

@ -5,11 +5,20 @@ using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading;
namespace RageCoop.Core
{
internal class ZeroTierNetwork
{
public List<string> Addresses = new List<string>();
public string Device;
public string ID;
public string Mac;
public string Name;
public string Status;
public string Type;
public ZeroTierNetwork(string line)
{
// <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>
@ -20,80 +29,34 @@ namespace RageCoop.Core
Status = v[3];
Type = v[4];
Device = v[5];
foreach (var i in v[6].Split(','))
{
Addresses.Add(i.Split('/')[0]);
}
foreach (var i in v[6].Split(',')) Addresses.Add(i.Split('/')[0]);
}
public string ID;
public string Name;
public string Mac;
public string Status;
public string Type;
public string Device;
public List<string> Addresses = new List<string>();
}
internal static class ZeroTierHelper
{
private static readonly string _path = "zerotier-cli";
private static readonly string _arg = "";
static ZeroTierHelper()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var batpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "ZeroTier", "One", "zerotier-cli.bat");
var batpath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
"ZeroTier", "One", "zerotier-cli.bat");
_arg = $"/c \"{batpath}\" ";
_path = "cmd.exe";
}
var status = RunCommand("status");
if (!status.StartsWith("200"))
{
throw new Exception("ZeroTier not ready: " + status);
}
}
public static ZeroTierNetwork Join(string networkId, int timeout = 10000)
{
var p = Run("join " + networkId);
var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 join OK"))
{
throw new Exception(o + p.StandardError.ReadToEnd());
}
if (timeout == 0) { return Networks[networkId]; }
int i = 0;
while (i <= timeout)
{
i += 100;
if (Networks.TryGetValue(networkId, out var n))
{
if (n.Addresses.Count != 0 && (!n.Addresses.Where(x => x == "-").Any()))
{
return n;
}
System.Threading.Thread.Sleep(100);
}
else
{
break;
}
}
return null;
}
public static void Leave(string networkId)
{
var p = Run("leave " + networkId);
var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 leave OK"))
{
throw new Exception(o + p.StandardError.ReadToEnd());
}
if (!status.StartsWith("200")) throw new Exception("ZeroTier not ready: " + status);
}
public static Dictionary<string, ZeroTierNetwork> Networks
{
get
{
Dictionary<string, ZeroTierNetwork> networks = new Dictionary<string, ZeroTierNetwork>();
var networks = new Dictionary<string, ZeroTierNetwork>();
var p = Run("listnetworks");
var lines = Regex.Split(p.StandardOutput.ReadToEnd(), "\n").Skip(1);
@ -106,33 +69,67 @@ namespace RageCoop.Core
networks.Add(n.ID, n);
}
}
return networks;
}
}
public static ZeroTierNetwork Join(string networkId, int timeout = 10000)
{
var p = Run("join " + networkId);
var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 join OK")) throw new Exception(o + p.StandardError.ReadToEnd());
if (timeout == 0) return Networks[networkId];
var i = 0;
while (i <= timeout)
{
i += 100;
if (Networks.TryGetValue(networkId, out var n))
{
if (n.Addresses.Count != 0 && !n.Addresses.Where(x => x == "-").Any()) return n;
Thread.Sleep(100);
}
else
{
break;
}
}
return null;
}
public static void Leave(string networkId)
{
var p = Run("leave " + networkId);
var o = p.StandardOutput.ReadToEnd();
if (!o.StartsWith("200 leave OK")) throw new Exception(o + p.StandardError.ReadToEnd());
}
private static Process Run(string args)
{
var p = new Process();
p.StartInfo = new ProcessStartInfo()
p.StartInfo = new ProcessStartInfo
{
FileName = _path,
Arguments = _arg + args,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
CreateNoWindow = true
};
p.Start();
p.WaitForExit();
return p;
}
private static string RunCommand(string command)
{
var p = Run(command);
return p.StandardOutput.ReadToEnd() + p.StandardError.ReadToEnd();
}
public static void Check()
{
}
}
}
}

View File

@ -5,24 +5,32 @@ namespace RageCoop.Core
{
internal static class PacketExtensions
{
internal static bool IsSyncEvent(this PacketType p)
{
return 30 <= (byte)p && (byte)p <= 40;
}
#region MESSAGE-READ
public static Vector3 ReadVector3(this NetIncomingMessage m)
{
return new Vector3
{
X = m.ReadFloat(),
Y = m.ReadFloat(),
Z = m.ReadFloat(),
Z = m.ReadFloat()
};
}
public static Vector2 ReadVector2(this NetIncomingMessage m)
{
return new Vector2
{
X = m.ReadFloat(),
Y = m.ReadFloat(),
Y = m.ReadFloat()
};
}
public static Quaternion ReadQuaternion(this NetIncomingMessage m)
{
return new Quaternion
@ -30,22 +38,26 @@ namespace RageCoop.Core
X = m.ReadFloat(),
Y = m.ReadFloat(),
Z = m.ReadFloat(),
W = m.ReadFloat(),
W = m.ReadFloat()
};
}
public static byte[] ReadByteArray(this NetIncomingMessage m)
{
return m.ReadBytes(m.ReadInt32());
}
#endregion
#region MESSAGE-WRITE
public static void Write(this NetOutgoingMessage m, Vector3 v)
{
m.Write(v.X);
m.Write(v.Y);
m.Write(v.Z);
}
public static void Write(this NetOutgoingMessage m, Quaternion q)
{
m.Write(q.X);
@ -53,16 +65,13 @@ namespace RageCoop.Core
m.Write(q.Z);
m.Write(q.W);
}
public static void WriteByteArray(this NetOutgoingMessage m, byte[] b)
{
m.Write(b.Length);
m.Write(b);
}
#endregion
internal static bool IsSyncEvent(this PacketType p)
{
return (30 <= (byte)p) && ((byte)p <= 40);
}
#endregion
}
}
}

View File

@ -1,56 +1,51 @@
using Lidgren.Network;
using System;
using System;
using Lidgren.Network;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class ChatMessage : Packet
{
public override PacketType Type => PacketType.ChatMessage;
private readonly Func<string, byte[]> crypt;
private readonly Func<byte[], byte[]> decrypt;
public ChatMessage(Func<string, byte[]> crypter)
{
crypt = crypter;
}
public ChatMessage(Func<byte[], byte[]> decrypter)
{
decrypt = decrypter;
}
public override PacketType Type => PacketType.ChatMessage;
public string Username { get; set; }
public string Message { get; set; }
protected override void Serialize(NetOutgoingMessage m)
{
// Write Username
m.Write(Username);
// Write Message
m.WriteByteArray(crypt(Message));
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
// Read username
Username = m.ReadString();
Message = decrypt(m.ReadByteArray()).GetString();
#endregion
}
}
}
}
}

View File

@ -1,20 +1,23 @@
using Lidgren.Network;
using System;
using GTA;
using Lidgren.Network;
using RageCoop.Core.Scripting;
using System;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class CustomEvent : Packet
{
public static Func<byte, NetIncomingMessage, object> ResolveHandle = null;
public CustomEventFlags Flags;
public override PacketType Type => PacketType.CustomEvent;
public CustomEvent(CustomEventFlags flags = CustomEventFlags.None)
{
Flags = flags;
}
public override PacketType Type => PacketType.CustomEvent;
public int Hash { get; set; }
public object[] Args { get; set; }
@ -24,65 +27,75 @@ namespace RageCoop.Core
m.Write((byte)Flags);
m.Write(Hash);
m.Write(Args.Length);
foreach (var arg in Args)
{
CoreUtils.GetBytesFromObject(arg, m);
}
foreach (var arg in Args) CoreUtils.GetBytesFromObject(arg, m);
}
public override void Deserialize(NetIncomingMessage m)
{
Flags = (CustomEventFlags)m.ReadByte();
Hash = m.ReadInt32();
Args = new object[m.ReadInt32()];
for (int i = 0; i < Args.Length; i++)
for (var i = 0; i < Args.Length; i++)
{
byte type = m.ReadByte();
var type = m.ReadByte();
switch (type)
{
case 0x01:
Args[i] = m.ReadByte(); break;
Args[i] = m.ReadByte();
break;
case 0x02:
Args[i] = m.ReadInt32(); break;
Args[i] = m.ReadInt32();
break;
case 0x03:
Args[i] = m.ReadUInt16(); break;
Args[i] = m.ReadUInt16();
break;
case 0x04:
Args[i] = m.ReadInt32(); break;
Args[i] = m.ReadInt32();
break;
case 0x05:
Args[i] = m.ReadUInt32(); break;
Args[i] = m.ReadUInt32();
break;
case 0x06:
Args[i] = m.ReadInt64(); break;
Args[i] = m.ReadInt64();
break;
case 0x07:
Args[i] = m.ReadUInt64(); break;
Args[i] = m.ReadUInt64();
break;
case 0x08:
Args[i] = m.ReadFloat(); break;
Args[i] = m.ReadFloat();
break;
case 0x09:
Args[i] = m.ReadBoolean(); break;
Args[i] = m.ReadBoolean();
break;
case 0x10:
Args[i] = m.ReadString(); break;
Args[i] = m.ReadString();
break;
case 0x11:
Args[i] = m.ReadVector3(); break;
Args[i] = m.ReadVector3();
break;
case 0x12:
Args[i] = m.ReadQuaternion(); break;
Args[i] = m.ReadQuaternion();
break;
case 0x13:
Args[i] = (GTA.Model)m.ReadInt32(); break;
Args[i] = (Model)m.ReadInt32();
break;
case 0x14:
Args[i] = m.ReadVector2(); break;
Args[i] = m.ReadVector2();
break;
case 0x15:
Args[i] = m.ReadByteArray(); break;
Args[i] = m.ReadByteArray();
break;
default:
if (ResolveHandle == null)
{
throw new InvalidOperationException($"Unexpected type: {type}");
}
else
{
Args[i] = ResolveHandle(type, m); break;
}
Args[i] = ResolveHandle(type, m);
break;
}
}
}
}
}
}
}

View File

@ -1,5 +1,4 @@

using Lidgren.Network;
using Lidgren.Network;
namespace RageCoop.Core
{
@ -9,8 +8,9 @@ namespace RageCoop.Core
AlreadyExists = 1,
Completed = 2,
Loaded = 3,
LoadFailed = 4,
LoadFailed = 4
}
internal partial class Packets
{
internal class FileTransferRequest : Packet
@ -24,9 +24,6 @@ namespace RageCoop.Core
protected override void Serialize(NetOutgoingMessage m)
{
// The ID from the download
m.Write(ID);
@ -36,13 +33,10 @@ namespace RageCoop.Core
// The length of the file
m.Write(FileLength);
}
public override void Deserialize(NetIncomingMessage m)
{
ID = m.ReadInt32();
Name = m.ReadString();
FileLength = m.ReadInt64();
@ -54,19 +48,17 @@ namespace RageCoop.Core
public override PacketType Type => PacketType.FileTransferResponse;
public int ID { get; set; }
public FileResponse Response { get; set; }
protected override void Serialize(NetOutgoingMessage m)
{
// The ID from the download
m.Write(ID);
m.Write((byte)Response);
}
public override void Deserialize(NetIncomingMessage m)
{
ID = m.ReadInt32();
Response = (FileResponse)m.ReadByte();
}
@ -81,17 +73,13 @@ namespace RageCoop.Core
protected override void Serialize(NetOutgoingMessage m)
{
// The ID from the download
m.Write(ID);
m.WriteByteArray(FileChunk);
}
public override void Deserialize(NetIncomingMessage m)
{
ID = m.ReadInt32();
FileChunk = m.ReadByteArray();
}
@ -104,24 +92,19 @@ namespace RageCoop.Core
protected override void Serialize(NetOutgoingMessage m)
{
// The ID for the download
m.Write(ID);
}
public override void Deserialize(NetIncomingMessage m)
{
ID = m.ReadInt32();
}
}
internal class AllResourcesSent : Packet
{
public override PacketType Type => PacketType.AllResourcesSent;
}
}
}
}

View File

@ -4,7 +4,6 @@ namespace RageCoop.Core
{
internal partial class Packets
{
internal class HolePunchInit : Packet
{
public override PacketType Type => PacketType.HolePunchInit;
@ -12,16 +11,13 @@ namespace RageCoop.Core
public string TargetInternal { get; set; }
public string TargetExternal { get; set; }
public bool Connect { get; set; }
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(TargetID);
m.Write(TargetInternal);
m.Write(TargetExternal);
m.Write(Connect);
}
public override void Deserialize(NetIncomingMessage m)
@ -32,26 +28,25 @@ namespace RageCoop.Core
TargetInternal = m.ReadString();
TargetExternal = m.ReadString();
Connect = m.ReadBoolean();
#endregion
}
}
internal class HolePunch : Packet
{
public override PacketType Type => PacketType.HolePunch;
public int Puncher { get; set; }
/// <summary>
/// 1:initial, 2:acknowledged, 3:confirmed
/// 1:initial, 2:acknowledged, 3:confirmed
/// </summary>
public byte Status { get; set; }
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(Puncher);
m.Write(Status);
}
public override void Deserialize(NetIncomingMessage m)
@ -60,8 +55,9 @@ namespace RageCoop.Core
Puncher = m.ReadInt32();
Status = m.ReadByte();
#endregion
}
}
}
}
}

View File

@ -1,48 +1,49 @@
using Lidgren.Network;
using System.Collections.Generic;
using System.Collections.Generic;
using Lidgren.Network;
namespace RageCoop.Core
{
internal partial class Packets
{
/// <summary>
/// Request direct connection to another client
/// Request direct connection to another client
/// </summary>
internal class ConnectionRequest : Packet
{
public int TargetID { get; set; }
public override PacketType Type => PacketType.ConnectionRequest;
protected override void Serialize(NetOutgoingMessage m)
{
var data = new List<byte>(10);
m.Write(TargetID);
}
public override void Deserialize(NetIncomingMessage m)
{
TargetID = m.ReadInt32();
}
}
/// <summary>
/// Sent to the host when a direct connection has been established
/// Sent to the host when a direct connection has been established
/// </summary>
internal class P2PConnect : Packet
{
public int ID { get; set; }
public override PacketType Type => PacketType.P2PConnect;
protected override void Serialize(NetOutgoingMessage m)
{
var data = new List<byte>(10);
m.Write(ID);
}
public override void Deserialize(NetIncomingMessage m)
{
ID = m.ReadInt32();
}
}
}
}
}

View File

@ -1,5 +1,5 @@
using Lidgren.Network;
using System;
using System;
using Lidgren.Network;
namespace RageCoop.Core
{
@ -33,9 +33,11 @@ namespace RageCoop.Core
Voice = 22,
#region Sync
PedSync = 23,
VehicleSync = 24,
ProjectileSync = 25,
#endregion
#region EVENT
@ -50,6 +52,7 @@ namespace RageCoop.Core
Unknown = 255
}
internal enum ConnectionChannel
{
Default = 0,
@ -64,7 +67,7 @@ namespace RageCoop.Core
VehicleSync = 9,
PedSync = 10,
ProjectileSync = 11,
SyncEvents = 12,
SyncEvents = 12
}
[Flags]
@ -86,7 +89,7 @@ namespace RageCoop.Core
IsInCoverFacingLeft = 1 << 12,
IsBlindFiring = 1 << 13,
IsInvincible = 1 << 14,
IsFullSync = 1 << 15,
IsFullSync = 1 << 15
}
internal enum ProjectileDataFlags : byte
@ -95,9 +98,11 @@ namespace RageCoop.Core
Exploded = 1 << 0,
IsAttached = 1 << 1,
IsOrgin = 1 << 2,
IsShotByVehicle = 1 << 3,
IsShotByVehicle = 1 << 3
}
#region ===== VEHICLE DATA =====
internal enum VehicleDataFlags : ushort
{
None = 0,
@ -116,7 +121,7 @@ namespace RageCoop.Core
HasRoof = 1 << 12,
IsFullSync = 1 << 13,
IsOnFire = 1 << 14,
Repaired = 1 << 15,
Repaired = 1 << 15
}
internal enum PlayerConfigFlags : byte
@ -135,6 +140,7 @@ namespace RageCoop.Core
public byte LeftHeadLightBroken { get; set; }
public byte RightHeadLightBroken { get; set; }
}
#endregion
internal interface IPacket
@ -147,12 +153,19 @@ namespace RageCoop.Core
internal abstract class Packet : IPacket
{
public abstract PacketType Type { get; }
public virtual void Deserialize(NetIncomingMessage m)
{
}
public void Pack(NetOutgoingMessage m)
{
m.Write((byte)Type);
Serialize(m);
}
protected virtual void Serialize(NetOutgoingMessage m) { }
public virtual void Deserialize(NetIncomingMessage m) { }
protected virtual void Serialize(NetOutgoingMessage m)
{
}
}
}
}

View File

@ -1,14 +1,12 @@
using GTA;
using System.Collections.Generic;
using GTA;
using GTA.Math;
using Lidgren.Network;
using System.Collections.Generic;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class PedSync : Packet
{
public override PacketType Type => PacketType.PedSync;
@ -28,13 +26,6 @@ namespace RageCoop.Core
public Vector3 Velocity { get; set; }
#region RAGDOLL
public Vector3 HeadPosition { get; set; }
public Vector3 RightFootPosition { get; set; }
public Vector3 LeftFootPosition { get; set; }
#endregion
public byte Speed { get; set; }
public Vector3 AimCoords { get; set; }
@ -42,27 +33,8 @@ namespace RageCoop.Core
public float Heading { get; set; }
#region FULL
public int ModelHash { get; set; }
public uint CurrentWeaponHash { get; set; }
public byte[] Clothes { get; set; }
public Dictionary<uint, bool> WeaponComponents { get; set; }
public byte WeaponTint { get; set; }
public BlipColor BlipColor { get; set; } = (BlipColor)255;
public BlipSprite BlipSprite { get; set; } = 0;
public float BlipScale { get; set; } = 1;
#endregion
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(ID);
m.Write(OwnerID);
m.Write((ushort)Flags);
@ -73,7 +45,6 @@ namespace RageCoop.Core
m.Write(HeadPosition);
m.Write(RightFootPosition);
m.Write(LeftFootPosition);
}
else
{
@ -82,16 +53,15 @@ namespace RageCoop.Core
m.Write(VehicleID);
m.Write((byte)(Seat + 3));
}
m.Write(Position);
}
m.Write(Rotation);
m.Write(Velocity);
if (Flags.HasPedFlag(PedDataFlags.IsAiming))
{
m.Write(AimCoords);
}
if (Flags.HasPedFlag(PedDataFlags.IsAiming)) m.Write(AimCoords);
m.Write(Heading);
@ -104,7 +74,7 @@ namespace RageCoop.Core
{
m.Write(true);
m.Write((ushort)WeaponComponents.Count);
foreach (KeyValuePair<uint, bool> component in WeaponComponents)
foreach (var component in WeaponComponents)
{
m.Write(component.Key);
m.Write(component.Value);
@ -125,15 +95,12 @@ namespace RageCoop.Core
m.Write(BlipScale);
}
}
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
ID = m.ReadInt32();
OwnerID = m.ReadInt32();
Flags = (PedDataFlags)m.ReadUInt16();
@ -164,10 +131,8 @@ namespace RageCoop.Core
Velocity = m.ReadVector3();
if (Flags.HasPedFlag(PedDataFlags.IsAiming))
{
// Read player aim coords
AimCoords = m.ReadVector3();
}
Heading = m.ReadFloat();
@ -186,12 +151,10 @@ namespace RageCoop.Core
if (m.ReadBoolean())
{
WeaponComponents = new Dictionary<uint, bool>();
ushort comCount = m.ReadUInt16();
for (ushort i = 0; i < comCount; i++)
{
WeaponComponents.Add(m.ReadUInt32(), m.ReadBoolean());
}
var comCount = m.ReadUInt16();
for (ushort i = 0; i < comCount; i++) WeaponComponents.Add(m.ReadUInt32(), m.ReadBoolean());
}
WeaponTint = m.ReadByte();
BlipColor = (BlipColor)m.ReadByte();
@ -202,12 +165,35 @@ namespace RageCoop.Core
BlipScale = m.ReadFloat();
}
}
#endregion
}
#region RAGDOLL
public Vector3 HeadPosition { get; set; }
public Vector3 RightFootPosition { get; set; }
public Vector3 LeftFootPosition { get; set; }
#endregion
#region FULL
public int ModelHash { get; set; }
public uint CurrentWeaponHash { get; set; }
public byte[] Clothes { get; set; }
public Dictionary<uint, bool> WeaponComponents { get; set; }
public byte WeaponTint { get; set; }
public BlipColor BlipColor { get; set; } = (BlipColor)255;
public BlipSprite BlipSprite { get; set; } = 0;
public float BlipScale { get; set; } = 1;
#endregion
}
}
}
}

View File

@ -1,6 +1,6 @@
using GTA.Math;
using System.Net;
using GTA.Math;
using Lidgren.Network;
using System.Net;
namespace RageCoop.Core
{
@ -11,8 +11,19 @@ namespace RageCoop.Core
public int ID;
public string Username;
}
public class Handshake : Packet
{
/// <summary>
/// The asymetrically crypted Aes IV
/// </summary>
public byte[] AesIVCrypted;
/// <summary>
/// The asymetrically crypted Aes key
/// </summary>
public byte[] AesKeyCrypted;
public override PacketType Type => PacketType.Handshake;
public int PedID { get; set; }
@ -21,24 +32,14 @@ namespace RageCoop.Core
public string ModVersion { get; set; }
/// <summary>
/// The asymetrically crypted Aes key
/// </summary>
public byte[] AesKeyCrypted;
/// <summary>
/// The asymetrically crypted Aes IV
/// </summary>
public byte[] AesIVCrypted;
/// <summary>
/// The password hash with client Aes
/// The password hash with client Aes
/// </summary>
public byte[] PasswordEncrypted { get; set; }
public IPEndPoint InternalEndPoint { get; set; }
protected override void Serialize(NetOutgoingMessage m)
{
// Write Player Ped ID
m.Write(PedID);
@ -59,15 +60,12 @@ namespace RageCoop.Core
// Write PassHash
m.WriteByteArray(PasswordEncrypted);
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
// Read player netHandle
PedID = m.ReadInt32();
@ -85,13 +83,16 @@ namespace RageCoop.Core
PasswordEncrypted = m.ReadByteArray();
#endregion
}
}
public class HandshakeSuccess : Packet
{
public PlayerData[] Players { get; set; }
public override PacketType Type => PacketType.HandshakeSuccess;
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(Players.Length);
@ -101,20 +102,19 @@ namespace RageCoop.Core
m.Write(p.Username);
}
}
public override void Deserialize(NetIncomingMessage m)
{
Players = new PlayerData[m.ReadInt32()];
for (int i = 0; i < Players.Length; i++)
{
Players[i] = new PlayerData()
for (var i = 0; i < Players.Length; i++)
Players[i] = new PlayerData
{
ID = m.ReadInt32(),
Username = m.ReadString(),
Username = m.ReadString()
};
}
}
}
public class PlayerConnect : Packet
{
public override PacketType Type => PacketType.PlayerConnect;
@ -124,7 +124,6 @@ namespace RageCoop.Core
protected override void Serialize(NetOutgoingMessage m)
{
// Write NetHandle
m.Write(PedID);
@ -140,6 +139,7 @@ namespace RageCoop.Core
// Read Username
Username = m.ReadString();
#endregion
}
}
@ -151,9 +151,7 @@ namespace RageCoop.Core
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(PedID);
}
public override void Deserialize(NetIncomingMessage m)
@ -161,24 +159,27 @@ namespace RageCoop.Core
#region NetIncomingMessageToPacket
PedID = m.ReadInt32();
#endregion
}
}
public class PlayerInfoUpdate : Packet
{
public bool IsHost;
public override PacketType Type => PacketType.PlayerInfoUpdate;
/// <summary>
/// Ped ID for this Player
/// Ped ID for this Player
/// </summary>
public int PedID { get; set; }
public string Username { get; set; }
public float Latency { get; set; }
public Vector3 Position { get; set; }
public bool IsHost;
protected override void Serialize(NetOutgoingMessage m)
{
// Write ID
m.Write(PedID);
@ -195,8 +196,6 @@ namespace RageCoop.Core
public override void Deserialize(NetIncomingMessage m)
{
// Read player ID
PedID = m.ReadInt32();
@ -213,23 +212,18 @@ namespace RageCoop.Core
public class PublicKeyResponse : Packet
{
public override PacketType Type => PacketType.PublicKeyResponse;
public byte[] Exponent;
public byte[] Modulus;
public byte[] Exponent;
public override PacketType Type => PacketType.PublicKeyResponse;
protected override void Serialize(NetOutgoingMessage m)
{
m.WriteByteArray(Modulus);
m.WriteByteArray(Exponent);
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
@ -246,4 +240,4 @@ namespace RageCoop.Core
public override PacketType Type => PacketType.PublicKeyRequest;
}
}
}
}

View File

@ -22,12 +22,8 @@ namespace RageCoop.Core
public ProjectileDataFlags Flags { get; set; }
protected override void Serialize(NetOutgoingMessage m)
{
// Write id
m.Write(ID);
@ -46,16 +42,12 @@ namespace RageCoop.Core
// Write velocity
m.Write(Velocity);
m.Write((byte)Flags);
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
// Read id
ID = m.ReadInt32();
@ -79,4 +71,4 @@ namespace RageCoop.Core
}
}
}
}
}

View File

@ -5,7 +5,6 @@ namespace RageCoop.Core
{
internal partial class Packets
{
internal class BulletShot : Packet
{
public override PacketType Type => PacketType.BulletShot;
@ -18,9 +17,6 @@ namespace RageCoop.Core
protected override void Serialize(NetOutgoingMessage m)
{
// Write OwnerID
m.Write(OwnerID);
@ -32,17 +28,12 @@ namespace RageCoop.Core
// Write EndPosition
m.Write(EndPosition);
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
// Read OwnerID
OwnerID = m.ReadInt32();
@ -54,12 +45,9 @@ namespace RageCoop.Core
// Read EndPosition
EndPosition = m.ReadVector3();
#endregion
}
}
}
}
}

View File

@ -1,5 +1,4 @@

using Lidgren.Network;
using Lidgren.Network;
namespace RageCoop.Core
{
@ -14,14 +13,8 @@ namespace RageCoop.Core
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(VehicleID);
m.Write(Hover);
}
public override void Deserialize(NetIncomingMessage m)
@ -34,9 +27,5 @@ namespace RageCoop.Core
#endregion
}
}
}
}
}

View File

@ -1,11 +1,9 @@

using Lidgren.Network;
using Lidgren.Network;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class OwnerChanged : Packet
{
public override PacketType Type => PacketType.OwnerChanged;
@ -23,16 +21,11 @@ namespace RageCoop.Core
{
#region NetIncomingMessageToPacket
ID = m.ReadInt32();
NewOwnerID = m.ReadInt32();
#endregion
}
}
}
}
}

View File

@ -1,11 +1,9 @@

using Lidgren.Network;
using Lidgren.Network;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class PedKilled : Packet
{
public override PacketType Type => PacketType.PedKilled;
@ -13,27 +11,17 @@ namespace RageCoop.Core
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(VictimID);
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
VictimID = m.ReadInt32();
#endregion
}
}
}
}
}

View File

@ -5,7 +5,6 @@ namespace RageCoop.Core
{
internal partial class Packets
{
internal class VehicleBulletShot : Packet
{
public override PacketType Type => PacketType.VehicleBulletShot;
@ -18,35 +17,25 @@ namespace RageCoop.Core
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(OwnerID);
m.Write(Bone);
m.Write(WeaponHash);
m.Write(StartPosition);
m.Write(EndPosition);
}
public override void Deserialize(NetIncomingMessage m)
{
#region NetIncomingMessageToPacket
OwnerID = m.ReadInt32();
Bone = m.ReadUInt16();
WeaponHash = m.ReadUInt32();
StartPosition = m.ReadVector3();
EndPosition = m.ReadVector3();
#endregion
}
}
}
}
}

View File

@ -1,13 +1,12 @@
using GTA;
using System.Collections.Generic;
using GTA;
using GTA.Math;
using Lidgren.Network;
using System.Collections.Generic;
namespace RageCoop.Core
{
internal partial class Packets
{
public class VehicleSync : Packet
{
public override PacketType Type => PacketType.VehicleSync;
@ -31,34 +30,8 @@ namespace RageCoop.Core
public float SteeringAngle { get; set; }
public float DeluxoWingRatio { get; set; } = -1;
#region FULL-SYNC
public int ModelHash { get; set; }
public float EngineHealth { get; set; }
public byte[] Colors { get; set; }
public Dictionary<int, int> Mods { get; set; }
public VehicleDamageModel DamageModel { get; set; }
public byte LandingGear { get; set; }
public byte RoofState { get; set; }
public VehicleLockStatus LockStatus { get; set; }
public int Livery { get; set; } = -1;
public byte RadioStation { get; set; } = 255;
public string LicensePlate { get; set; }
#endregion
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(ID);
m.Write(OwnerID);
m.Write((ushort)Flags);
@ -70,10 +43,7 @@ namespace RageCoop.Core
m.Write(BrakePower);
m.Write(SteeringAngle);
if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering))
{
m.Write(DeluxoWingRatio);
}
if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering)) m.Write(DeluxoWingRatio);
if (Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
{
@ -82,14 +52,9 @@ namespace RageCoop.Core
// Check
if (Flags.HasVehFlag(VehicleDataFlags.IsAircraft))
{
// Write the vehicle landing gear
m.Write(LandingGear);
}
if (Flags.HasVehFlag(VehicleDataFlags.HasRoof))
{
m.Write(RoofState);
}
if (Flags.HasVehFlag(VehicleDataFlags.HasRoof)) m.Write(RoofState);
// Write vehicle colors
m.Write(Colors[0]);
@ -99,7 +64,7 @@ namespace RageCoop.Core
// Write the count of mods
m.Write((short)Mods.Count);
// Loop the dictionary and add the values
foreach (KeyValuePair<int, int> mod in Mods)
foreach (var mod in Mods)
{
// Write the mod value
m.Write(mod.Key);
@ -153,10 +118,7 @@ namespace RageCoop.Core
SteeringAngle = m.ReadFloat();
if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering))
{
DeluxoWingRatio = m.ReadFloat();
}
if (Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering)) DeluxoWingRatio = m.ReadFloat();
if (Flags.HasVehFlag(VehicleDataFlags.IsFullSync))
{
@ -169,36 +131,28 @@ namespace RageCoop.Core
// Check
if (Flags.HasVehFlag(VehicleDataFlags.IsAircraft))
{
// Read vehicle landing gear
LandingGear = m.ReadByte();
}
if (Flags.HasVehFlag(VehicleDataFlags.HasRoof))
{
RoofState = m.ReadByte();
}
if (Flags.HasVehFlag(VehicleDataFlags.HasRoof)) RoofState = m.ReadByte();
// Read vehicle colors
byte vehColor1 = m.ReadByte();
byte vehColor2 = m.ReadByte();
Colors = new byte[] { vehColor1, vehColor2 };
var vehColor1 = m.ReadByte();
var vehColor2 = m.ReadByte();
Colors = new[] { vehColor1, vehColor2 };
// Read vehicle mods
// Create new Dictionary
Mods = new Dictionary<int, int>();
// Read count of mods
short vehModCount = m.ReadInt16();
var vehModCount = m.ReadInt16();
// Loop
for (int i = 0; i < vehModCount; i++)
{
for (var i = 0; i < vehModCount; i++)
// Read the mod value
Mods.Add(m.ReadInt32(), m.ReadInt32());
}
if (m.ReadBoolean())
{
// Read vehicle damage model
DamageModel = new VehicleDamageModel()
DamageModel = new VehicleDamageModel
{
BrokenDoors = m.ReadByte(),
OpenedDoors = m.ReadByte(),
@ -207,7 +161,6 @@ namespace RageCoop.Core
LeftHeadLightBroken = m.ReadByte(),
RightHeadLightBroken = m.ReadByte()
};
}
// Read LockStatus
@ -220,8 +173,34 @@ namespace RageCoop.Core
Livery = m.ReadByte() - 1;
}
#endregion
}
#region FULL-SYNC
public int ModelHash { get; set; }
public float EngineHealth { get; set; }
public byte[] Colors { get; set; }
public Dictionary<int, int> Mods { get; set; }
public VehicleDamageModel DamageModel { get; set; }
public byte LandingGear { get; set; }
public byte RoofState { get; set; }
public VehicleLockStatus LockStatus { get; set; }
public int Livery { get; set; } = -1;
public byte RadioStation { get; set; } = 255;
public string LicensePlate { get; set; }
#endregion
}
}
}
}

View File

@ -10,20 +10,20 @@ namespace RageCoop.Core
public byte[] Buffer { get; set; }
public int Recorded { get; set; }
public override PacketType Type => PacketType.Voice;
protected override void Serialize(NetOutgoingMessage m)
{
m.Write(ID);
m.Write(Buffer);
m.Write(Recorded);
}
public override void Deserialize(NetIncomingMessage m)
{
ID = m.ReadInt32();
Buffer = m.ReadByteArray();
Recorded = m.ReadInt32();
}
}
}
}
}

View File

@ -6,69 +6,73 @@ using System.Text;
namespace RageCoop.Core.Scripting
{
/// <summary>
/// Describes how the event should be sent or processed
/// Describes how the event should be sent or processed
/// </summary>
public enum CustomEventFlags : byte
{
None = 0,
/// <summary>
/// Data will be encrypted and decrypted on target client
/// Data will be encrypted and decrypted on target client
/// </summary>
Encrypted = 1,
/// <summary>
/// Event will be queued and fired in script thread, specify this flag if your handler will call native functions.
/// Event will be queued and fired in script thread, specify this flag if your handler will call native functions.
/// </summary>
Queued = 2,
Queued = 2
}
/// <summary>
/// Struct to identify different event using hash
/// Struct to identify different event using hash
/// </summary>
public struct CustomEventHash
{
private static readonly MD5 Hasher = MD5.Create();
private static readonly Dictionary<int, string> Hashed = new Dictionary<int, string>();
/// <summary>
/// Hash value
/// Hash value
/// </summary>
public int Hash;
/// <summary>
/// Create from hash
/// Create from hash
/// </summary>
/// <param name="hash"></param>
public static implicit operator CustomEventHash(int hash)
{
return new CustomEventHash() { Hash = hash };
return new CustomEventHash { Hash = hash };
}
/// <summary>
/// Create from string
/// Create from string
/// </summary>
/// <param name="name"></param>
public static implicit operator CustomEventHash(string name)
{
return new CustomEventHash() { Hash = FromString(name) };
return new CustomEventHash { Hash = FromString(name) };
}
/// <summary>
/// Get a Int32 hash of a string.
/// Get a Int32 hash of a string.
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
/// <exception cref="ArgumentException">The exception is thrown when the name did not match a previously computed one and the hash was the same.</exception>
/// <exception cref="ArgumentException">
/// The exception is thrown when the name did not match a previously computed one and
/// the hash was the same.
/// </exception>
public static int FromString(string s)
{
var hash = BitConverter.ToInt32(Hasher.ComputeHash(Encoding.UTF8.GetBytes(s)), 0);
lock (Hashed)
{
if (Hashed.TryGetValue(hash, out string name))
if (Hashed.TryGetValue(hash, out var name))
{
if (name != s)
{
throw new ArgumentException($"Hashed value has collision with another name:{name}, hashed value:{hash}");
}
throw new ArgumentException(
$"Hashed value has collision with another name:{name}, hashed value:{hash}");
return hash;
}
@ -77,8 +81,9 @@ namespace RageCoop.Core.Scripting
return hash;
}
}
/// <summary>
/// To int
/// To int
/// </summary>
/// <param name="h"></param>
public static implicit operator int(CustomEventHash h)
@ -86,8 +91,8 @@ namespace RageCoop.Core.Scripting
return h.Hash;
}
}
/// <summary>
///
/// </summary>
public static class CustomEvents
{
@ -110,15 +115,18 @@ namespace RageCoop.Core.Scripting
internal static readonly CustomEventHash CreateVehicle = "RageCoop.CreateVehicle";
internal static readonly CustomEventHash WeatherTimeSync = "RageCoop.WeatherTimeSync";
internal static readonly CustomEventHash IsHost = "RageCoop.IsHost";
/// <summary>
/// Get event hash from string.
/// Get event hash from string.
/// </summary>
/// <returns></returns>
/// <remarks>This method is obsoete, you should use implicit operator or <see cref="CustomEventHash.FromString(string)"/></remarks>
/// <remarks>
/// This method is obsoete, you should use implicit operator or <see cref="CustomEventHash.FromString(string)" />
/// </remarks>
[Obsolete]
public static int Hash(string s)
{
return CustomEventHash.FromString(s);
}
}
}
}

View File

@ -4,21 +4,22 @@ using System.IO;
namespace RageCoop.Core.Scripting
{
/// <summary>
///
/// </summary>
public class ResourceFile
{
/// <summary>
/// Full name with relative path of this file
/// Full name with relative path of this file
/// </summary>
public string Name { get; internal set; }
/// <summary>
/// Whether this is a directory
/// Whether this is a directory
/// </summary>
public bool IsDirectory { get; internal set; }
/// <summary>
/// Get a stream that can be used to read file content.
/// Get a stream that can be used to read file content.
/// </summary>
public Func<Stream> GetStream { get; internal set; }
}
}
}

View File

@ -5,55 +5,70 @@ using System.Threading;
namespace RageCoop.Core
{
/// <summary>
/// A worker that constantly execute jobs in a background thread.
/// A worker that constantly execute jobs in a background thread.
/// </summary>
public class Worker : IDisposable
{
private readonly SemaphoreSlim _semaphoreSlim;
private readonly Thread _workerThread;
private bool _stopping = false;
/// <summary>
/// Name of the worker
/// </summary>
public string Name { get; set; }
/// <summary>
/// Whether this worker is busy executing job(s).
/// </summary>
public bool IsBusy { get; private set; }
internal Worker(string name, Logger logger, int maxJobs = Int32.MaxValue)
private readonly ConcurrentQueue<Action> Jobs = new ConcurrentQueue<Action>();
private bool _stopping;
internal Worker(string name, Logger logger, int maxJobs = int.MaxValue)
{
Name = name;
_semaphoreSlim = new SemaphoreSlim(0, maxJobs);
_workerThread = new Thread(() =>
{
while (!_stopping)
{
IsBusy = false;
_semaphoreSlim.Wait();
if (Jobs.TryDequeue(out var job))
{
IsBusy = true;
try
{
job.Invoke();
}
catch (Exception ex)
{
logger.Error("Error occurred when executing queued job:");
logger.Error(ex);
}
}
else
{
throw new InvalidOperationException("Hmm... that's unexpected.");
}
}
IsBusy = false;
});
{
while (!_stopping)
{
IsBusy = false;
_semaphoreSlim.Wait();
if (Jobs.TryDequeue(out var job))
{
IsBusy = true;
try
{
job.Invoke();
}
catch (Exception ex)
{
logger.Error("Error occurred when executing queued job:");
logger.Error(ex);
}
}
else
{
throw new InvalidOperationException("Hmm... that's unexpected.");
}
}
IsBusy = false;
});
_workerThread.Start();
}
/// <summary>
/// Queue a job to be executed
/// Name of the worker
/// </summary>
public string Name { get; set; }
/// <summary>
/// Whether this worker is busy executing job(s).
/// </summary>
public bool IsBusy { get; private set; }
/// <summary>
/// Finish current job and stop the worker.
/// </summary>
public void Dispose()
{
Stop();
_semaphoreSlim.Dispose();
}
/// <summary>
/// Queue a job to be executed
/// </summary>
/// <param name="work"></param>
public void QueueJob(Action work)
@ -61,26 +76,15 @@ namespace RageCoop.Core
Jobs.Enqueue(work);
_semaphoreSlim.Release();
}
/// <summary>
/// Finish current job and stop the worker.
/// Finish current job and stop the worker.
/// </summary>
public void Stop()
{
_stopping = true;
QueueJob(() => { });
if (_workerThread.IsAlive)
{
_workerThread.Join();
}
if (_workerThread.IsAlive) _workerThread.Join();
}
/// <summary>
/// Finish current job and stop the worker.
/// </summary>
public void Dispose()
{
Stop();
_semaphoreSlim.Dispose();
}
private readonly ConcurrentQueue<Action> Jobs = new ConcurrentQueue<Action>();
}
}
}