Restore
This commit is contained in:
13
.github/FUNDING.yml
vendored
Normal file
13
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [RAGECOOP] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
#patreon: # Replace with a single Patreon username
|
||||
#open_collective: # Replace with a single Open Collective username
|
||||
#ko_fi: # Replace with a single Ko-fi username
|
||||
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
#liberapay: # Replace with a single Liberapay username
|
||||
#issuehunt: # Replace with a single IssueHunt username
|
||||
#otechie: # Replace with a single Otechie username
|
||||
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: ['https://www.buymeacoffee.com/xEntenKoeniqx', 'https://patreon.com/Sardelka']
|
@ -1,6 +1,7 @@
|
||||
|
||||
|
||||
# 🌐 RAGECOOP
|
||||
[![Downloads][downloads-shield]][downloads-url]
|
||||
[![Contributors][contributors-shield]][contributors-url]
|
||||
[![Forks][forks-shield]][forks-url]
|
||||
[![Stargazers][stars-shield]][stars-url]
|
||||
@ -55,9 +56,6 @@ You can also download nightly builds [here](https://github.com/RAGECOOP/RAGECOOP
|
||||
Please note that this is incompatible with all previous versions of ragecoop, remove old files before installing.
|
||||
|
||||
|
||||
# Support us
|
||||
|
||||
<a href="https://patreon.com/Sardelka"><img src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3DSardelka%26type%3Dpatrons&style=for-the-badge" /></a>
|
||||
|
||||
# 🦆 Special thanks to
|
||||
- [Makinolo](https://github.com/Makinolo), [oldnapalm](https://github.com/oldnapalm)
|
||||
@ -70,6 +68,8 @@ Please note that this is incompatible with all previous versions of ragecoop, re
|
||||
# 📝 License
|
||||
This project is licensed under [MIT license](https://github.com/RAGECOOP/RAGECOOP-V/blob/main/LICENSE)
|
||||
|
||||
[downloads-shield]: https://img.shields.io/github/downloads/RAGECOOP/RAGECOOP-V/total?style=for-the-badge
|
||||
[downloads-url]: https://github.com/RAGECOOP/RAGECOOP-V/releases
|
||||
[contributors-shield]: https://img.shields.io/github/contributors/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
|
||||
[contributors-url]: https://github.com/RAGECOOP/RAGECOOP-V/graphs/contributors
|
||||
[forks-shield]: https://img.shields.io/github/forks/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
|
||||
|
85
RageCoop.Client/Networking/HolePunch.cs
Normal file
85
RageCoop.Client/Networking/HolePunch.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using RageCoop.Core;
|
||||
using Lidgren.Network;
|
||||
using System.Net;
|
||||
|
||||
namespace RageCoop.Client
|
||||
{
|
||||
internal static partial class HolePunch
|
||||
{
|
||||
static HolePunch()
|
||||
{
|
||||
// Periodically send hole punch message as needed
|
||||
var timer=new Timer(1000);
|
||||
timer.Elapsed+=DoPunch;
|
||||
timer.Enabled=true;
|
||||
}
|
||||
|
||||
private static void DoPunch(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Networking.IsOnServer) { return; }
|
||||
foreach (var p in PlayerList.Players.Values.ToArray())
|
||||
{
|
||||
if (p.InternalEndPoint!=null && p.ExternalEndPoint!=null && (p.Connection==null || p.Connection.Status==NetConnectionStatus.Disconnected))
|
||||
{
|
||||
Main.Logger.Trace($"Sending HolePunch message to {p.InternalEndPoint},{p.ExternalEndPoint}. {p.Username}:{p.PedID}");
|
||||
var msg = Networking.Peer.CreateMessage();
|
||||
new Packets.HolePunch
|
||||
{
|
||||
Puncher=Main.LocalPlayerID,
|
||||
Status=p.HolePunchStatus
|
||||
}.Pack(msg);
|
||||
Networking.Peer.SendUnconnectedMessage(msg, new List<IPEndPoint> { p.InternalEndPoint, p.ExternalEndPoint });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.Logger.Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Add(Packets.HolePunchInit p)
|
||||
{
|
||||
if(PlayerList.Players.TryGetValue(p.TargetID,out var player))
|
||||
{
|
||||
Main.Logger.Debug($"{p.TargetID},{player.Username} added to HolePunch target");
|
||||
player.InternalEndPoint = CoreUtils.StringToEndPoint(p.TargetInternal);
|
||||
player.ExternalEndPoint = CoreUtils.StringToEndPoint(p.TargetExternal);
|
||||
player.ConnectWhenPunched=p.Connect;
|
||||
}
|
||||
else
|
||||
{
|
||||
Main.Logger.Warning("No player with specified TargetID found for hole punching:"+p.TargetID);
|
||||
}
|
||||
}
|
||||
public static void Punched(Packets.HolePunch p,IPEndPoint from)
|
||||
{
|
||||
Main.Logger.Debug($"HolePunch message received from:{from}, status:{p.Status}");
|
||||
if(PlayerList.Players.TryGetValue(p.Puncher,out var puncher))
|
||||
{
|
||||
Main.Logger.Debug("Puncher identified as: "+puncher.Username);
|
||||
puncher.HolePunchStatus=(byte)(p.Status+1);
|
||||
if (p.Status>=3)
|
||||
{
|
||||
Main.Logger.Debug("HolePunch sucess: "+from+", "+puncher.PedID);
|
||||
if (puncher.ConnectWhenPunched && (puncher.Connection==null || puncher.Connection.Status==NetConnectionStatus.Disconnected))
|
||||
{
|
||||
Main.Logger.Debug("Connecting to peer: "+from);
|
||||
var msg = Networking.Peer.CreateMessage();
|
||||
new Packets.P2PConnect { ID=Main.LocalPlayerID }.Pack(msg);
|
||||
puncher.Connection=Networking.Peer.Connect(from,msg);
|
||||
Networking.Peer.FlushSendQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -146,19 +146,20 @@ namespace RageCoop.Client
|
||||
PedID = packet.PedID,
|
||||
Username= packet.Username,
|
||||
};
|
||||
GTA.UI.Notification.Show($"~h~{p.Username}~h~ connected.");
|
||||
PlayerList.SetPlayer(packet.PedID, packet.Username);
|
||||
|
||||
Main.Logger.Debug($"player connected:{p.Username}");
|
||||
Main.QueueAction(() =>
|
||||
GTA.UI.Notification.Show($"~h~{p.Username}~h~ connected."));
|
||||
}
|
||||
private static void PlayerDisconnect(Packets.PlayerDisconnect packet)
|
||||
{
|
||||
var name = PlayerList.GetPlayer(packet.PedID).Username;
|
||||
GTA.UI.Notification.Show($"~h~{name}~h~ left.");
|
||||
PlayerList.RemovePlayer(packet.PedID);
|
||||
EntityPool.RemoveAllFromPlayer(packet.PedID);
|
||||
|
||||
|
||||
Main.QueueAction(() =>
|
||||
GTA.UI.Notification.Show($"~h~{name}~h~ left."));
|
||||
}
|
||||
|
||||
#endregion // -- PLAYER --
|
||||
|
@ -70,16 +70,22 @@ namespace RageCoop.Client
|
||||
|
||||
Main.Logger.Info(">> Connected <<");
|
||||
}
|
||||
else if (PlayerList.PendingConnections.TryGetValue(message.SenderEndPoint.ToString(),out var player))
|
||||
else
|
||||
{
|
||||
PlayerList.PendingConnections.Remove(message.SenderEndPoint.ToString());
|
||||
Main.Logger.Debug($"Connection to {player.Username},{player.PedID},{player.Connection.Status} established, sending ID...");
|
||||
// Inform target our ID
|
||||
SendTo(new Packets.ConnectionEstablished()
|
||||
// Self-initiated connection
|
||||
if (message.SenderConnection.RemoteHailMessage==null) { return; }
|
||||
|
||||
var p = message.SenderConnection.RemoteHailMessage.GetPacket<Packets.P2PConnect>();
|
||||
if (PlayerList.Players.TryGetValue(p.ID,out var player))
|
||||
{
|
||||
ID=Main.LocalPlayerID
|
||||
}, player.Connection, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
|
||||
Peer.FlushSendQueue();
|
||||
player.Connection=message.SenderConnection;
|
||||
Main.Logger.Debug($"Direct connectionn to {player.Username} established");
|
||||
}
|
||||
else
|
||||
{
|
||||
Main.Logger.Info($"Unidentified peer connection from {message.SenderEndPoint} was rejected.");
|
||||
message.SenderConnection.Disconnect("eat poop");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NetConnectionStatus.Disconnected:
|
||||
@ -103,22 +109,6 @@ namespace RageCoop.Client
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NetIncomingMessageType.NatIntroductionSuccess:
|
||||
{
|
||||
var playerID = int.Parse(message.ReadString());
|
||||
// Main.Logger.Debug("NatIntroductionSuccess received from "+message.SenderEndPoint+" "+playerID);
|
||||
if (PlayerList.Players.TryGetValue(playerID, out var player) && (player.Connection==null || player.Connection.Status==NetConnectionStatus.Disconnected))
|
||||
{
|
||||
Main.Logger.Debug($"Establishing direct connection to {player.Username},{player.PedID}");
|
||||
player.Connection=Peer.Connect(message.SenderEndPoint);
|
||||
var ep = message.SenderEndPoint.ToString();
|
||||
if (!PlayerList.PendingConnections.ContainsKey(ep))
|
||||
{
|
||||
PlayerList.PendingConnections.Add(ep, player);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NetIncomingMessageType.Data:
|
||||
{
|
||||
|
||||
@ -184,14 +174,23 @@ namespace RageCoop.Client
|
||||
var packetType = (PacketType)message.ReadByte();
|
||||
int len = message.ReadInt32();
|
||||
byte[] data = message.ReadBytes(len);
|
||||
if (packetType==PacketType.PublicKeyResponse)
|
||||
switch (packetType)
|
||||
{
|
||||
var packet = new Packets.PublicKeyResponse();
|
||||
packet.Deserialize(data);
|
||||
Security.SetServerPublicKey(packet.Modulus, packet.Exponent);
|
||||
_publicKeyReceived.Set();
|
||||
}
|
||||
|
||||
case PacketType.HolePunch:
|
||||
{
|
||||
HolePunch.Punched(data.GetPacket<Packets.HolePunch>(), message.SenderEndPoint);
|
||||
break;
|
||||
}
|
||||
case PacketType.PublicKeyResponse:
|
||||
{
|
||||
|
||||
var packet = data.GetPacket<Packets.PublicKeyResponse>();
|
||||
Security.SetServerPublicKey(packet.Modulus, packet.Exponent);
|
||||
_publicKeyReceived.Set();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NetIncomingMessageType.DebugMessage:
|
||||
@ -211,69 +210,33 @@ namespace RageCoop.Client
|
||||
|
||||
switch (packetType)
|
||||
{
|
||||
case PacketType.ConnectionEstablished:
|
||||
{
|
||||
var p=new Packets.ConnectionEstablished();
|
||||
p.Deserialize(data);
|
||||
Main.Logger.Debug("Connection message received from "+senderConnection.RemoteEndPoint+","+p.ID);
|
||||
if(PlayerList.Players.TryGetValue(p.ID,out var player)){
|
||||
Main.Logger.Debug($"Direct connection to {player.Username},{player.PedID} has been established");
|
||||
player.Connection=senderConnection;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PacketType.HolePunchInit:
|
||||
HolePunch.Add(data.GetPacket<Packets.HolePunchInit>());
|
||||
break;
|
||||
|
||||
case PacketType.PlayerConnect:
|
||||
{
|
||||
|
||||
Packets.PlayerConnect packet = new Packets.PlayerConnect();
|
||||
packet.Deserialize(data);
|
||||
|
||||
Main.QueueAction(() => PlayerConnect(packet));
|
||||
}
|
||||
PlayerConnect(data.GetPacket<Packets.PlayerConnect>());
|
||||
break;
|
||||
|
||||
case PacketType.PlayerDisconnect:
|
||||
{
|
||||
|
||||
Packets.PlayerDisconnect packet = new Packets.PlayerDisconnect();
|
||||
packet.Deserialize(data);
|
||||
Main.QueueAction(() => PlayerDisconnect(packet));
|
||||
|
||||
}
|
||||
PlayerDisconnect(data.GetPacket<Packets.PlayerDisconnect>());
|
||||
break;
|
||||
|
||||
case PacketType.PlayerInfoUpdate:
|
||||
{
|
||||
var packet = new Packets.PlayerInfoUpdate();
|
||||
packet.Deserialize(data);
|
||||
PlayerList.UpdatePlayer(packet);
|
||||
break;
|
||||
}
|
||||
#region ENTITY SYNC
|
||||
case PacketType.VehicleSync:
|
||||
{
|
||||
|
||||
Packets.VehicleSync packet = new Packets.VehicleSync();
|
||||
packet.Deserialize(data);
|
||||
VehicleSync(packet);
|
||||
|
||||
}
|
||||
PlayerList.UpdatePlayer(data.GetPacket<Packets.PlayerInfoUpdate>());
|
||||
break;
|
||||
|
||||
case PacketType.VehicleSync:
|
||||
VehicleSync(data.GetPacket<Packets.VehicleSync>());
|
||||
break;
|
||||
|
||||
case PacketType.PedSync:
|
||||
{
|
||||
|
||||
Packets.PedSync packet = new Packets.PedSync();
|
||||
packet.Deserialize(data);
|
||||
PedSync(packet);
|
||||
|
||||
}
|
||||
PedSync(data.GetPacket<Packets.PedSync>());
|
||||
break;
|
||||
case PacketType.ProjectileSync:
|
||||
{
|
||||
Packets.ProjectileSync packet = new Packets.ProjectileSync();
|
||||
packet.Deserialize(data);
|
||||
ProjectileSync(packet);
|
||||
break;
|
||||
}
|
||||
#endregion
|
||||
ProjectileSync(data.GetPacket<Packets.ProjectileSync>());
|
||||
break;
|
||||
|
||||
case PacketType.ChatMessage:
|
||||
{
|
||||
|
||||
@ -284,10 +247,9 @@ namespace RageCoop.Client
|
||||
packet.Deserialize(data);
|
||||
|
||||
Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message); return true; });
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case PacketType.CustomEvent:
|
||||
{
|
||||
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
|
||||
@ -295,6 +257,7 @@ namespace RageCoop.Client
|
||||
Scripting.API.Events.InvokeCustomEventReceived(packet);
|
||||
}
|
||||
break;
|
||||
|
||||
case PacketType.CustomEventQueued:
|
||||
{
|
||||
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
|
||||
@ -305,6 +268,7 @@ namespace RageCoop.Client
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case PacketType.FileTransferChunk:
|
||||
{
|
||||
Packets.FileTransferChunk packet = new Packets.FileTransferChunk();
|
||||
@ -312,10 +276,11 @@ namespace RageCoop.Client
|
||||
DownloadManager.Write(packet.ID, packet.FileChunk);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (packetType.IsSyncEvent())
|
||||
{
|
||||
// Dispatch to main thread
|
||||
// Dispatch to script thread
|
||||
Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, data); return true; });
|
||||
}
|
||||
break;
|
||||
|
@ -108,7 +108,7 @@ namespace RageCoop.Client
|
||||
OwnerID=v.OwnerID,
|
||||
Flags = veh.GetVehicleFlags(),
|
||||
SteeringAngle = veh.SteeringAngle,
|
||||
Position = veh.PredictPosition(),
|
||||
Position = veh.Position,
|
||||
Velocity=veh.Velocity,
|
||||
Quaternion=veh.ReadQuaternion(),
|
||||
RotationVelocity=veh.RotationVelocity,
|
||||
@ -160,11 +160,11 @@ namespace RageCoop.Client
|
||||
ID =sp.ID,
|
||||
ShooterID=sp.ShooterID,
|
||||
Rotation=p.Rotation,
|
||||
Position=p.Position,
|
||||
Velocity=p.Velocity,
|
||||
WeaponHash=(uint)p.WeaponHash,
|
||||
Exploded=p.IsDead
|
||||
};
|
||||
packet.Position=p.Position+packet.Velocity*Latency;
|
||||
if (p.IsDead) { EntityPool.RemoveProjectile(sp.ID, "Dead"); }
|
||||
Send(packet, ConnectionChannel.ProjectileSync);
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ namespace RageCoop.Client
|
||||
|
||||
public static bool LeftAlign = true;
|
||||
public static Dictionary<int, Player> Players = new Dictionary<int, Player> { };
|
||||
public static Dictionary<string, Player> PendingConnections = new Dictionary<string, Player>();
|
||||
public static void Tick()
|
||||
{
|
||||
if (!Networking.IsOnServer)
|
||||
@ -138,6 +137,7 @@ namespace RageCoop.Client
|
||||
|
||||
internal class Player
|
||||
{
|
||||
public byte HolePunchStatus { get; set; } = 1;
|
||||
public string Username { get; internal set; }
|
||||
/// <summary>
|
||||
/// Universal character ID.
|
||||
@ -146,6 +146,9 @@ namespace RageCoop.Client
|
||||
{
|
||||
get; internal set;
|
||||
}
|
||||
public IPEndPoint InternalEndPoint { get; set; }
|
||||
public IPEndPoint ExternalEndPoint { get; set; }
|
||||
public bool ConnectWhenPunched { get; set; }
|
||||
public Blip FakeBlip { get; set; }
|
||||
public Vector3 Position { get; set; }
|
||||
public SyncedPed Character { get; set; }
|
||||
|
@ -37,6 +37,7 @@
|
||||
<Compile Include="Menus\Sub\UpdateMenu.cs" />
|
||||
<Compile Include="Networking\Chat.cs" />
|
||||
<Compile Include="Networking\DownloadManager.cs" />
|
||||
<Compile Include="Networking\HolePunch.cs" />
|
||||
<Compile Include="Networking\Networking.cs" />
|
||||
<Compile Include="Networking\Receive.cs" />
|
||||
<Compile Include="Networking\Send.cs" />
|
||||
|
@ -221,7 +221,8 @@ namespace RageCoop.Client
|
||||
new ScaledText(toDraw, Player.Username, 0.4f, GTA.UI.Font.ChaletLondon)
|
||||
{
|
||||
Outline = true,
|
||||
Alignment = GTA.UI.Alignment.Center
|
||||
Alignment = GTA.UI.Alignment.Center,
|
||||
Color=Owner.HasDirectConnection? Color.FromArgb(179, 229, 252) : Color.White,
|
||||
}.Draw();
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ namespace RageCoop.Client
|
||||
CreateProjectile();
|
||||
return;
|
||||
}
|
||||
MainProjectile.Velocity=Velocity+(Position+Networking.Latency*Velocity-MainProjectile.Position);
|
||||
MainProjectile.Velocity=Velocity+(Position+Shooter.Owner.PacketTravelTime*Velocity-MainProjectile.Position);
|
||||
MainProjectile.Rotation=Rotation;
|
||||
LastUpdated=Main.Ticked;
|
||||
}
|
||||
|
@ -353,23 +353,22 @@ namespace RageCoop.Client
|
||||
LastUpdated=Main.Ticked;
|
||||
}
|
||||
float _elapsed;
|
||||
Vector3 _predictedVel;
|
||||
Vector3 _predictedPos;
|
||||
void DisplayVehicle(bool touching)
|
||||
{
|
||||
// predict velocity/position
|
||||
_elapsed = Owner.PacketTravelTime+0.001f*LastSyncedStopWatch.ElapsedMilliseconds;
|
||||
// new LemonUI.Elements.ScaledText(new System.Drawing.PointF(50, 50), Owner.HasDirectConnection+" "+LastSyncedStopWatch.ElapsedMilliseconds).Draw();
|
||||
_predictedVel = Velocity+Acceleration*_elapsed;
|
||||
_predictedPos = Position+_elapsed*(LastVelocity+_predictedVel)/2;
|
||||
LastVelocity=_predictedVel;
|
||||
// _predictedVel = Velocity+Acceleration*_elapsed;
|
||||
_predictedPos = Position+_elapsed*Velocity;
|
||||
// LastVelocity=_predictedVel;
|
||||
var current = MainVehicle.ReadPosition();
|
||||
var dist = current.DistanceTo(Position);
|
||||
var cali = ((Velocity.Length()<0.1 && !touching)?dist*4:dist)*(_predictedPos - current);
|
||||
|
||||
if (dist<8)
|
||||
{
|
||||
MainVehicle.Velocity = _predictedVel;
|
||||
MainVehicle.Velocity = Velocity;
|
||||
MainVehicle.ApplyForce(cali);
|
||||
if (IsFlipped)
|
||||
{
|
||||
@ -393,7 +392,7 @@ namespace RageCoop.Client
|
||||
else
|
||||
{
|
||||
MainVehicle.Position=_predictedPos;
|
||||
MainVehicle.Velocity=_predictedVel;
|
||||
MainVehicle.Velocity=Velocity;
|
||||
MainVehicle.Quaternion=Quaternion;
|
||||
}
|
||||
}
|
||||
|
@ -161,10 +161,6 @@ namespace RageCoop.Client
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector3 PredictPosition(this Entity e)
|
||||
{
|
||||
return e.ReadPosition()+e.Velocity*(Networking.Latency);
|
||||
}
|
||||
|
||||
public static Vehicle CreateVehicle(Model model, Vector3 position, float heading = 0f)
|
||||
{
|
||||
|
@ -9,6 +9,7 @@ using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Lidgren.Network;
|
||||
|
||||
[assembly: InternalsVisibleTo("RageCoop.Server")]
|
||||
[assembly: InternalsVisibleTo("RageCoop.Client")]
|
||||
@ -259,6 +260,18 @@ namespace RageCoop.Core
|
||||
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()
|
||||
{
|
||||
msg.ReadByte();
|
||||
return GetPacket<T>(msg.ReadBytes(msg.ReadInt32()));
|
||||
}
|
||||
public static T GetPacket<T>(this byte[] data) where T : Packet, new()
|
||||
{
|
||||
T p = new T();
|
||||
p.Deserialize(data);
|
||||
return p;
|
||||
}
|
||||
public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag)
|
||||
{
|
||||
return (flagToCheck & flag)!=0;
|
||||
|
69
RageCoop.Core/Packets/HolePunch.cs
Normal file
69
RageCoop.Core/Packets/HolePunch.cs
Normal file
@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Lidgren.Network;
|
||||
|
||||
namespace RageCoop.Core
|
||||
{
|
||||
internal partial class Packets
|
||||
{
|
||||
|
||||
internal class HolePunchInit : Packet
|
||||
{
|
||||
public override PacketType Type => PacketType.HolePunchInit;
|
||||
public int TargetID { get; set; }
|
||||
public string TargetInternal { get; set; }
|
||||
public string TargetExternal { get; set; }
|
||||
public bool Connect { get; set; }
|
||||
public override byte[] Serialize()
|
||||
{
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
byteArray.AddInt(TargetID);
|
||||
byteArray.AddString(TargetInternal);
|
||||
byteArray.AddString(TargetExternal);
|
||||
byteArray.AddBool(Connect);
|
||||
return byteArray.ToArray();
|
||||
|
||||
}
|
||||
|
||||
public override void Deserialize(byte[] array)
|
||||
{
|
||||
#region NetIncomingMessageToPacket
|
||||
BitReader reader = new BitReader(array);
|
||||
TargetID = reader.ReadInt32();
|
||||
TargetInternal = reader.ReadString();
|
||||
TargetExternal = reader.ReadString();
|
||||
Connect=reader.ReadBoolean();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
internal class HolePunch : Packet
|
||||
{
|
||||
public override PacketType Type => PacketType.HolePunch;
|
||||
public int Puncher { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 1:initial, 2:acknowledged, 3:confirmed
|
||||
/// </summary>
|
||||
public byte Status { get;set;}
|
||||
public override byte[] Serialize()
|
||||
{
|
||||
|
||||
List<byte> byteArray = new List<byte>();
|
||||
byteArray.AddInt(Puncher);
|
||||
byteArray.Add(Status);
|
||||
return byteArray.ToArray();
|
||||
|
||||
}
|
||||
|
||||
public override void Deserialize(byte[] array)
|
||||
{
|
||||
#region NetIncomingMessageToPacket
|
||||
BitReader reader = new BitReader(array);
|
||||
Puncher = reader.ReadInt32();
|
||||
Status = reader.ReadByte();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -29,10 +29,10 @@ namespace RageCoop.Core
|
||||
/// <summary>
|
||||
/// Sent to the host when a direct connection has been established
|
||||
/// </summary>
|
||||
internal class ConnectionEstablished : Packet
|
||||
internal class P2PConnect : Packet
|
||||
{
|
||||
public int ID { get; set; }
|
||||
public override PacketType Type => PacketType.ConnectionEstablished;
|
||||
public override PacketType Type => PacketType.P2PConnect;
|
||||
public override byte[] Serialize()
|
||||
{
|
||||
var data = new List<byte>(10);
|
||||
|
@ -31,13 +31,13 @@ namespace RageCoop.Core
|
||||
CustomEventQueued = 17,
|
||||
|
||||
ConnectionRequest=18,
|
||||
ConnectionEstablished = 19,
|
||||
P2PConnect = 19,
|
||||
HolePunchInit=20,
|
||||
HolePunch=21,
|
||||
#region Sync
|
||||
|
||||
#region INTERVAL
|
||||
VehicleSync = 20,
|
||||
PedSync = 22,
|
||||
ProjectileSync=24,
|
||||
VehicleSync = 23,
|
||||
ProjectileSync =24,
|
||||
#endregion
|
||||
|
||||
#region EVENT
|
||||
@ -53,7 +53,6 @@ namespace RageCoop.Core
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
Unknown=255
|
||||
}
|
||||
internal static class PacketExtensions
|
||||
|
198
RageCoop.Server/Networking/Server.Connections.cs
Normal file
198
RageCoop.Server/Networking/Server.Connections.cs
Normal file
@ -0,0 +1,198 @@
|
||||
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(_compatibleVersion))
|
||||
{
|
||||
connection.Deny($"RAGECOOP version {_compatibleVersion.Replace('_', '.')}.x 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,
|
||||
}
|
||||
);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
92
RageCoop.Server/Networking/Server.EntitySync.cs
Normal file
92
RageCoop.Server/Networking/Server.EntitySync.cs
Normal file
@ -0,0 +1,92 @@
|
||||
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 PedSync(Packets.PedSync packet, Client client)
|
||||
{
|
||||
_worker.QueueJob(() => Entities.Update(packet, client));
|
||||
|
||||
bool isPlayer = packet.ID==client.Player.ID;
|
||||
if (isPlayer)
|
||||
{
|
||||
_worker.QueueJob(() => API.Events.InvokePlayerUpdate(client));
|
||||
}
|
||||
|
||||
if (Settings.UseP2P) { return; }
|
||||
foreach (var c in ClientsByNetHandle.Values)
|
||||
{
|
||||
|
||||
// Don't send data back
|
||||
if (c.NetHandle==client.NetHandle) { continue; }
|
||||
|
||||
// Check streaming distance
|
||||
if (isPlayer)
|
||||
{
|
||||
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if ((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
packet.Pack(outgoingMessage);
|
||||
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
|
||||
}
|
||||
}
|
||||
private void VehicleSync(Packets.VehicleSync packet, Client client)
|
||||
{
|
||||
_worker.QueueJob(() => Entities.Update(packet, client));
|
||||
bool isPlayer = packet.ID==client.Player?.LastVehicle?.ID;
|
||||
|
||||
|
||||
if (Settings.UseP2P) { return; }
|
||||
foreach (var c in ClientsByNetHandle.Values)
|
||||
{
|
||||
if (c.NetHandle==client.NetHandle) { continue; }
|
||||
if (isPlayer)
|
||||
{
|
||||
// Player's vehicle
|
||||
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
else if ((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
packet.Pack(outgoingMessage);
|
||||
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
|
||||
}
|
||||
}
|
||||
private void ProjectileSync(Packets.ProjectileSync packet, Client client)
|
||||
{
|
||||
|
||||
if (Settings.UseP2P) { return; }
|
||||
foreach (var c in ClientsByNetHandle.Values)
|
||||
{
|
||||
if (c.NetHandle==client.NetHandle) { continue; }
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
packet.Pack(outgoingMessage);
|
||||
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
36
RageCoop.Server/Networking/Server.HolePunch.cs
Normal file
36
RageCoop.Server/Networking/Server.HolePunch.cs
Normal file
@ -0,0 +1,36 @@
|
||||
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 HolePunch(Client host, Client client)
|
||||
{
|
||||
// Send to host
|
||||
Send(new Packets.HolePunchInit
|
||||
{
|
||||
Connect=false,
|
||||
TargetID=client.Player.ID,
|
||||
TargetInternal=client.InternalEndPoint.ToString(),
|
||||
TargetExternal=client.EndPoint.ToString()
|
||||
}, host, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
|
||||
|
||||
// Send to client
|
||||
Send(new Packets.HolePunchInit
|
||||
{
|
||||
Connect=true,
|
||||
TargetID=host.Player.ID,
|
||||
TargetInternal=host.InternalEndPoint.ToString(),
|
||||
TargetExternal=host.EndPoint.ToString()
|
||||
}, client, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ namespace RageCoop.Server
|
||||
/// <summary>
|
||||
/// The instantiable RageCoop server class
|
||||
/// </summary>
|
||||
public class Server
|
||||
public partial class Server
|
||||
{
|
||||
/// <summary>
|
||||
/// The API for controlling server and hooking events.
|
||||
@ -108,7 +108,7 @@ namespace RageCoop.Server
|
||||
Logger?.Error(ex);
|
||||
}
|
||||
}
|
||||
Thread.Sleep(5000);
|
||||
Thread.Sleep(1000);
|
||||
}
|
||||
});
|
||||
_announceThread=new Thread(async () =>
|
||||
@ -316,10 +316,7 @@ namespace RageCoop.Server
|
||||
{
|
||||
int len = message.ReadInt32();
|
||||
byte[] data = message.ReadBytes(len);
|
||||
|
||||
Packets.Handshake packet = new();
|
||||
packet.Deserialize(data);
|
||||
GetHandshake(message.SenderConnection, packet);
|
||||
GetHandshake(message.SenderConnection, data.GetPacket<Packets.Handshake>());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -457,33 +454,20 @@ namespace RageCoop.Server
|
||||
}
|
||||
private void HandlePacket(PacketType type,byte[] data,Client sender)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case PacketType.PedSync:
|
||||
{
|
||||
PedSync(data.GetPacket<Packets.PedSync>(), sender);
|
||||
break;
|
||||
|
||||
Packets.PedSync packet = new();
|
||||
packet.Deserialize(data);
|
||||
PedSync(packet, sender);
|
||||
}
|
||||
break;
|
||||
case PacketType.VehicleSync:
|
||||
{
|
||||
Packets.VehicleSync packet = new();
|
||||
packet.Deserialize(data);
|
||||
VehicleSync(packet, sender);
|
||||
}
|
||||
VehicleSync(data.GetPacket<Packets.VehicleSync>(), sender);
|
||||
break;
|
||||
|
||||
case PacketType.ProjectileSync:
|
||||
{
|
||||
Packets.ProjectileSync packet = new();
|
||||
packet.Deserialize(data);
|
||||
ProjectileSync(packet, sender);
|
||||
}
|
||||
ProjectileSync(data.GetPacket<Packets.ProjectileSync>(), sender);
|
||||
break;
|
||||
|
||||
case PacketType.ChatMessage:
|
||||
@ -496,6 +480,7 @@ namespace RageCoop.Server
|
||||
ChatMessageReceived(packet.Username,packet.Message, sender);
|
||||
}
|
||||
break;
|
||||
|
||||
case PacketType.CustomEvent:
|
||||
{
|
||||
Packets.CustomEvent packet = new Packets.CustomEvent();
|
||||
@ -503,19 +488,10 @@ namespace RageCoop.Server
|
||||
_worker.QueueJob(() => API.Events.InvokeCustomEventReceived(packet, sender));
|
||||
}
|
||||
break;
|
||||
case PacketType.ConnectionRequest:
|
||||
{
|
||||
var p=new Packets.ConnectionRequest();
|
||||
p.Deserialize(data);
|
||||
if (ClientsByID.TryGetValue(p.TargetID,out var target))
|
||||
{
|
||||
MainNetServer.Introduce(target.InternalEndPoint,target.EndPoint,sender.InternalEndPoint,sender.EndPoint,Guid.NewGuid().ToString());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Logger?.Error("Unhandled Data / Packet type");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -524,268 +500,6 @@ namespace RageCoop.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(_compatibleVersion))
|
||||
{
|
||||
connection.Deny($"RAGECOOP version {_compatibleVersion.Replace('_', '.')}.x 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,
|
||||
}
|
||||
);
|
||||
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; }
|
||||
MainNetServer.Introduce(target.InternalEndPoint, target.EndPoint, newClient.InternalEndPoint, newClient.EndPoint, target.Player.ID.ToString());
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#region -- SYNC --
|
||||
#region SyncEntities
|
||||
|
||||
private void PedSync(Packets.PedSync packet, Client client)
|
||||
{
|
||||
_worker.QueueJob(() => Entities.Update(packet, client));
|
||||
|
||||
bool isPlayer = packet.ID==client.Player.ID;
|
||||
if (isPlayer)
|
||||
{
|
||||
_worker.QueueJob(() => API.Events.InvokePlayerUpdate(client));
|
||||
}
|
||||
|
||||
if (Settings.UseP2P) { return; }
|
||||
foreach (var c in ClientsByNetHandle.Values)
|
||||
{
|
||||
|
||||
// Don't send data back
|
||||
if (c.NetHandle==client.NetHandle) { continue; }
|
||||
|
||||
// Check streaming distance
|
||||
if (isPlayer)
|
||||
{
|
||||
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if ((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
packet.Pack(outgoingMessage);
|
||||
MainNetServer.SendMessage(outgoingMessage,c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
|
||||
}
|
||||
}
|
||||
private void VehicleSync(Packets.VehicleSync packet, Client client)
|
||||
{
|
||||
_worker.QueueJob(() => Entities.Update(packet, client));
|
||||
bool isPlayer = packet.ID==client.Player?.LastVehicle?.ID;
|
||||
|
||||
|
||||
if (Settings.UseP2P) { return; }
|
||||
foreach (var c in ClientsByNetHandle.Values)
|
||||
{
|
||||
if (c.NetHandle==client.NetHandle) { continue; }
|
||||
if (isPlayer)
|
||||
{
|
||||
// Player's vehicle
|
||||
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
else if((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
packet.Pack(outgoingMessage);
|
||||
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
|
||||
}
|
||||
}
|
||||
private void ProjectileSync(Packets.ProjectileSync packet, Client client)
|
||||
{
|
||||
|
||||
if (Settings.UseP2P) { return; }
|
||||
foreach (var c in ClientsByNetHandle.Values)
|
||||
{
|
||||
if (c.NetHandle==client.NetHandle) { continue; }
|
||||
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
|
||||
packet.Pack(outgoingMessage);
|
||||
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
// Send a message to targets or all players
|
||||
internal void ChatMessageReceived(string name, string message,Client sender=null)
|
||||
{
|
||||
@ -831,8 +545,6 @@ namespace RageCoop.Server
|
||||
}.Pack(msg);
|
||||
MainNetServer.SendMessage(msg, target.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
internal void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback)
|
||||
{
|
@ -88,6 +88,6 @@
|
||||
/// <summary>
|
||||
/// Whether to use direct connection between players to send entity information
|
||||
/// </summary>
|
||||
public bool UseP2P { get; set; } = true;
|
||||
public bool UseP2P { get; set; } = false;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user