diff --git a/RageCoop.Client/Main.cs b/RageCoop.Client/Main.cs index ba77d11..c959359 100644 --- a/RageCoop.Client/Main.cs +++ b/RageCoop.Client/Main.cs @@ -41,11 +41,13 @@ namespace RageCoop.Client internal static Vector3 PlayerPosition; internal static Scripting.Resources Resources = null; private static List> QueuedActions = new List>(); + public static Worker Worker; /// /// Don't use it! /// public Main() { + Worker = new Worker("RageCoop.Client.Main.Worker", Logger); try { Settings = Util.ReadSettings(); diff --git a/RageCoop.Client/Networking/Networking.cs b/RageCoop.Client/Networking/Networking.cs index 3055982..3a7847b 100644 --- a/RageCoop.Client/Networking/Networking.cs +++ b/RageCoop.Client/Networking/Networking.cs @@ -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, diff --git a/RageCoop.Client/Networking/Receive.cs b/RageCoop.Client/Networking/Receive.cs index b109d49..5ec77cf 100644 --- a/RageCoop.Client/Networking/Receive.cs +++ b/RageCoop.Client/Networking/Receive.cs @@ -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!"); - }); - - Main.Logger.Info(">> 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 <<"); + } + 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: { diff --git a/RageCoop.Client/Networking/Send.cs b/RageCoop.Client/Networking/Send.cs index b827f8d..87d229c 100644 --- a/RageCoop.Client/Networking/Send.cs +++ b/RageCoop.Client/Networking/Send.cs @@ -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 -- - /// - /// Pack the packet then send to server. - /// - /// - /// - /// + public static List Targets = new List(); 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 } } diff --git a/RageCoop.Client/PlayerList.cs b/RageCoop.Client/PlayerList.cs index 2c05d59..4f4ae34 100644 --- a/RageCoop.Client/PlayerList.cs +++ b/RageCoop.Client/PlayerList.cs @@ -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 Players = new Dictionary { }; + public static Dictionary Players = new Dictionary { }; + public static Dictionary PendingConnections = new Dictionary(); 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 { }; + Players=new Dictionary { }; } } - internal class PlayerData + internal class Player { public string Username { get; internal set; } /// @@ -122,12 +125,16 @@ namespace RageCoop.Client { get; internal set; } + public Vector3 Position { get; set; } public SyncedPed Character { get; set; } /// - /// Player Latency in second. + /// Player Latency in seconds, will be the latency to server if not using P2P connection. /// - 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; } } diff --git a/RageCoop.Client/Scripting/API.cs b/RageCoop.Client/Scripting/API.cs index 3af77cb..86b2005 100644 --- a/RageCoop.Client/Scripting/API.cs +++ b/RageCoop.Client/Scripting/API.cs @@ -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); } /// diff --git a/RageCoop.Client/Sync/Entities/SyncedEntity.cs b/RageCoop.Client/Sync/Entities/SyncedEntity.cs index 4570458..d968714 100644 --- a/RageCoop.Client/Sync/Entities/SyncedEntity.cs +++ b/RageCoop.Client/Sync/Entities/SyncedEntity.cs @@ -25,10 +25,27 @@ namespace RageCoop.Client /// Network ID for this entity /// public int ID { get; internal set; } + + + private int _ownerID; /// /// /// - 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; } /// /// /// diff --git a/RageCoop.Client/Sync/Entities/SyncedPed.cs b/RageCoop.Client/Sync/Entities/SyncedPed.cs index bb02472..b0e5c41 100644 --- a/RageCoop.Client/Sync/Entities/SyncedPed.cs +++ b/RageCoop.Client/Sync/Entities/SyncedPed.cs @@ -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 /// @@ -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(); diff --git a/RageCoop.Client/Sync/Entities/SyncedVehicle.cs b/RageCoop.Client/Sync/Entities/SyncedVehicle.cs index 250d391..0718713 100644 --- a/RageCoop.Client/Sync/Entities/SyncedVehicle.cs +++ b/RageCoop.Client/Sync/Entities/SyncedVehicle.cs @@ -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) { diff --git a/RageCoop.Client/Sync/EntityPool.cs b/RageCoop.Client/Sync/EntityPool.cs index d5e0435..66e7143 100644 --- a/RageCoop.Client/Sync/EntityPool.cs +++ b/RageCoop.Client/Sync/EntityPool.cs @@ -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(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) { diff --git a/RageCoop.Client/WorldThread.cs b/RageCoop.Client/WorldThread.cs index b2f734c..16e59ea 100644 --- a/RageCoop.Client/WorldThread.cs +++ b/RageCoop.Client/WorldThread.cs @@ -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(); } diff --git a/RageCoop.Core/BitReader.cs b/RageCoop.Core/BitReader.cs index 43874e5..aeb2941 100644 --- a/RageCoop.Core/BitReader.cs +++ b/RageCoop.Core/BitReader.cs @@ -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() diff --git a/RageCoop.Core/CoreUtils.cs b/RageCoop.Core/CoreUtils.cs index 7e7064d..84e82d3 100644 --- a/RageCoop.Core/CoreUtils.cs +++ b/RageCoop.Core/CoreUtils.cs @@ -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); diff --git a/RageCoop.Core/Packets/Misc.cs b/RageCoop.Core/Packets/Misc.cs index a54c636..c36c7e2 100644 --- a/RageCoop.Core/Packets/Misc.cs +++ b/RageCoop.Core/Packets/Misc.cs @@ -5,15 +5,6 @@ namespace RageCoop.Core { internal partial class Packets { - - /// - /// Used to measure the connection latency - /// - internal class PingPong : Packet - { - public override PacketType Type => PacketType.PingPong; - } - /// /// Request direct connection to another client /// @@ -33,5 +24,26 @@ namespace RageCoop.Core TargetID = reader.ReadInt32(); } } + + + /// + /// Sent to the host when a direct connection has been established + /// + internal class ConnectionEstablished : Packet + { + public int ID { get; set; } + public override PacketType Type => PacketType.ConnectionEstablished; + public override byte[] Serialize() + { + var data = new List(10); + data.AddInt(ID); + return data.ToArray(); + } + public override void Deserialize(byte[] array) + { + var reader = new BitReader(array); + ID = reader.ReadInt32(); + } + } } } diff --git a/RageCoop.Core/Packets/Packets.cs b/RageCoop.Core/Packets/Packets.cs index 068e1fc..4d2e677 100644 --- a/RageCoop.Core/Packets/Packets.cs +++ b/RageCoop.Core/Packets/Packets.cs @@ -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 diff --git a/RageCoop.Core/Packets/PlayerPackets.cs b/RageCoop.Core/Packets/PlayerPackets.cs index eb286da..0d6ba89 100644 --- a/RageCoop.Core/Packets/PlayerPackets.cs +++ b/RageCoop.Core/Packets/PlayerPackets.cs @@ -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(); + 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 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(); } } diff --git a/RageCoop.Server/Client.cs b/RageCoop.Server/Client.cs index 264276d..bc20288 100644 --- a/RageCoop.Server/Client.cs +++ b/RageCoop.Server/Client.cs @@ -39,7 +39,7 @@ namespace RageCoop.Server /// /// The client's latency in seconds. /// - public float Latency { get; internal set; } + public float Latency => Connection.AverageRoundtripTime/2; internal readonly Dictionary> Callbacks = new(); internal byte[] PublicKey { get; set; } /// @@ -80,21 +80,6 @@ namespace RageCoop.Server _displayNameTag=value; } } - internal void UpdateLatency() - { - _latencyWatch.Restart(); - Server.GetResponse(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 /// /// Kick this client diff --git a/RageCoop.Server/Server.cs b/RageCoop.Server/Server.cs index 81c14c4..650e162 100644 --- a/RageCoop.Server/Server.cs +++ b/RageCoop.Server/Server.cs @@ -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 - { - 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)) @@ -689,7 +718,8 @@ 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; } diff --git a/RageCoop.Server/ServerSettings.cs b/RageCoop.Server/ServerSettings.cs index 3ec9ce3..1774571 100644 --- a/RageCoop.Server/ServerSettings.cs +++ b/RageCoop.Server/ServerSettings.cs @@ -84,5 +84,10 @@ /// List of all allowed username characters /// public string AllowedUsernameChars { get; set; } = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_"; + + /// + /// Whether to use direct connection between players to send entity information + /// + public bool UseP2P { get; set; } = true; } }