Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remake #4

Merged
merged 24 commits into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add Json and converted DataClient to strutc
  • Loading branch information
treviasxk committed Mar 4, 2025
commit e281b5eece62f648a9b814c192b3965582c1d499
1 change: 1 addition & 0 deletions Nethostfire.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
</Reference>
<!-- MySQL -->
<EmbeddedResource Include="plugins/MySqlConnector.dll" />
<EmbeddedResource Include="plugins/Newtonsoft.Json.dll" />
</ItemGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Nethostfire is a library (netstandard2.1) to create UDP server and client in C#,
- Various types of shipping (single, group, all).
- RSA, AES and Base64 encryption both on the server and on the client.
- Automatic decryption.
- Suport JSON convert/deconvert.
- Feature to send UDP bytes without losses.
- Feature to send UDP bytes in enqueued.
- Adapted to manipulate objects in Unity 3D.
Expand Down
Binary file added plugins/Newtonsoft.Json.dll
Binary file not shown.
17 changes: 17 additions & 0 deletions scripts/json/Json.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Nethostfire{
public class Json {
/// <summary>
/// Convert object to JSON.
/// </summary>
public static string Convert(object data){
return System.GetDynamicAssembly("Newtonsoft.Json", "Newtonsoft.Json.JsonConvert", new(){MethodName = "SerializeObject", Params = [data]}) ?? "";
}

/// <summary>
/// Desconvert JSON to object.
/// </summary>
public static T? Deconvert<T>(string json){
return (T?)System.GetDynamicAssembly("Newtonsoft.Json", "Newtonsoft.Json.JsonConvert", new(){MethodName = "DeserializeObject", Params = [json]})?.ToObject<T>();
}
}
}
83 changes: 53 additions & 30 deletions scripts/system/System.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
using System.Text;

namespace Nethostfire {
class DataClient{
struct DataClient{
// Public key RSA to encrypt bytes
public string? PublicKeyRSA;
public string PublicKeyRSA;
// Private key AES to encrypt bytes
public byte[]? PrivateKeyAES;
public byte[] PrivateKeyAES;
// Timer to check if client is connected
public long LastTimer;
// Timer to check packets interval
Expand All @@ -28,18 +28,22 @@ class DataClient{
public int Ping;
// IndexID to send bytes
public int IndexID;
// Limit max receive pps
public int LimitMaxPPS;
// List to check packets duplications
public HashSet<int> ListIndex = new();
public HashSet<int> ListIndex;
// List shippiments without packet loss
public readonly ConcurrentDictionary<int, byte[]> ListHoldConnection = new();
public ConcurrentDictionary<int, byte[]> ListHoldConnection;
// List shippiments without packet loss queued
public readonly ConcurrentDictionary<int, byte[]> QueuingHoldConnection = new();
// Limit max receive pps
public int LimitMaxPPS;
public ConcurrentDictionary<int, byte[]> QueuingHoldConnection;
// Limit max receive pps for GroupID
public readonly ConcurrentDictionary<int, int> LimitMaxPPSGroupID = new();
public ConcurrentDictionary<int, int> LimitMaxPPSGroupID;
}
struct MethodData {
public string MethodName;
public object[] Params;
public Type Type;
}

public enum ServerStatus{
Stopped = 0,
Stopping = 1,
Expand Down Expand Up @@ -124,13 +128,13 @@ class System{
public static Process Process = Process.GetCurrentProcess();
public static HashSet<UDP.Client> ListClient = new();
public static HashSet<UDP.Server> ListServer = new();

static ConcurrentDictionary<string, Assembly> assemblys = new();
static StreamWriter? fileLog;
public static bool SaveLog = true;

// Transform packets and send.
public static void SendPacket(UdpClient? socket, byte[] bytes, int groupID, DataClient? dataClient, TypeEncrypt typeEncrypt = TypeEncrypt.None, TypeShipping typeShipping = TypeShipping.None, IPEndPoint? ip = null, bool background = false){
if(socket != null && dataClient != null){
public static void SendPacket(UdpClient? socket, byte[] bytes, int groupID, DataClient dataClient, TypeEncrypt typeEncrypt = TypeEncrypt.None, TypeShipping typeShipping = TypeShipping.None, IPEndPoint? ip = null, bool background = false){
if(socket != null){
bytes = BytesToSend(bytes, groupID, typeEncrypt, typeShipping, dataClient, background);
if(bytes.Length > 1)
try{socket?.Send(bytes, bytes.Length, ip);}catch{}
Expand Down Expand Up @@ -197,11 +201,11 @@ public static byte[] BytesToSend(byte[] bytes, int groupID, TypeEncrypt typeEnc
// Hold Connection
if(typeShipping == TypeShipping.WithoutPacketLoss)
dataClient.ListHoldConnection.TryAdd(dataClient.IndexID, _bytes);


// Queuing Hold Connection
if(typeShipping == TypeShipping.WithoutPacketLossEnqueue){
dataClient.QueuingHoldConnection.TryAdd(dataClient.IndexID, _bytes);

if(dataClient.QueuingHoldConnection.Count != 1){
dataClient.IndexID++;
return [];
Expand Down Expand Up @@ -250,7 +254,6 @@ public static (byte[], int, TypeEncrypt, int)? BytesToReceive(UdpClient socket,
// Check if the packet is background
if(_background){
// Decompress AES

if(_groupID == 0)
_bytes = Decompress(_bytes);

Expand Down Expand Up @@ -289,13 +292,13 @@ public static (byte[], int, TypeEncrypt, int)? BytesToReceive(UdpClient socket,
// Hold Connection respond
SendPing(socket, _bytes2, ip);
}
// Check packets duplication
if(!dataClient.ListIndex.Contains(_indexID))
dataClient.ListIndex.Add(_indexID);
else
return null;

// // Check packets duplication
// if(!dataClient.ListIndex.Contains(_indexID))
// dataClient.ListIndex.Add(_indexID);
// else
// return null;

return (_bytes, _groupID, _typeEncrypt, _typeShipping);
}catch{}
return null;
Expand Down Expand Up @@ -430,17 +433,37 @@ public static void StartUnity(UDP.Client? client = null, UDP.Server? server = nu
}catch{}
}

public static dynamic? GetDynamicAssembly(string libName, string typeName){
var assembly = Assembly.GetExecutingAssembly();

public static dynamic? GetDynamicAssembly(string libName, string typeName, MethodData? methodData = null){
dynamic? Dynamic = null;
foreach(var resourceNames in assembly.GetManifestResourceNames().Where(item => item.Contains(libName + ".dll"))){
var stream = assembly.GetManifestResourceStream(resourceNames);
if(stream != null){
byte[] data = new byte[stream.Length];
stream?.Read(data, 0, data.Length);
Dynamic = Assembly.Load(data).CreateInstance(typeName);

if(!assemblys.TryGetValue(libName, out Assembly assembly)){
assembly = Assembly.GetExecutingAssembly();
foreach(var resourceNames in assembly.GetManifestResourceNames().Where(item => item.Contains(libName + ".dll"))){
if(assembly.GetManifestResourceStream(resourceNames) is Stream stream && stream != null){
byte[] data = new byte[stream.Length];
stream?.Read(data, 0, data.Length);
assembly = Assembly.Load(data);
assemblys.TryAdd(libName, assembly);
}
}
}

if(methodData.HasValue && assembly.GetType(typeName) is Type typeStatic && typeStatic != null && typeStatic.IsAbstract && typeStatic.IsSealed){
MethodInfo? metodoGenerico = typeStatic.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(m => m.Name == methodData.Value.MethodName
&& !m.IsGenericMethod // Exclui a versão genérica
&& m.GetParameters().Length == 1)
.FirstOrDefault();

if(metodoGenerico!.IsGenericMethod){
Dynamic = metodoGenerico.MakeGenericMethod(methodData.Value.Type).Invoke(null, methodData.Value.Params);
}else{
Dynamic = metodoGenerico.Invoke(null, methodData.Value.Params);
}
}else
Dynamic = assembly.CreateInstance(typeName);

return Dynamic;
}

Expand Down
4 changes: 2 additions & 2 deletions scripts/udp/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public partial class UDP {
public class Client : IDisposable{
public UdpClient? Socket;
IPEndPoint? IPEndPoint;
DataClient dataServer = new();
DataClient dataServer = new(){ListIndex = new(), ListHoldConnection = new(), QueuingHoldConnection = new(), LimitMaxPPSGroupID = new()};
ClientStatus CurrentClientStatus = ClientStatus.Disconnected;
long connectingTimeoutTmp;

Expand Down Expand Up @@ -89,7 +89,7 @@ public void Disconnect(){
ChangeStatus(ClientStatus.Disconnecting);
Socket.Close();
Socket = null;
dataServer = new();
dataServer = new(){ListIndex = new(), ListHoldConnection = new(), QueuingHoldConnection = new(), LimitMaxPPSGroupID = new()};
if(CurrentClientStatus == ClientStatus.Disconnecting)
ChangeStatus(ClientStatus.Disconnected);
}
Expand Down
43 changes: 21 additions & 22 deletions scripts/udp/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,15 @@ public void Stop(){
/// All received PPS in a IP will be limited.
/// </summary>
public void ChangeLimitMaxPPS(int pps, IPEndPoint ip){
if(DataClients.TryGetValue(ip, out DataClient? dataClient))
if(DataClients.TryGetValue(ip, out DataClient dataClient))
dataClient.LimitMaxPPS = pps;
}

/// <summary>
/// All received PPS of groupID in a IP will be limited.
/// </summary>
public void ChangeLimitMaxPPS(int pps, int groupID, IPEndPoint ip){
if(DataClients.TryGetValue(ip, out DataClient? dataClient))
if(DataClients.TryGetValue(ip, out DataClient dataClient))
if(pps == 0)
dataClient.LimitMaxPPSGroupID.TryRemove(groupID, out _);
else
Expand Down Expand Up @@ -323,7 +323,6 @@ async void ReceivePackage(){
while(Socket != null){
byte[] bytes;
IPEndPoint ip;

// Connection alway fail when ip not found, use 'try' is necessary.
try{
// Receive bytes and ip
Expand Down Expand Up @@ -352,7 +351,7 @@ async void ReceivePackage(){
if(DataClients.TryGetValue(ip, out var dataClient)){
// Update time online
dataClient.LastTimer = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;

DataClients[ip] = dataClient;
// Commands
if(bytes.Length == 1){
switch(bytes[0]){
Expand Down Expand Up @@ -381,48 +380,49 @@ async void ReceivePackage(){
return;
}

// Check if Client in Queuing
if(QueuingClients.TryGetValue(ip, out var _queuingClients))
dataClient = _queuingClients;
else
dataClient = new DataClient(){LastTimer = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond};

// Update LastTimer
dataClient.LastTimer = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;

// Check if Client in Queuing
if(!QueuingClients.TryGetValue(ip, out dataClient))
dataClient = new(){LastTimer = dataClient.LastTimer, ListIndex = new(), ListHoldConnection = new(), QueuingHoldConnection = new(), LimitMaxPPSGroupID = new()};
else{
dataClient.LastTimer = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
QueuingClients[ip] = dataClient;
}

// item1 = bytes
// item2 = groupID
// item3 = typeEncrypt
// item4 = typeShipping
data = BytesToReceive(Socket, bytes, dataClient, ip);
}



// Check RSA client received and send RSA server
if(data.HasValue && data.Value.Item4 == 0 && data.Value.Item2 == 0 && PublicKeyRSA != null){
if(data.HasValue && data.Value.Item4 == 0 && data.Value.Item2 == 0 && PublicKeyRSA != null){
string PublicKeyRSAClient = Encoding.ASCII.GetString(data.Value.Item1);
if(PublicKeyRSAClient.StartsWith("<RSAKeyValue>") && PublicKeyRSAClient.EndsWith("</RSAKeyValue>")){
dataClient.PublicKeyRSA = PublicKeyRSAClient;
if(QueuingClients.TryAdd(ip, dataClient)){
ShowLog(ip + " Connecting...");
}
if(QueuingClients.TryAdd(ip, dataClient))
ShowLog(ip + " Incoming...");

// Send PublicKeyRSA
SendPacket(Socket, Encoding.ASCII.GetBytes(PublicKeyRSA), 0, dataClient, ip: ip, background: true); // groupID: 0 = RSA
}
return;
}


// Check AES client, send AES server and connect client
if(data.HasValue && data.Value.Item4 == 0 && data.Value.Item2 == 1 && PrivateKeyAES != null){
dataClient.PrivateKeyAES = data.Value.Item1;
dataClient.MaxPPSTimer = 0;
if(QueuingClients.TryRemove(ip, out _)){
if(DataClients.TryAdd(ip, dataClient)){
OnConnected?.Invoke(ip);
ShowLog(ip + " Connected!");
}
if(QueuingClients.TryRemove(ip, out _) && DataClients.TryAdd(ip, dataClient)){
OnConnected?.Invoke(ip);
ShowLog(ip + " Connected!");
}

// Send PrivateKeyAES
SendPacket(Socket, PrivateKeyAES, 1, dataClient, ip: ip, background: true); // groupID: 1 = AES
return;
Expand All @@ -434,7 +434,6 @@ async void ReceivePackage(){

void Service(){
while(Socket != null){

// Check timer connection dataClients.
Parallel.ForEach(DataClients.Where(item => item.Value.LastTimer + ConnectedTimeout < DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond), item =>{
OnDisconnected?.Invoke(item.Key);
Expand Down