983 lines
40 KiB
C#
Raw Normal View History

2021-07-07 13:36:25 +02:00
using System;
using System.Text;
using System.Net;
2021-07-07 13:36:25 +02:00
using System.Linq;
using System.Collections.Generic;
using System.Threading;
2021-08-14 21:49:23 +02:00
using System.Reflection;
using System.IO;
2021-12-07 08:18:29 +01:00
using System.Net.Http;
2022-05-22 15:55:26 +08:00
using RageCoop.Core;
2022-04-19 05:33:18 +02:00
using Newtonsoft.Json;
2021-07-07 13:36:25 +02:00
using Lidgren.Network;
2022-06-01 19:05:45 +08:00
using System.Timers;
using System.Security.Cryptography;
2022-06-04 18:09:42 +08:00
using RageCoop.Server.Scripting;
using System.Net.Sockets;
using System.Threading.Tasks;
2021-07-07 13:36:25 +02:00
2022-05-22 15:55:26 +08:00
namespace RageCoop.Server
2021-07-07 13:36:25 +02:00
{
2022-07-01 13:54:18 +08:00
internal struct IpInfo
2022-04-19 05:33:18 +02:00
{
[JsonProperty("ip")]
public string Address { get; set; }
}
2022-07-01 13:54:18 +08:00
/// <summary>
/// The instantiable RageCoop server class
/// </summary>
public class Server
2021-07-07 13:36:25 +02:00
{
2022-07-01 13:54:18 +08:00
/// <summary>
/// The API for controlling server and hooking events.
/// </summary>
2022-06-30 09:28:13 +08:00
public API API { get; private set; }
internal BaseScript BaseScript { get; set; }=new BaseScript();
2022-06-24 18:25:24 +08:00
internal readonly ServerSettings Settings;
internal NetServer MainNetServer;
internal readonly Dictionary<Command, Action<CommandContext>> Commands = new();
internal readonly Dictionary<long,Client> Clients = new();
private Dictionary<int,FileTransfer> InProgressFileTransfers=new();
private Resources Resources;
internal Logger Logger;
2022-06-24 10:33:36 +08:00
private Security Security;
2022-06-30 09:28:13 +08:00
private System.Timers.Timer _sendInfoTimer = new System.Timers.Timer(5000);
private bool _stopping = false;
2022-06-27 15:35:23 +08:00
private Thread _listenerThread;
private Thread _announceThread;
2022-06-30 09:28:13 +08:00
private Worker _worker;
2022-07-01 12:22:31 +08:00
private Dictionary<int,Action<PacketType,byte[]>> PendingResponses=new();
private Dictionary<PacketType, Func<byte[],Packet>> RequestHandlers=new();
2022-06-30 09:28:13 +08:00
private readonly string _compatibleVersion = "V0_5";
2022-07-01 13:54:18 +08:00
/// <summary>
/// Instantiate a server.
/// </summary>
/// <param name="settings"></param>
/// <param name="logger"></param>
/// <exception cref="ArgumentNullException"></exception>
2022-06-24 18:25:24 +08:00
public Server(ServerSettings settings,Logger logger=null)
2021-07-07 13:36:25 +02:00
{
2022-06-24 18:25:24 +08:00
Settings = settings;
if (settings==null) { throw new ArgumentNullException("Server settings cannot be null!"); }
Logger=logger;
2022-06-24 18:25:24 +08:00
if (Logger!=null) { Logger.LogLevel=Settings.LogLevel;}
API=new API(this);
Resources=new Resources(this);
2022-06-24 10:33:36 +08:00
Security=new Security(Logger);
2022-06-27 15:35:23 +08:00
}
2022-07-01 13:54:18 +08:00
/// <summary>
/// Spawn threads and start the server
/// </summary>
2022-06-27 15:35:23 +08:00
public void Start()
{
Logger?.Info("================");
2022-06-24 18:25:24 +08:00
Logger?.Info($"Server bound to: 0.0.0.0:{Settings.Port}");
Logger?.Info($"Server version: {Assembly.GetCallingAssembly().GetName().Version}");
Logger?.Info($"Compatible RAGECOOP versions: {_compatibleVersion.Replace('_', '.')}.x");
Logger?.Info("================");
2021-09-28 16:51:16 +02:00
2022-04-06 02:18:24 +02:00
// 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP
NetPeerConfiguration config = new("623c92c287cc392406e7aaaac1c0f3b0")
2021-07-07 13:36:25 +02:00
{
2022-06-24 18:25:24 +08:00
Port = Settings.Port,
MaximumConnections = Settings.MaxPlayers,
EnableUPnP = false,
AutoFlushSendQueue = true
2021-07-07 13:36:25 +02:00
};
config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
config.EnableMessageType(NetIncomingMessageType.ConnectionLatencyUpdated);
2022-06-24 10:33:36 +08:00
config.EnableMessageType(NetIncomingMessageType.UnconnectedData);
2021-07-07 13:36:25 +02:00
MainNetServer = new NetServer(config);
MainNetServer.Start();
2022-07-01 12:22:31 +08:00
_worker=new Worker("ServerWorker",Logger);
2022-06-30 09:28:13 +08:00
_sendInfoTimer.Elapsed+=(s, e) => { SendPlayerInfos(); };
_sendInfoTimer.AutoReset=true;
_sendInfoTimer.Enabled=true;
Logger?.Info(string.Format("Server listening on {0}:{1}", config.LocalAddress.ToString(), config.Port));
2022-06-24 18:25:24 +08:00
if (Settings.AnnounceSelf)
2021-12-07 08:18:29 +01:00
{
2022-04-19 03:38:48 +02:00
#region -- MASTERSERVER --
2022-06-27 15:35:23 +08:00
_announceThread=new Thread(async () =>
2021-12-07 08:18:29 +01:00
{
2022-04-19 03:38:48 +02:00
try
2021-12-07 08:18:29 +01:00
{
2022-04-19 03:38:48 +02:00
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
2021-12-07 08:18:29 +01:00
2022-04-19 03:38:48 +02:00
HttpClient httpClient = new();
2021-12-07 08:18:29 +01:00
2022-04-19 05:33:18 +02:00
IpInfo info;
try
{
HttpResponseMessage response = await httpClient.GetAsync("https://ipinfo.io/json");
2022-04-19 07:41:39 +02:00
if (response.StatusCode != HttpStatusCode.OK)
2022-04-19 05:33:18 +02:00
{
2022-04-19 07:41:39 +02:00
throw new Exception($"IPv4 request failed! [{(int)response.StatusCode}/{response.ReasonPhrase}]");
2022-04-19 05:33:18 +02:00
}
string content = await response.Content.ReadAsStringAsync();
info = JsonConvert.DeserializeObject<IpInfo>(content);
Logger?.Info($"Your public IP is {info.Address}, announcing to master server...");
2022-04-19 05:33:18 +02:00
}
catch (Exception ex)
{
Logger?.Error(ex.InnerException?.Message ?? ex.Message);
2022-04-19 05:33:18 +02:00
return;
}
2022-06-27 15:35:23 +08:00
var realMaster = Settings.MasterServer=="[AUTO]" ? Util.DownloadString("https://ragecoop.online/stuff/masterserver") : Settings.MasterServer;
while (!_stopping)
2022-04-19 03:38:48 +02:00
{
string msg =
"{ " +
2022-04-19 05:33:18 +02:00
"\"address\": \"" + info.Address + "\", " +
2022-06-24 18:25:24 +08:00
"\"port\": \"" + Settings.Port + "\", " +
"\"name\": \"" + Settings.Name + "\", " +
2022-04-19 03:38:48 +02:00
"\"version\": \"" + _compatibleVersion.Replace("_", ".") + "\", " +
"\"players\": \"" + MainNetServer.ConnectionsCount + "\", " +
2022-06-24 18:25:24 +08:00
"\"maxPlayers\": \"" + Settings.MaxPlayers + "\"" +
2022-04-19 03:38:48 +02:00
" }";
HttpResponseMessage response = null;
2021-12-07 08:18:29 +01:00
try
{
2022-06-03 16:28:02 +08:00
response = await httpClient.PostAsync(realMaster, new StringContent(msg, Encoding.UTF8, "application/json"));
2021-12-07 08:18:29 +01:00
}
2022-04-19 03:38:48 +02:00
catch (Exception ex)
2021-12-07 08:18:29 +01:00
{
Logger?.Error($"MasterServer: {ex.Message}");
2022-04-19 03:38:48 +02:00
// Sleep for 5s
Thread.Sleep(5000);
continue;
2021-12-07 08:18:29 +01:00
}
2022-04-19 03:38:48 +02:00
if (response == null)
2021-12-07 08:18:29 +01:00
{
Logger?.Error("MasterServer: Something went wrong!");
2022-04-19 03:38:48 +02:00
}
else if (response.StatusCode != HttpStatusCode.OK)
{
if (response.StatusCode == HttpStatusCode.BadRequest)
2022-04-14 12:18:07 +02:00
{
2022-04-19 03:38:48 +02:00
string requestContent = await response.Content.ReadAsStringAsync();
Logger?.Error($"MasterServer: [{(int)response.StatusCode}], {requestContent}");
2022-04-14 12:18:07 +02:00
}
2022-04-19 03:38:48 +02:00
else
2022-04-14 12:18:07 +02:00
{
Logger?.Error($"MasterServer: [{(int)response.StatusCode}]");
Logger?.Error($"MasterServer: [{await response.Content.ReadAsStringAsync()}]");
2022-04-14 12:18:07 +02:00
}
}
2022-04-19 03:38:48 +02:00
// Sleep for 10s
2022-06-27 15:35:23 +08:00
for(int i = 0; i<10; i++)
{
if (_stopping)
{
break;
}
else
{
Thread.Sleep(1000);
}
}
2021-12-07 08:18:29 +01:00
}
2022-04-19 03:38:48 +02:00
}
catch (HttpRequestException ex)
{
Logger?.Error($"MasterServer: {ex.InnerException.Message}");
2022-04-19 03:38:48 +02:00
}
catch (Exception ex)
{
Logger?.Error($"MasterServer: {ex.Message}");
2022-04-19 03:38:48 +02:00
}
2022-06-27 15:35:23 +08:00
});
_announceThread.Start();
2022-04-19 03:38:48 +02:00
#endregion
2021-12-07 08:18:29 +01:00
}
BaseScript.API=API;
2022-06-22 14:18:20 +08:00
BaseScript.OnStart();
Resources.LoadAll();
2022-06-27 15:35:23 +08:00
_listenerThread=new Thread(() => Listen());
_listenerThread.Start();
}
2022-07-01 13:54:18 +08:00
/// <summary>
/// Terminate threads and stop the server
/// </summary>
2022-06-27 15:35:23 +08:00
public void Stop()
{
_stopping = true;
2022-06-30 09:28:13 +08:00
_sendInfoTimer.Stop();
_sendInfoTimer.Enabled=false;
_sendInfoTimer.Dispose();
2022-06-27 15:35:23 +08:00
Logger?.Flush();
_listenerThread?.Join();
_announceThread?.Join();
2022-06-30 09:28:13 +08:00
_worker.Dispose();
2021-07-07 13:36:25 +02:00
}
private void Listen()
{
Logger?.Info("Listening for clients");
2022-06-27 15:35:23 +08:00
while (!_stopping)
2022-06-04 14:13:08 +08:00
{
try
{
ProcessMessage(MainNetServer.WaitMessage(200));
}
catch(Exception ex)
{
Logger?.Error("Error processing message");
Logger?.Error(ex);
}
2022-06-04 14:13:08 +08:00
}
2022-06-27 15:35:23 +08:00
Logger?.Info("Server is shutting down!");
MainNetServer.Shutdown("Server is shutting down!");
2022-06-22 14:18:20 +08:00
BaseScript.OnStop();
2022-07-01 12:22:31 +08:00
Resources.UnloadAll();
2022-06-04 14:13:08 +08:00
}
private void ProcessMessage(NetIncomingMessage message)
{
2022-06-24 10:33:36 +08:00
Client sender;
if (message == null) { return; }
2022-06-04 14:13:08 +08:00
switch (message.MessageType)
{
case NetIncomingMessageType.ConnectionApproval:
2021-07-07 13:36:25 +02:00
{
Logger?.Info($"New incoming connection from: [{message.SenderConnection.RemoteEndPoint}]");
2022-07-01 12:22:31 +08:00
if (message.ReadByte() != (byte)PacketType.Handshake)
2022-06-04 14:13:08 +08:00
{
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: Wrong packet!");
2022-06-04 14:13:08 +08:00
message.SenderConnection.Deny("Wrong packet!");
}
else
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
Packets.Handshake packet = new();
packet.Unpack(data);
GetHandshake(message.SenderConnection, packet);
}
catch (Exception e)
2021-07-07 13:36:25 +02:00
{
Logger?.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: {e.Message}");
2022-06-24 10:33:36 +08:00
Logger?.Error(e);
2022-06-04 14:13:08 +08:00
message.SenderConnection.Deny(e.Message);
}
}
break;
}
case NetIncomingMessageType.StatusChanged:
{
// Get sender client
if (!Clients.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
{
break;
}
2022-06-04 14:13:08 +08:00
NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
if (status == NetConnectionStatus.Disconnected)
{
SendPlayerDisconnectPacket(sender);
2022-06-04 14:13:08 +08:00
}
else if (status == NetConnectionStatus.Connected)
{
SendPlayerConnectPacket(sender);
2022-07-01 12:22:31 +08:00
_worker.QueueJob(() => API.Events.InvokePlayerConnected(sender));
Resources.SendTo(sender);
2022-06-04 14:13:08 +08:00
}
break;
}
case NetIncomingMessageType.Data:
{
2022-07-01 12:22:31 +08:00
// Get sender client
if (Clients.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
{
2022-07-01 12:22:31 +08:00
// Get packet type
var type = (PacketType)message.ReadByte();
switch (type)
2022-06-04 14:13:08 +08:00
{
2022-07-01 12:22:31 +08:00
case PacketType.Response:
{
2022-07-01 12:22:31 +08:00
int id = message.ReadInt32();
if (PendingResponses.TryGetValue(id, out var callback))
{
callback((PacketType)message.ReadByte(), message.ReadBytes(message.ReadInt32()));
PendingResponses.Remove(id);
}
break;
}
2022-07-01 12:22:31 +08:00
case PacketType.Request:
{
2022-07-01 12:22:31 +08:00
int id = message.ReadInt32();
if (RequestHandlers.TryGetValue((PacketType)message.ReadByte(), out var handler))
{
2022-07-01 12:22:31 +08:00
var response=MainNetServer.CreateMessage();
response.Write((byte)PacketType.Response);
response.Write(id);
handler(message.ReadBytes(message.ReadInt32())).Pack(response);
MainNetServer.SendMessage(response,message.SenderConnection,NetDeliveryMethod.ReliableOrdered);
}
2022-07-01 12:22:31 +08:00
break;
2022-04-03 02:27:30 +02:00
}
default:
2022-05-22 15:55:26 +08:00
{
2022-07-01 12:22:31 +08:00
byte[] data = message.ReadBytes(message.ReadInt32());
if (type.IsSyncEvent())
2022-05-22 15:55:26 +08:00
{
2022-07-01 12:22:31 +08:00
// Sync Events
try
{
var toSend = MainNetServer.Connections.Exclude(message.SenderConnection);
if (toSend.Count!=0)
{
var outgoingMessage = MainNetServer.CreateMessage();
outgoingMessage.Write((byte)type);
outgoingMessage.Write(data.Length);
outgoingMessage.Write(data);
MainNetServer.SendMessage(outgoingMessage, toSend, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.SyncEvents);
}
}
catch (Exception e)
2022-05-22 15:55:26 +08:00
{
2022-07-01 12:22:31 +08:00
DisconnectAndLog(message.SenderConnection, type, e);
}
2022-05-22 15:55:26 +08:00
}
2022-07-01 12:22:31 +08:00
else
2022-05-22 15:55:26 +08:00
{
2022-07-01 12:22:31 +08:00
HandlePacket(type, data, sender);
2022-05-22 15:55:26 +08:00
}
2022-07-01 12:22:31 +08:00
break;
2022-05-22 15:55:26 +08:00
}
2022-06-04 14:13:08 +08:00
}
2022-07-01 12:22:31 +08:00
}
break;
2021-07-07 13:36:25 +02:00
}
2022-06-04 14:13:08 +08:00
case NetIncomingMessageType.ConnectionLatencyUpdated:
{
// Get sender client
if (!Clients.TryGetValue(message.SenderConnection.RemoteUniqueIdentifier, out sender))
{
break;
}
if (sender != null)
2022-06-04 14:13:08 +08:00
{
sender.Latency = message.ReadFloat();
2022-06-04 14:13:08 +08:00
}
}
break;
case NetIncomingMessageType.ErrorMessage:
Logger?.Error(message.ReadString());
2022-06-04 14:13:08 +08:00
break;
case NetIncomingMessageType.WarningMessage:
Logger?.Warning(message.ReadString());
2022-06-04 14:13:08 +08:00
break;
case NetIncomingMessageType.DebugMessage:
case NetIncomingMessageType.VerboseDebugMessage:
Logger?.Debug(message.ReadString());
2022-06-04 14:13:08 +08:00
break;
2022-06-24 10:33:36 +08:00
case NetIncomingMessageType.UnconnectedData:
{
2022-07-01 12:22:31 +08:00
if (message.ReadByte()==(byte)PacketType.PublicKeyRequest)
2022-06-24 10:33:36 +08:00
{
var msg = MainNetServer.CreateMessage();
var p=new Packets.PublicKeyResponse();
Security.GetPublicKey(out p.Modulus,out p.Exponent);
p.Pack(msg);
Logger?.Debug($"Sending public key to {message.SenderEndPoint}, length:{msg.LengthBytes}");
MainNetServer.SendUnconnectedMessage(msg, message.SenderEndPoint);
}
}
break;
2022-06-04 14:13:08 +08:00
default:
Logger?.Error(string.Format("Unhandled type: {0} {1} bytes {2} | {3}", message.MessageType, message.LengthBytes, message.DeliveryMethod, message.SequenceChannel));
2022-06-04 14:13:08 +08:00
break;
}
2022-06-04 14:13:08 +08:00
MainNetServer.Recycle(message);
2021-07-07 13:36:25 +02:00
}
2022-07-01 12:22:31 +08:00
internal void QueueJob(Action job)
{
_worker.QueueJob(job);
}
private void HandlePacket(PacketType type,byte[] data,Client sender)
{
try
{
switch (type)
{
#region SyncData
case PacketType.PedStateSync:
{
Packets.PedStateSync packet = new();
packet.Unpack(data);
PedStateSync(packet, sender);
break;
}
case PacketType.VehicleStateSync:
{
Packets.VehicleStateSync packet = new();
packet.Unpack(data);
VehicleStateSync(packet, sender);
break;
}
case PacketType.PedSync:
{
Packets.PedSync packet = new();
packet.Unpack(data);
PedSync(packet, sender);
}
break;
case PacketType.VehicleSync:
{
Packets.VehicleSync packet = new();
packet.Unpack(data);
VehicleSync(packet, sender);
}
break;
case PacketType.ProjectileSync:
{
Packets.ProjectileSync packet = new();
packet.Unpack(data);
ProjectileSync(packet, sender);
}
break;
#endregion
case PacketType.ChatMessage:
{
Packets.ChatMessage packet = new();
packet.Unpack(data);
_worker.QueueJob(() => API.Events.InvokeOnChatMessage(packet, sender));
SendChatMessage(packet, sender);
}
break;
case PacketType.CustomEvent:
{
Packets.CustomEvent packet = new Packets.CustomEvent();
packet.Unpack(data);
_worker.QueueJob(() => API.Events.InvokeCustomEventReceived(packet, sender));
}
break;
default:
Logger?.Error("Unhandled Data / Packet type");
break;
}
}
catch (Exception e)
{
DisconnectAndLog(sender.Connection, type, e);
}
}
object _sendPlayersLock=new object();
internal void SendPlayerInfos()
{
2022-06-22 14:18:20 +08:00
lock (_sendPlayersLock)
{
2022-06-22 14:18:20 +08:00
foreach (Client c in Clients.Values)
{
2022-06-22 14:18:20 +08:00
BaseScript.SetAutoRespawn(c,c.Config.EnableAutoRespawn);
MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != c.NetID).ForEach(x =>
{
2022-06-22 14:18:20 +08:00
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
2022-07-01 13:54:18 +08:00
PedID=c.Player.ID,
2022-06-22 14:18:20 +08:00
Username=c.Username,
Latency=c.Latency,
Flags=c.Config.GetFlags(),
BlipColor=c.Config.BlipColor,
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
});
}
2022-06-01 19:05:45 +08:00
2022-06-22 14:18:20 +08:00
}
}
2021-07-07 13:36:25 +02:00
2022-07-01 12:22:31 +08:00
private void DisconnectAndLog(NetConnection senderConnection,PacketType type, Exception e)
{
Logger?.Error($"Error receiving a packet of type {type}");
Logger?.Error(e.Message);
Logger?.Error(e.StackTrace);
senderConnection.Disconnect(e.Message);
}
#region -- SYNC --
2021-07-07 13:36:25 +02:00
// Before we approve the connection, we must shake hands
2022-06-04 18:09:42 +08:00
private void GetHandshake(NetConnection connection, Packets.Handshake packet)
2021-07-07 13:36:25 +02:00
{
Logger?.Debug("New handshake from: [Name: " + packet.Username + " | Address: " + connection.RemoteEndPoint.Address.ToString() + "]");
2021-07-07 13:36:25 +02:00
if (!packet.ModVersion.StartsWith(_compatibleVersion))
2021-12-18 00:21:23 +01:00
{
2022-06-04 18:09:42 +08:00
connection.Deny($"RAGECOOP version {_compatibleVersion.Replace('_', '.')}.x required!");
2021-12-18 00:21:23 +01:00
return;
}
2021-07-07 13:36:25 +02:00
if (string.IsNullOrWhiteSpace(packet.Username))
{
2022-06-04 18:09:42 +08:00
connection.Deny("Username is empty or contains spaces!");
2021-07-07 13:36:25 +02:00
return;
}
if (packet.Username.Any(p => !char.IsLetterOrDigit(p) && !(p == '_') && !(p=='-')))
2021-07-07 13:36:25 +02:00
{
2022-06-04 18:09:42 +08:00
connection.Deny("Username contains special chars!");
2021-07-07 13:36:25 +02:00
return;
}
2022-06-21 18:13:30 +08:00
if (Clients.Values.Any(x => x.Username.ToLower() == packet.Username.ToLower()))
2021-07-07 13:36:25 +02:00
{
connection.Deny("Username is already taken!");
2021-07-07 13:36:25 +02:00
return;
}
2022-06-24 10:33:36 +08:00
string passhash;
try
{
Security.AddConnection(connection.RemoteEndPoint, packet.AesKeyCrypted,packet.AesIVCrypted);
passhash=BitConverter.ToString(Security.Decrypt(packet.PassHashEncrypted, connection.RemoteEndPoint)).Replace("-", String.Empty);
2022-06-24 10:33:36 +08:00
}
catch (Exception ex)
{
Logger?.Error($"Cannot process handshake packet from {connection.RemoteEndPoint}");
Logger?.Error(ex);
connection.Deny("Malformed handshak packet!");
return;
}
var args = new HandshakeEventArgs()
2021-07-07 13:36:25 +02:00
{
EndPoint=connection.RemoteEndPoint,
ID=packet.PedID,
2022-06-24 10:33:36 +08:00
Username=packet.Username,
PasswordHash=passhash,
};
API.Events.InvokePlayerHandshake(args);
if (args.Cancel)
2021-08-21 16:52:17 +02:00
{
connection.Deny(args.DenyReason);
2021-08-21 16:52:17 +02:00
return;
2021-07-07 13:36:25 +02:00
}
2022-06-24 10:33:36 +08:00
connection.Approve();
2021-07-09 00:20:09 +02:00
Client tmpClient;
2021-07-07 13:36:25 +02:00
// Add the player to Players
lock (Clients)
{
2022-06-04 18:09:42 +08:00
Clients.Add(connection.RemoteUniqueIdentifier,
tmpClient = new Client(this)
2021-08-26 17:01:32 +02:00
{
2022-06-04 18:09:42 +08:00
NetID = connection.RemoteUniqueIdentifier,
Connection=connection,
2022-06-21 18:13:30 +08:00
Username=packet.Username,
Player = new()
{
2022-06-24 10:33:36 +08:00
ID= packet.PedID,
}
2021-08-26 17:01:32 +02:00
}
2022-05-22 15:55:26 +08:00
);;
}
2022-06-04 18:09:42 +08:00
Logger?.Debug($"Handshake sucess, Player:{packet.Username} PedID:{packet.PedID}");
2022-06-24 10:33:36 +08:00
2021-07-07 13:36:25 +02:00
}
// The connection has been approved, now we need to send all other players to the new player and the new player to all players
private void SendPlayerConnectPacket(Client newClient)
2021-07-07 13:36:25 +02:00
{
// Send other players to this client
Clients.Values.ForEach(target =>
2021-07-07 13:36:25 +02:00
{
if (target==newClient) { return; }
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerConnect()
{
// NetHandle = targetNetHandle,
Username = target.Username,
2022-07-01 13:54:18 +08:00
PedID=target.Player.ID,
2021-07-07 13:36:25 +02:00
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, newClient.Connection, NetDeliveryMethod.ReliableOrdered, 0);
});
// Send new client to all players
var cons = MainNetServer.Connections.Exclude(newClient.Connection);
if (cons.Count!=0)
{
2021-07-07 13:36:25 +02:00
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
2022-01-01 03:07:18 +01:00
new Packets.PlayerConnect()
2021-07-07 13:36:25 +02:00
{
2022-07-01 13:54:18 +08:00
PedID=newClient.Player.ID,
Username = newClient.Username
2022-05-22 15:55:26 +08:00
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage,cons , NetDeliveryMethod.ReliableOrdered, 0);
}
2022-06-04 18:09:42 +08:00
Logger?.Info($"Player {newClient.Username} connected!");
2022-06-24 18:25:24 +08:00
if (!string.IsNullOrEmpty(Settings.WelcomeMessage))
{
2022-06-24 18:25:24 +08:00
SendChatMessage(new Packets.ChatMessage() { Username = "Server", Message = Settings.WelcomeMessage }, null,new List<NetConnection>() { newClient.Connection });
}
2021-07-07 13:36:25 +02:00
}
// Send all players a message that someone has left the server
private void SendPlayerDisconnectPacket(Client localClient)
2021-07-07 13:36:25 +02:00
{
var cons = MainNetServer.Connections.Exclude(localClient.Connection);
if (cons.Count!=0)
2021-07-07 13:36:25 +02:00
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
2022-01-01 03:07:18 +01:00
new Packets.PlayerDisconnect()
{
2022-07-01 13:54:18 +08:00
PedID=localClient.Player.ID,
2021-07-07 13:36:25 +02:00
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage,cons , NetDeliveryMethod.ReliableOrdered, 0);
}
2022-06-30 09:28:13 +08:00
_worker.QueueJob(() => API.Events.InvokePlayerDisconnected(localClient));
2022-07-01 13:54:18 +08:00
Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.Player.ID}");
Clients.Remove(localClient.NetID);
2022-06-27 13:02:31 +08:00
Security.RemoveConnection(localClient.Connection.RemoteEndPoint);
2022-05-22 15:55:26 +08:00
}
#region SyncEntities
private void PedStateSync(Packets.PedStateSync packet, Client client)
2022-05-22 15:55:26 +08:00
{
2022-05-30 11:11:40 +08:00
foreach (var c in Clients.Values)
2022-05-22 15:55:26 +08:00
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
if (c.NetID==client.NetID) { continue; }
2022-05-30 11:11:40 +08:00
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
2022-05-22 15:55:26 +08:00
}
private void VehicleStateSync(Packets.VehicleStateSync packet, Client client)
2022-05-22 15:55:26 +08:00
{
// Save the new data
2022-07-01 13:54:18 +08:00
_worker.QueueJob(() =>
2022-05-22 15:55:26 +08:00
{
2022-07-01 13:54:18 +08:00
if (packet.Passengers.ContainsValue(client.Player.ID))
{
client.Player.VehicleID = packet.ID;
}
});
2022-05-22 15:55:26 +08:00
2022-05-30 11:11:40 +08:00
foreach (var c in Clients.Values)
2022-05-22 15:55:26 +08:00
{
if (c.NetID==client.NetID) { continue; }
2022-05-22 15:55:26 +08:00
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
2022-05-30 11:11:40 +08:00
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
2022-05-22 15:55:26 +08:00
}
private void PedSync(Packets.PedSync packet, Client client)
2022-05-22 15:55:26 +08:00
{
2022-07-01 13:54:18 +08:00
bool isPlayer = packet.ID==client.Player.ID;
2022-06-22 14:18:20 +08:00
if (isPlayer)
{
client.Player.Position=packet.Position;
2022-06-30 09:28:13 +08:00
client.Player.Health=packet.Health ;
2022-07-01 13:54:18 +08:00
client.Player.Owner=client;
2022-06-30 09:28:13 +08:00
_worker.QueueJob(() => API.Events.InvokePlayerUpdate(client));
2022-06-22 14:18:20 +08:00
}
foreach (var c in Clients.Values)
2022-05-22 15:55:26 +08:00
{
2022-05-30 11:11:40 +08:00
// Don't send data back
if (c.NetID==client.NetID) { continue; }
// Check streaming distance
if (isPlayer)
{
2022-06-24 18:25:24 +08:00
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
{
continue;
}
}
2022-06-24 18:25:24 +08:00
else if ((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
{
continue;
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
2022-05-30 11:11:40 +08:00
MainNetServer.SendMessage(outgoingMessage,c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
2021-07-07 13:36:25 +02:00
}
private void VehicleSync(Packets.VehicleSync packet, Client client)
2022-05-22 15:55:26 +08:00
{
bool isPlayer = packet.ID==client.Player.VehicleID;
2022-05-30 11:11:40 +08:00
foreach (var c in Clients.Values)
2022-05-22 15:55:26 +08:00
{
if (c.NetID==client.NetID) { continue; }
if (isPlayer)
{
// Player's vehicle
2022-06-24 18:25:24 +08:00
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
{
continue;
}
}
2022-06-24 18:25:24 +08:00
else if((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
{
continue;
}
2022-05-22 15:55:26 +08:00
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
2022-05-30 11:11:40 +08:00
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
2022-05-22 15:55:26 +08:00
}
private void ProjectileSync(Packets.ProjectileSync packet, Client client)
2021-07-07 13:36:25 +02:00
{
2022-05-30 11:11:40 +08:00
foreach (var c in Clients.Values)
2021-08-26 17:01:32 +02:00
{
if (c.NetID==client.NetID) { continue; }
2021-08-26 17:01:32 +02:00
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
2022-05-30 11:11:40 +08:00
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
2021-07-07 13:36:25 +02:00
}
2022-05-22 15:55:26 +08:00
#endregion
2021-07-07 13:36:25 +02:00
// Send a message to targets or all players
private void SendChatMessage(Packets.ChatMessage packet,Client sender=null, List<NetConnection> targets = null)
2021-07-07 13:36:25 +02:00
{
2022-06-04 18:09:42 +08:00
if (packet.Message.StartsWith('/'))
2021-08-15 07:54:25 +02:00
{
2022-06-04 18:09:42 +08:00
string[] cmdArgs = packet.Message.Split(" ");
2022-06-30 09:28:13 +08:00
string cmdName = cmdArgs[0].Remove(0, 1);
_worker.QueueJob(()=>API.Events.InvokeOnCommandReceived(cmdName, cmdArgs, sender));
2021-08-15 07:54:25 +02:00
2022-06-04 18:09:42 +08:00
return;
}
packet.Message = packet.Message.Replace("~", "");
SendChatMessage(packet.Username, packet.Message, targets);
2021-08-18 11:47:59 +02:00
Logger?.Info(packet.Username + ": " + packet.Message);
2021-07-07 13:36:25 +02:00
}
internal void SendChatMessage(string username, string message, List<NetConnection> targets = null)
{
if (MainNetServer.Connections.Count==0) { return; }
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
2022-05-22 15:55:26 +08:00
new Packets.ChatMessage() { Username = username, Message = message }.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, targets ?? MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Chat);
}
internal void SendChatMessage(string username, string message, NetConnection target)
{
SendChatMessage(username, message, new List<NetConnection>() { target });
}
#endregion
2021-08-18 11:47:59 +02:00
internal void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback)
{
Command command = new(name) { Usage = usage, ArgsLength = argsLength };
if (Commands.ContainsKey(command))
{
throw new Exception("Command \"" + command.Name + "\" was already been registered!");
}
Commands.Add(command, callback);
}
internal void RegisterCommand(string name, Action<CommandContext> callback)
2021-08-18 11:47:59 +02:00
{
Command command = new(name);
2021-08-18 11:47:59 +02:00
if (Commands.ContainsKey(command))
{
throw new Exception("Command \"" + command.Name + "\" was already been registered!");
}
Commands.Add(command, callback);
}
internal void RegisterCommands<T>()
2021-08-18 11:47:59 +02:00
{
IEnumerable<MethodInfo> commands = typeof(T).GetMethods().Where(method => method.GetCustomAttributes(typeof(Command), false).Any());
2021-08-18 11:47:59 +02:00
foreach (MethodInfo method in commands)
{
Command attribute = method.GetCustomAttribute<Command>(true);
2021-08-18 11:47:59 +02:00
RegisterCommand(attribute.Name, attribute.Usage, attribute.ArgsLength, (Action<CommandContext>)Delegate.CreateDelegate(typeof(Action<CommandContext>), method));
2021-08-18 11:47:59 +02:00
}
}
2022-07-01 12:22:31 +08:00
internal T GetResponse<T>(Client client,Packet request, ConnectionChannel channel = ConnectionChannel.RequestResponse,int timeout=5000) where T:Packet, new()
{
if (Thread.CurrentThread==_listenerThread)
{
throw new InvalidOperationException("Cannot wait for response from the listener thread!");
}
var received=new AutoResetEvent(false);
byte[] response=null;
var id = NewRequestID();
PendingResponses.Add(id, (type,p) =>
{
response=p;
received.Set();
});
var msg = MainNetServer.CreateMessage();
msg.Write((byte)PacketType.Request);
msg.Write(id);
request.Pack(msg);
MainNetServer.SendMessage(msg,client.Connection,NetDeliveryMethod.ReliableOrdered,(int)channel);
if (received.WaitOne(timeout))
{
var p = new T();
p.Unpack(response);
return p;
}
else
{
return null;
}
}
internal void SendFile(string path,string name,Client client,Action<float> updateCallback=null)
{
2022-07-01 12:22:31 +08:00
int id = NewFileID();
var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
fs.Seek(0, SeekOrigin.Begin);
var total = fs.Length;
2022-07-01 12:22:31 +08:00
if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferRequest()
{
FileLength= total,
Name=name,
ID=id,
},ConnectionChannel.File)?.Response!=FileResponse.NeedToDownload)
{
Logger?.Info($"Skipping file transfer \"{name}\" to {client.Username}");
fs.Close();
fs.Dispose();
return;
}
Logger?.Debug($"Initiating file transfer:{name}, {total}");
FileTransfer transfer = new()
{
ID=id,
Name = name,
};
2022-07-01 12:22:31 +08:00
InProgressFileTransfers.Add(id, transfer);
int read = 0;
2022-07-01 12:22:31 +08:00
int thisRead;
do
{
// 4 KB chunk
byte[] chunk = new byte[4096];
read += thisRead=fs.Read(chunk, 0, 4096);
if (thisRead!=chunk.Length)
{
if (thisRead==0) { break; }
Logger?.Trace($"Purging chunk:{thisRead}");
Array.Resize(ref chunk, thisRead);
}
Send(
new Packets.FileTransferChunk()
{
ID=id,
FileChunk=chunk,
},
client, ConnectionChannel.File, NetDeliveryMethod.ReliableOrdered);
transfer.Progress=read/fs.Length;
if (updateCallback!=null) { updateCallback(transfer.Progress);}
} while (thisRead>0);
2022-07-01 12:22:31 +08:00
if(GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferComplete()
{
ID= id,
},ConnectionChannel.File)?.Response!=FileResponse.Completed)
{
Logger.Warning($"File trasfer to {client.Username} failed: "+name);
}
fs.Close();
fs.Dispose();
Logger?.Debug($"All file chunks sent:{name}");
InProgressFileTransfers.Remove(id);
}
2022-07-01 12:22:31 +08:00
private int NewFileID()
{
int ID = 0;
while ((ID==0)
|| InProgressFileTransfers.ContainsKey(ID))
{
byte[] rngBytes = new byte[4];
RandomNumberGenerator.Create().GetBytes(rngBytes);
// Convert the bytes into an integer
ID = BitConverter.ToInt32(rngBytes, 0);
}
return ID;
}
2022-07-01 12:22:31 +08:00
private int NewRequestID()
{
int ID = 0;
while ((ID==0)
|| PendingResponses.ContainsKey(ID))
{
byte[] rngBytes = new byte[4];
RandomNumberGenerator.Create().GetBytes(rngBytes);
// Convert the bytes into an integer
ID = BitConverter.ToInt32(rngBytes, 0);
}
return ID;
}
internal void Send(Packet p,Client client, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
p.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, client.Connection,method,(int)channel);
}
2021-07-07 13:36:25 +02:00
}
}