P2P connectivity

This commit is contained in:
Sardelka 2022-08-08 17:03:41 +08:00
parent 09bb121ffe
commit c3d7c2b335
19 changed files with 313 additions and 149 deletions

View File

@ -41,11 +41,13 @@ namespace RageCoop.Client
internal static Vector3 PlayerPosition;
internal static Scripting.Resources Resources = null;
private static List<Func<bool>> QueuedActions = new List<Func<bool>>();
public static Worker Worker;
/// <summary>
/// Don't use it!
/// </summary>
public Main()
{
Worker = new Worker("RageCoop.Client.Main.Worker", Logger);
try
{
Settings = Util.ReadSettings();

View File

@ -11,7 +11,7 @@ namespace RageCoop.Client
internal static partial class Networking
{
public static NetPeer Peer;
public static float Latency = 0;
public static float Latency => ServerConnection.AverageRoundtripTime/2;
public static bool ShowNetworkInfo = false;
public static Security Security;
public static NetConnection ServerConnection;
@ -22,17 +22,21 @@ namespace RageCoop.Client
static Networking()
{
Security=new Security(Main.Logger);
RequestHandlers.Add(PacketType.PingPong, (b) =>
{
return new Packets.PingPong();
});
Task.Run(() =>
{
while (true)
{
if (Peer!=null)
{
ProcessMessage(Peer.WaitMessage(200));
try
{
ProcessMessage(Peer.WaitMessage(200));
}
catch(Exception ex)
{
Main.Logger.Error(ex);
}
}
else
{
@ -64,7 +68,9 @@ namespace RageCoop.Client
{
AutoFlushSendQueue = false,
SimulatedMinimumLatency =SimulatedLatency,
AcceptIncomingConnections = true
AcceptIncomingConnections = true,
MaximumConnections = 32,
PingInterval = 5
};
config.EnableMessageType(NetIncomingMessageType.UnconnectedData);
@ -110,7 +116,7 @@ namespace RageCoop.Client
Username =username,
ModVersion = Main.CurrentVersion,
PasswordEncrypted=Security.Encrypt(password.GetBytes()),
InternalEndPoint = (System.Net.IPEndPoint)Peer.Socket.LocalEndPoint
InternalEndPoint = new System.Net.IPEndPoint(CoreUtils.GetLocalAddress(ip[0]),Peer.Port)
};
Security.GetSymmetricKeysCrypted(out handshake.AesKeyCrypted, out handshake.AesIVCrypted);
@ -135,7 +141,7 @@ namespace RageCoop.Client
#region -- PLAYER --
private static void PlayerConnect(Packets.PlayerConnect packet)
{
var p = new PlayerData
var p = new Player
{
PedID = packet.PedID,
Username= packet.Username,

View File

@ -36,26 +36,51 @@ namespace RageCoop.Client
{
case NetIncomingMessageType.StatusChanged:
NetConnectionStatus status = (NetConnectionStatus)message.ReadByte();
string reason = message.ReadString();
switch (status)
{
case NetConnectionStatus.InitiatedConnect:
#if !NON_INTERACTIVE
CoopMenu.InitiateConnectionMenuSetting();
#endif
if (message.SenderConnection==ServerConnection)
{
CoopMenu.InitiateConnectionMenuSetting();
}
break;
case NetConnectionStatus.Connected:
Memory.ApplyPatches();
Main.QueueAction(() =>
if (message.SenderConnection==ServerConnection)
{
CoopMenu.ConnectedMenuSetting();
Main.MainChat.Init();
GTA.UI.Notification.Show("~g~Connected!");
});
Memory.ApplyPatches();
var response = message.SenderConnection.RemoteHailMessage;
if ((PacketType)response.ReadByte()!=PacketType.HandshakeSuccess)
{
throw new Exception("Invalid handshake response!");
}
var p = new Packets.HandshakeSuccess();
p.Deserialize(response.ReadBytes(response.ReadInt32()));
foreach(var player in p.Players)
{
PlayerList.SetPlayer(player.ID,player.Username);
}
Main.QueueAction(() =>
{
CoopMenu.ConnectedMenuSetting();
Main.MainChat.Init();
GTA.UI.Notification.Show("~g~Connected!");
});
Main.Logger.Info(">> Connected <<");
Main.Logger.Info(">> Connected <<");
}
else if (PlayerList.PendingConnections.TryGetValue(message.SenderEndPoint.ToString(),out var player))
{
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()
{
ID=Main.LocalPlayerID
}, player.Connection, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
Peer.FlushSendQueue();
}
break;
case NetConnectionStatus.Disconnected:
if (message.SenderConnection==ServerConnection)
@ -75,12 +100,25 @@ namespace RageCoop.Client
GTA.UI.Notification.Show("~r~Disconnected: " + reason));
Main.Resources.Unload();
}
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:
{
@ -123,7 +161,7 @@ namespace RageCoop.Client
{
byte[] data = message.ReadBytes(message.ReadInt32());
HandlePacket(packetType, data);
HandlePacket(packetType, data,message.SenderConnection);
break;
}
}
@ -160,6 +198,7 @@ namespace RageCoop.Client
case NetIncomingMessageType.ErrorMessage:
case NetIncomingMessageType.WarningMessage:
case NetIncomingMessageType.VerboseDebugMessage:
Main.Logger.Trace(message.ReadString());
break;
default:
break;
@ -167,11 +206,22 @@ namespace RageCoop.Client
Peer.Recycle(message);
}
private static void HandlePacket(PacketType packetType, byte[] data)
private static void HandlePacket(PacketType packetType, byte[] data, NetConnection senderConnection)
{
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.PlayerConnect:
{

View File

@ -4,6 +4,7 @@ using GTA.Native;
using Lidgren.Network;
using RageCoop.Core;
using System;
using System.Collections.Generic;
namespace RageCoop.Client
{
@ -11,18 +12,18 @@ namespace RageCoop.Client
{
public static int SyncInterval = 30;
#region -- SEND --
/// <summary>
/// Pack the packet then send to server.
/// </summary>
/// <param name="p"></param>
/// <param name="channel"></param>
/// <param name="method"></param>
public static List<NetConnection> Targets = new List<NetConnection>();
public static void Send(Packet p, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = Peer.CreateMessage();
p.Pack(outgoingMessage);
Peer.SendMessage(outgoingMessage,ServerConnection, method, (int)channel);
Peer.SendMessage(outgoingMessage,Targets, method, (int)channel);
}
public static void SendTo(Packet p,NetConnection connection, ConnectionChannel channel = ConnectionChannel.Default, NetDeliveryMethod method = NetDeliveryMethod.UnreliableSequenced)
{
NetOutgoingMessage outgoingMessage = Peer.CreateMessage();
p.Pack(outgoingMessage);
Peer.SendMessage(outgoingMessage, connection, method, (int)channel);
}
public static void SendPed(SyncedPed c, bool full)
@ -101,16 +102,16 @@ namespace RageCoop.Client
Flags = veh.GetVehicleFlags(),
SteeringAngle = veh.SteeringAngle,
Position = veh.PredictPosition(),
Velocity=veh.Velocity,
Quaternion=veh.ReadQuaternion(),
RotationVelocity=veh.RotationVelocity,
ThrottlePower = veh.ThrottlePower,
BrakePower = veh.BrakePower,
};
var velo = veh.Velocity;
if (v.LastVelocity==default) {v.LastVelocity=velo; }
packet.Acceleration = (velo-v.LastVelocity)*1000/v.LastSentStopWatch.ElapsedMilliseconds;
packet.Velocity=(v.LastVelocity = velo) + packet.Acceleration*Latency;
if (v.LastVelocity==default) {v.LastVelocity=packet.Velocity; }
packet.Acceleration = (packet.Velocity-v.LastVelocity)*1000/v.LastSentStopWatch.ElapsedMilliseconds;
v.LastSentStopWatch.Restart();
v.LastVelocity= packet.Velocity;
if (packet.Flags.HasVehFlag(VehicleDataFlags.IsDeluxoHovering)) { packet.DeluxoWingRatio=v.MainVehicle.GetDeluxoWingRatio(); }
if (full)
{
@ -185,10 +186,6 @@ namespace RageCoop.Client
Peer.SendMessage(outgoingMessage,ServerConnection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Chat);
Peer.FlushSendQueue();
#if DEBUG
#endif
}
#endregion
}
}

View File

@ -1,7 +1,10 @@
using GTA;
using GTA.Math;
using GTA.Native;
using RageCoop.Core;
using System.Collections.Generic;
using Lidgren.Network;
using System.Net;
namespace RageCoop.Client
{
@ -14,7 +17,8 @@ namespace RageCoop.Client
public static ulong Pressed { get; set; }
public static bool LeftAlign = true;
public static Dictionary<int, PlayerData> Players = new Dictionary<int, PlayerData> { };
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)
@ -24,7 +28,7 @@ namespace RageCoop.Client
if ((Util.GetTickCount64() - _lastUpdate) >= 1000)
{
Update(Main.Settings.Username);
Update();
}
if ((Util.GetTickCount64() - Pressed) < 5000 && !Main.MainChat.Focused
@ -40,7 +44,7 @@ namespace RageCoop.Client
}
}
private static void Update(string localUsername)
private static void Update()
{
_lastUpdate = Util.GetTickCount64();
@ -48,9 +52,9 @@ namespace RageCoop.Client
int i=0;
foreach (var player in Players)
foreach (var player in Players.Values)
{
_mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Value.Latency * 1000:N0}ms", player.Value.Username, 116, 0, i - 1, "", "", 2, "", "", ' ');
_mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{(player.PedID==Main.LocalPlayerID ? Networking.Latency : player.Latency) * 1000:N0}ms", player.Username, 116, 0, i - 1, "", "", 2, "", "", ' ');
}
_mainScaleform.CallFunction("SET_TITLE", "Player list", $"{Players.Count} players");
@ -58,37 +62,36 @@ namespace RageCoop.Client
}
public static void SetPlayer(int id, string username, float latency = 0)
{
if(id == Main.LocalPlayerID) { Networking.Latency=latency; }
Main.Logger.Debug($"{id},{username},{latency}");
PlayerData p;
Player p;
if (Players.TryGetValue(id, out p))
{
p.Username=username;
p.PedID=id;
p.Latency=latency;
p._latencyToServer=latency;
}
else
{
p = new PlayerData { PedID=id, Username=username, Latency=latency };
p = new Player { PedID=id, Username=username, _latencyToServer=latency };
Players.Add(id, p);
}
}
public static void UpdatePlayer(Packets.PlayerInfoUpdate packet)
{
if (packet.PedID == Main.LocalPlayerID) {Main.Logger.Debug("Latency updated"); Networking.Latency=packet.Latency; }
var p = GetPlayer(packet.PedID);
if (p!=null)
{
p.Latency= packet.Latency;
p._latencyToServer = packet.Latency;
p.Position = packet.Position;
}
}
public static PlayerData GetPlayer(int id)
public static Player GetPlayer(int id)
{
PlayerData p;
Player p;
Players.TryGetValue(id, out p);
return p;
}
public static PlayerData GetPlayer(SyncedPed p)
public static Player GetPlayer(SyncedPed p)
{
var player = GetPlayer(p.ID);
if (player!=null)
@ -106,13 +109,13 @@ namespace RageCoop.Client
}
public static void Cleanup()
{
Players=new Dictionary<int, PlayerData> { };
Players=new Dictionary<int, Player> { };
}
}
internal class PlayerData
internal class Player
{
public string Username { get; internal set; }
/// <summary>
@ -122,12 +125,16 @@ namespace RageCoop.Client
{
get; internal set;
}
public Vector3 Position { get; set; }
public SyncedPed Character { get; set; }
/// <summary>
/// Player Latency in second.
/// Player Latency in seconds, will be the latency to server if not using P2P connection.
/// </summary>
public float Latency { get; set; }
public float Latency => HasDirectConnection ? Connection.AverageRoundtripTime/2 : _latencyToServer;
public float PacketTravelTime => HasDirectConnection ? Connection.AverageRoundtripTime/2 : Networking.Latency+_latencyToServer;
public float _latencyToServer = 0;
public bool DisplayNameTag { get; set; } = true;
public NetConnection Connection { get; set; }
public bool HasDirectConnection => Connection?.Status==NetConnectionStatus.Connected;
}
}

View File

@ -285,7 +285,7 @@ namespace RageCoop.Client.Scripting
Args=args,
Hash=eventHash
};
Networking.Send(p, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
Networking.SendTo(p,Networking.ServerConnection, ConnectionChannel.Event, Lidgren.Network.NetDeliveryMethod.ReliableOrdered);
}
/// <summary>

View File

@ -25,10 +25,27 @@ namespace RageCoop.Client
/// Network ID for this entity
/// </summary>
public int ID { get; internal set; }
private int _ownerID;
/// <summary>
///
/// </summary>
public int OwnerID { get; internal set; }
public int OwnerID
{
get
{
return _ownerID;
}
internal set
{
if (value==_ownerID) { return; }
_ownerID = value;
Owner=PlayerList.GetPlayer(value);
}
}
internal Player Owner { get; private set; }
/// <summary>
///
/// </summary>

View File

@ -49,7 +49,7 @@ namespace RageCoop.Client
internal BlipColor BlipColor = (BlipColor)255;
internal BlipSprite BlipSprite = (BlipSprite)0;
internal float BlipScale = 1;
internal PlayerData Player;
internal Player Player;
#endregion
/// <summary>
@ -142,7 +142,7 @@ namespace RageCoop.Client
if (IsPlayer)
{
Main.Logger.Debug("blip:"+Player.Username);
// Main.Logger.Debug("blip:"+Player.Username);
Main.QueueAction(() => { PedBlip.Name=Player.Username; });
}
PedBlip.Color=BlipColor;
@ -236,7 +236,7 @@ namespace RageCoop.Client
{
if (MainPed.Exists())
{
Main.Logger.Debug($"Removing ped {ID}. Reason:CreateCharacter");
// Main.Logger.Debug($"Removing ped {ID}. Reason:CreateCharacter");
MainPed.Kill();
MainPed.MarkAsNoLongerNeeded();
MainPed.Delete();

View File

@ -358,14 +358,14 @@ namespace RageCoop.Client
void DisplayVehicle(bool touching)
{
// predict velocity/position
_elapsed = Networking.Latency+0.001f*LastSyncedStopWatch.ElapsedMilliseconds;
_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;
var current = MainVehicle.ReadPosition();
var dist = current.DistanceTo(Position);
var cali = ((Velocity.Length()<0.1 && !touching)?dist*4:dist)*(_predictedPos - current);
// new LemonUI.Elements.ScaledText(new System.Drawing.PointF(50, 50), dist.ToString()).Draw();
if (dist<8)
{

View File

@ -1,12 +1,11 @@

using GTA;
using GTA;
using GTA.Native;
using RageCoop.Client.Scripting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using Lidgren.Network;
namespace RageCoop.Client
{
@ -291,7 +290,7 @@ namespace RageCoop.Client
{
Handle_Projectiles.Remove(p.Handle);
}
Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}");
// Main.Logger.Debug($"Removing projectile {sp.ID}. Reason:{reason}");
p.Explode();
}
ID_Projectiles.Remove(id);
@ -322,6 +321,7 @@ namespace RageCoop.Client
public static void DoSync()
{
UpdateTargets();
#if BENCHMARK
PerfCounter.Restart();
Debug.TimeStamps[TimeStamp.CheckProjectiles]=PerfCounter.ElapsedTicks;
@ -560,11 +560,19 @@ namespace RageCoop.Client
Debug.TimeStamps[TimeStamp.VehicleTotal]=PerfCounter.ElapsedTicks;
#endif
}
ThreadPool.QueueUserWorkItem((o) => { Networking.Peer.FlushSendQueue(); });
Networking.Peer.FlushSendQueue();
}
static void UpdateTargets()
{
Networking.Targets=new List<NetConnection>(PlayerList.Players.Count) { Networking.ServerConnection };
foreach (var p in PlayerList.Players.Values.ToArray())
{
if (p.HasDirectConnection && p.Position.DistanceTo(Main.PlayerPosition)<500)
{
Networking.Targets.Add(p.Connection);
}
}
}
public static void RemoveAllFromPlayer(int playerPedId)
{

View File

@ -113,7 +113,7 @@ namespace RageCoop.Client
{
if (ped.Handle==Game.Player.Character.Handle) { continue; }
Main.Logger.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic");
// Main.Logger.Trace($"Removing ped {ped.Handle}. Reason:RemoveTraffic");
ped.CurrentVehicle?.Delete();
ped.Kill();
ped.Delete();
@ -131,7 +131,7 @@ namespace RageCoop.Client
}
if ((v== null) || (v.IsLocal&&veh.PopulationType!=EntityPopulationType.Mission))
{
Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");
// Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");
veh.Delete();
}

View File

@ -25,8 +25,7 @@ namespace RageCoop.Core
}
public override string ReadString()
{
string value = Encoding.UTF8.GetString(ReadBytes(ReadInt32()));
return value;
return Encoding.UTF8.GetString(ReadBytes(ReadInt32()));
}
public Vector3 ReadVector3()

View File

@ -6,6 +6,7 @@ using System.Threading.Tasks;
using GTA.Math;
using System.Security.Cryptography;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Runtime.CompilerServices;
@ -100,7 +101,7 @@ namespace RageCoop.Core
//try to use the address as IPv4, otherwise get hostname
if (!IPAddress.TryParse(values[0], out ipaddy))
ipaddy = getIPfromHost(values[0]);
ipaddy = GetIPfromHost(values[0]);
}
else if (values.Length > 2) //ipv6
{
@ -141,8 +142,16 @@ namespace RageCoop.Core
return port;
}
private static IPAddress getIPfromHost(string p)
public static IPAddress GetLocalAddress(string target= "8.8.8.8")
{
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
{
socket.Connect(target, 65530);
IPEndPoint endPoint = socket.LocalEndPoint as IPEndPoint;
return endPoint.Address;
}
}
private static IPAddress GetIPfromHost(string p)
{
var hosts = Dns.GetHostAddresses(p);

View File

@ -5,15 +5,6 @@ namespace RageCoop.Core
{
internal partial class Packets
{
/// <summary>
/// Used to measure the connection latency
/// </summary>
internal class PingPong : Packet
{
public override PacketType Type => PacketType.PingPong;
}
/// <summary>
/// Request direct connection to another client
/// </summary>
@ -33,5 +24,26 @@ namespace RageCoop.Core
TargetID = reader.ReadInt32();
}
}
/// <summary>
/// Sent to the host when a direct connection has been established
/// </summary>
internal class ConnectionEstablished : Packet
{
public int ID { get; set; }
public override PacketType Type => PacketType.ConnectionEstablished;
public override byte[] Serialize()
{
var data = new List<byte>(10);
data.AddInt(ID);
return data.ToArray();
}
public override void Deserialize(byte[] array)
{
var reader = new BitReader(array);
ID = reader.ReadInt32();
}
}
}
}

View File

@ -18,6 +18,7 @@ namespace RageCoop.Core
Request=6,
Response=7,
PingPong = 8,
HandshakeSuccess = 9,
ChatMessage =10,
FileTransferChunk=11,
@ -30,6 +31,7 @@ namespace RageCoop.Core
CustomEventQueued = 17,
ConnectionRequest=18,
ConnectionEstablished = 19,
#region Sync
#region INTERVAL

View File

@ -1,14 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using System.Net;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class Handshake : Packet
internal struct PlayerData
{
public int ID;
public string Username;
}
public class Handshake : Packet
{
public override PacketType Type => PacketType.Handshake;
public int PedID { get; set; }
@ -92,7 +97,35 @@ namespace RageCoop.Core
#endregion
}
}
public class HandshakeSuccess : Packet
{
public PlayerData[] Players { get; set; }
public override PacketType Type => PacketType.HandshakeSuccess;
public override byte[] Serialize()
{
var data = new List<byte>();
data.AddInt(Players.Length);
foreach(var p in Players)
{
data.AddInt(p.ID);
data.AddString(p.Username);
}
return data.ToArray();
}
public override void Deserialize(byte[] array)
{
var reader = new BitReader(array);
Players=new PlayerData[reader.ReadInt32()];
for(int i = 0; i<Players.Length; i++)
{
Players[i]=new PlayerData()
{
ID=reader.ReadInt32(),
Username=reader.ReadString(),
};
}
}
}
public class PlayerConnect : Packet
{
public override PacketType Type => PacketType.PlayerConnect;
@ -168,6 +201,7 @@ namespace RageCoop.Core
public int PedID { get; set; }
public string Username { get; set; }
public float Latency { get; set; }
public Vector3 Position { get; set; }
public override byte[] Serialize()
{
@ -176,19 +210,14 @@ namespace RageCoop.Core
// Write ID
byteArray.AddRange(BitConverter.GetBytes(PedID));
// Get Username bytes
byte[] usernameBytes = Encoding.UTF8.GetBytes(Username);
// Write UsernameLength
byteArray.AddRange(BitConverter.GetBytes(usernameBytes.Length));
// Write Username
byteArray.AddRange(usernameBytes);
byteArray.AddString(Username);
// Write Latency
byteArray.AddFloat(Latency);
byteArray.AddVector3(Position);
return byteArray.ToArray();
}
@ -203,6 +232,8 @@ namespace RageCoop.Core
Username = reader.ReadString();
Latency=reader.ReadSingle();
Position=reader.ReadVector3();
}
}

View File

@ -39,7 +39,7 @@ namespace RageCoop.Server
/// <summary>
/// The client's latency in seconds.
/// </summary>
public float Latency { get; internal set; }
public float Latency => Connection.AverageRoundtripTime/2;
internal readonly Dictionary<int, Action<object>> Callbacks = new();
internal byte[] PublicKey { get; set; }
/// <summary>
@ -80,21 +80,6 @@ namespace RageCoop.Server
_displayNameTag=value;
}
}
internal void UpdateLatency()
{
_latencyWatch.Restart();
Server.GetResponse<Packets.PingPong>(this, new Packets.PingPong(), ConnectionChannel.PingPong);
_latencyWatch.Stop();
Latency = (float)_latencyWatch.ElapsedMilliseconds/2000;
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
PedID=Player.ID,
Username=Username,
Latency=Latency,
}.Pack(outgoingMessage);
Server.MainNetServer.SendToAll(outgoingMessage, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
}
#region FUNCTIONS
/// <summary>
/// Kick this client

View File

@ -91,9 +91,24 @@ namespace RageCoop.Server
{
foreach(var c in ClientsByNetHandle.Values.ToArray())
{
c.UpdateLatency();
try
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
PedID=c.Player.ID,
Username=c.Username,
Latency=c.Latency,
Position=c.Player.Position
}.Pack(outgoingMessage);
MainNetServer.SendToAll(outgoingMessage, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
}
catch(Exception ex)
{
Logger?.Error(ex);
}
}
Thread.Sleep(3000);
Thread.Sleep(5000);
}
});
_announceThread=new Thread(async () =>
@ -217,7 +232,8 @@ namespace RageCoop.Server
Port = Settings.Port,
MaximumConnections = Settings.MaxPlayers,
EnableUPnP = false,
AutoFlushSendQueue = true
AutoFlushSendQueue = true,
PingInterval=5
};
config.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
@ -375,6 +391,8 @@ namespace RageCoop.Server
if (type.IsSyncEvent())
{
// Sync Events
if (Settings.UseP2P) { break; }
try
{
var toSend = MainNetServer.Connections.Exclude(message.SenderConnection);
@ -563,8 +581,22 @@ namespace RageCoop.Server
connection.Deny("Malformed handshak packet!");
return;
}
connection.Approve();
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
@ -604,27 +636,6 @@ namespace RageCoop.Server
API.SendCustomEvent(new() { newClient },CustomEvents.IsHost, true);
}
// Send other players to this client
ClientsByNetHandle.Values.ForEach(target =>
{
if (target==newClient) { return; }
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerConnect()
{
// NetHandle = targetNetHandle,
Username = target.Username,
PedID=target.Player.ID,
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, newClient.Connection, 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});
// Send new client to all players
var cons = MainNetServer.Connections.Exclude(newClient.Connection);
if (cons.Count!=0)
@ -636,10 +647,28 @@ namespace RageCoop.Server
Username = newClient.Username
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage,cons , NetDeliveryMethod.ReliableOrdered, 0);
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))
@ -690,6 +719,7 @@ namespace RageCoop.Server
_worker.QueueJob(() => API.Events.InvokePlayerUpdate(client));
}
if (Settings.UseP2P) { return; }
foreach (var c in ClientsByNetHandle.Values)
{
@ -718,6 +748,9 @@ namespace RageCoop.Server
{
_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; }
@ -742,6 +775,7 @@ namespace RageCoop.Server
private void ProjectileSync(Packets.ProjectileSync packet, Client client)
{
if (Settings.UseP2P) { return; }
foreach (var c in ClientsByNetHandle.Values)
{
if (c.NetHandle==client.NetHandle) { continue; }

View File

@ -84,5 +84,10 @@
/// List of all allowed username characters
/// </summary>
public string AllowedUsernameChars { get; set; } = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_";
/// <summary>
/// Whether to use direct connection between players to send entity information
/// </summary>
public bool UseP2P { get; set; } = true;
}
}