RAGECOOP-V/RageCoop.Server/Networking/Server.Connections.cs
2022-08-18 17:45:08 +08:00

199 lines
7.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
namespace RageCoop.Server
{
public partial class Server
{
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);
}
private void GetHandshake(NetConnection connection, Packets.Handshake packet)
{
Logger?.Debug("New handshake from: [Name: " + packet.Username + " | Address: " + connection.RemoteEndPoint.Address.ToString() + "]");
if (!packet.ModVersion.StartsWith(Version.ToString(3)))
{
connection.Deny($"RAGECOOP version {Version.ToString(3)} required!");
return;
}
if (string.IsNullOrWhiteSpace(packet.Username))
{
connection.Deny("Username is empty or contains spaces!");
return;
}
if (packet.Username.Any(p => !_allowedCharacterSet.Contains(p)))
{
connection.Deny("Username contains special chars!");
return;
}
if (ClientsByNetHandle.Values.Any(x => x.Username.ToLower() == packet.Username.ToLower()))
{
connection.Deny("Username is already taken!");
return;
}
try
{
Security.AddConnection(connection.RemoteEndPoint, packet.AesKeyCrypted, packet.AesIVCrypted);
var args = new HandshakeEventArgs()
{
EndPoint=connection.RemoteEndPoint,
ID=packet.PedID,
Username=packet.Username,
PasswordHash=Security.Decrypt(packet.PasswordEncrypted, connection.RemoteEndPoint).GetString().GetSHA256Hash().ToHexString(),
};
API.Events.InvokePlayerHandshake(args);
if (args.Cancel)
{
connection.Deny(args.DenyReason);
return;
}
}
catch (Exception ex)
{
Logger?.Error($"Cannot process handshake packet from {connection.RemoteEndPoint}");
Logger?.Error(ex);
connection.Deny("Malformed handshak packet!");
return;
}
var handshakeSuccess = MainNetServer.CreateMessage();
var currentClients = ClientsByID.Values.ToArray();
var players = new Packets.PlayerData[currentClients.Length];
for (int i = 0; i<players.Length; i++)
{
players[i]=new Packets.PlayerData()
{
ID=currentClients[i].Player.ID,
Username=currentClients[i].Username,
};
}
new Packets.HandshakeSuccess()
{
Players=players
}.Pack(handshakeSuccess);
connection.Approve(handshakeSuccess);
Client tmpClient;
// Add the player to Players
lock (ClientsByNetHandle)
{
var player = new ServerPed(this)
{
ID= packet.PedID,
};
Entities.Add(player);
ClientsByNetHandle.Add(connection.RemoteUniqueIdentifier,
tmpClient = new Client(this)
{
NetHandle = connection.RemoteUniqueIdentifier,
Connection=connection,
Username=packet.Username,
Player = player,
InternalEndPoint=packet.InternalEndPoint,
}
);
player.Owner=tmpClient;
ClientsByName.Add(packet.Username.ToLower(), tmpClient);
ClientsByID.Add(player.ID, tmpClient);
if (ClientsByNetHandle.Count==1)
{
_hostClient=tmpClient;
}
}
Logger?.Debug($"Handshake sucess, Player:{packet.Username} PedID:{packet.PedID}");
}
// 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 PlayerConnected(Client newClient)
{
if (newClient==_hostClient)
{
API.SendCustomEvent(new() { newClient }, CustomEvents.IsHost, true);
}
// Send new client to all players
var cons = MainNetServer.Connections.Exclude(newClient.Connection);
if (cons.Count!=0)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerConnect()
{
PedID=newClient.Player.ID,
Username = newClient.Username
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, cons, NetDeliveryMethod.ReliableOrdered, 0);
}
// Send all props to this player
BaseScript.SendServerPropsTo(new(Entities.ServerProps.Values), new() { newClient });
// Send all blips to this player
BaseScript.SendServerBlipsTo(new(Entities.Blips.Values), new() { newClient });
// Create P2P connection
if (Settings.UseP2P)
{
ClientsByNetHandle.Values.ForEach(target =>
{
if (target==newClient) { return; }
HolePunch(target,newClient);
});
}
Logger?.Info($"Player {newClient.Username} connected!");
if (!string.IsNullOrEmpty(Settings.WelcomeMessage))
{
SendChatMessage("Server", Settings.WelcomeMessage, newClient);
}
}
// Send all players a message that someone has left the server
private void PlayerDisconnected(Client localClient)
{
var cons = MainNetServer.Connections.Exclude(localClient.Connection);
if (cons.Count!=0)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerDisconnect()
{
PedID=localClient.Player.ID,
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, cons, NetDeliveryMethod.ReliableOrdered, 0);
}
Entities.CleanUp(localClient);
_worker.QueueJob(() => API.Events.InvokePlayerDisconnected(localClient));
Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.Player.ID}");
if (ClientsByNetHandle.ContainsKey(localClient.NetHandle)) { ClientsByNetHandle.Remove(localClient.NetHandle); }
if (ClientsByName.ContainsKey(localClient.Username.ToLower())) { ClientsByName.Remove(localClient.Username.ToLower()); }
if (ClientsByID.ContainsKey(localClient.Player.ID)) { ClientsByID.Remove(localClient.Player.ID); }
if (localClient==_hostClient)
{
_hostClient = ClientsByNetHandle.Values.FirstOrDefault();
_hostClient?.SendCustomEvent(CustomEvents.IsHost, true);
}
Security.RemoveConnection(localClient.Connection.RemoteEndPoint);
}
}
}