From ff9e16bd5e551c682ebb3491e06d9035f21131fb Mon Sep 17 00:00:00 2001 From: Sardelka Date: Wed, 10 Aug 2022 20:42:47 +0800 Subject: [PATCH] Restore --- .github/FUNDING.yml | 13 + README.md | 6 +- RageCoop.Client/Networking/HolePunch.cs | 85 +++++ RageCoop.Client/Networking/Networking.cs | 7 +- RageCoop.Client/Networking/Receive.cs | 139 +++----- RageCoop.Client/Networking/Send.cs | 4 +- RageCoop.Client/PlayerList.cs | 5 +- RageCoop.Client/RageCoop.Client.csproj | 1 + RageCoop.Client/Sync/Entities/SyncedPed.cs | 3 +- .../Sync/Entities/SyncedProjectile.cs | 2 +- .../Sync/Entities/SyncedVehicle.cs | 11 +- RageCoop.Client/Util/Util.cs | 4 - RageCoop.Core/CoreUtils.cs | 13 + RageCoop.Core/Packets/HolePunch.cs | 69 ++++ RageCoop.Core/Packets/Misc.cs | 4 +- RageCoop.Core/Packets/Packets.cs | 11 +- .../Networking/Server.Connections.cs | 198 +++++++++++ .../Networking/Server.EntitySync.cs | 92 ++++++ .../Networking/Server.HolePunch.cs | 36 ++ RageCoop.Server/{ => Networking}/Server.cs | 308 +----------------- RageCoop.Server/ServerSettings.cs | 2 +- 21 files changed, 598 insertions(+), 415 deletions(-) create mode 100644 .github/FUNDING.yml create mode 100644 RageCoop.Client/Networking/HolePunch.cs create mode 100644 RageCoop.Core/Packets/HolePunch.cs create mode 100644 RageCoop.Server/Networking/Server.Connections.cs create mode 100644 RageCoop.Server/Networking/Server.EntitySync.cs create mode 100644 RageCoop.Server/Networking/Server.HolePunch.cs rename RageCoop.Server/{ => Networking}/Server.cs (70%) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..c3973a3 --- /dev/null +++ b/.github/FUNDING.yml @@ -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'] diff --git a/README.md b/README.md index 186743c..0a709b0 100644 --- a/README.md +++ b/README.md @@ -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 - - # 🦆 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 diff --git a/RageCoop.Client/Networking/HolePunch.cs b/RageCoop.Client/Networking/HolePunch.cs new file mode 100644 index 0000000..58e6cf3 --- /dev/null +++ b/RageCoop.Client/Networking/HolePunch.cs @@ -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 { 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(); + } + } + } + } + } +} diff --git a/RageCoop.Client/Networking/Networking.cs b/RageCoop.Client/Networking/Networking.cs index 3a7847b..1f00e3a 100644 --- a/RageCoop.Client/Networking/Networking.cs +++ b/RageCoop.Client/Networking/Networking.cs @@ -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 -- diff --git a/RageCoop.Client/Networking/Receive.cs b/RageCoop.Client/Networking/Receive.cs index 2210465..e99dd4b 100644 --- a/RageCoop.Client/Networking/Receive.cs +++ b/RageCoop.Client/Networking/Receive.cs @@ -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(); + 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(), message.SenderEndPoint); + break; + } + case PacketType.PublicKeyResponse: + { + + var packet = data.GetPacket(); + 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()); + break; + case PacketType.PlayerConnect: - { - - Packets.PlayerConnect packet = new Packets.PlayerConnect(); - packet.Deserialize(data); - - Main.QueueAction(() => PlayerConnect(packet)); - } + PlayerConnect(data.GetPacket()); break; + case PacketType.PlayerDisconnect: - { - - Packets.PlayerDisconnect packet = new Packets.PlayerDisconnect(); - packet.Deserialize(data); - Main.QueueAction(() => PlayerDisconnect(packet)); - - } + PlayerDisconnect(data.GetPacket()); 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()); break; + + case PacketType.VehicleSync: + VehicleSync(data.GetPacket()); + break; + case PacketType.PedSync: - { - - Packets.PedSync packet = new Packets.PedSync(); - packet.Deserialize(data); - PedSync(packet); - - } + PedSync(data.GetPacket()); break; case PacketType.ProjectileSync: - { - Packets.ProjectileSync packet = new Packets.ProjectileSync(); - packet.Deserialize(data); - ProjectileSync(packet); - break; - } - #endregion + ProjectileSync(data.GetPacket()); + 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; diff --git a/RageCoop.Client/Networking/Send.cs b/RageCoop.Client/Networking/Send.cs index ce7100c..b33d964 100644 --- a/RageCoop.Client/Networking/Send.cs +++ b/RageCoop.Client/Networking/Send.cs @@ -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); } diff --git a/RageCoop.Client/PlayerList.cs b/RageCoop.Client/PlayerList.cs index 5fafeff..fc428a5 100644 --- a/RageCoop.Client/PlayerList.cs +++ b/RageCoop.Client/PlayerList.cs @@ -18,7 +18,6 @@ namespace RageCoop.Client public static bool LeftAlign = true; public static Dictionary Players = new Dictionary { }; - public static Dictionary PendingConnections = new Dictionary(); 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; } /// /// 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; } diff --git a/RageCoop.Client/RageCoop.Client.csproj b/RageCoop.Client/RageCoop.Client.csproj index feba17b..54342ea 100644 --- a/RageCoop.Client/RageCoop.Client.csproj +++ b/RageCoop.Client/RageCoop.Client.csproj @@ -37,6 +37,7 @@ + diff --git a/RageCoop.Client/Sync/Entities/SyncedPed.cs b/RageCoop.Client/Sync/Entities/SyncedPed.cs index 3ac3ff2..c2c8054 100644 --- a/RageCoop.Client/Sync/Entities/SyncedPed.cs +++ b/RageCoop.Client/Sync/Entities/SyncedPed.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(); } diff --git a/RageCoop.Client/Sync/Entities/SyncedProjectile.cs b/RageCoop.Client/Sync/Entities/SyncedProjectile.cs index db366dc..a67833f 100644 --- a/RageCoop.Client/Sync/Entities/SyncedProjectile.cs +++ b/RageCoop.Client/Sync/Entities/SyncedProjectile.cs @@ -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; } diff --git a/RageCoop.Client/Sync/Entities/SyncedVehicle.cs b/RageCoop.Client/Sync/Entities/SyncedVehicle.cs index 0718713..a3a5e64 100644 --- a/RageCoop.Client/Sync/Entities/SyncedVehicle.cs +++ b/RageCoop.Client/Sync/Entities/SyncedVehicle.cs @@ -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; } } diff --git a/RageCoop.Client/Util/Util.cs b/RageCoop.Client/Util/Util.cs index 2c82254..44d69c5 100644 --- a/RageCoop.Client/Util/Util.cs +++ b/RageCoop.Client/Util/Util.cs @@ -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) { diff --git a/RageCoop.Core/CoreUtils.cs b/RageCoop.Core/CoreUtils.cs index 84e82d3..3c2369a 100644 --- a/RageCoop.Core/CoreUtils.cs +++ b/RageCoop.Core/CoreUtils.cs @@ -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() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4); } + + public static T GetPacket(this NetIncomingMessage msg) where T : Packet, new() + { + msg.ReadByte(); + return GetPacket(msg.ReadBytes(msg.ReadInt32())); + } + public static T GetPacket(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; diff --git a/RageCoop.Core/Packets/HolePunch.cs b/RageCoop.Core/Packets/HolePunch.cs new file mode 100644 index 0000000..6fb9d7c --- /dev/null +++ b/RageCoop.Core/Packets/HolePunch.cs @@ -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 byteArray = new List(); + 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; } + + /// + /// 1:initial, 2:acknowledged, 3:confirmed + /// + public byte Status { get;set;} + public override byte[] Serialize() + { + + List byteArray = new List(); + 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 + } + } + } +} diff --git a/RageCoop.Core/Packets/Misc.cs b/RageCoop.Core/Packets/Misc.cs index c36c7e2..60aee1a 100644 --- a/RageCoop.Core/Packets/Misc.cs +++ b/RageCoop.Core/Packets/Misc.cs @@ -29,10 +29,10 @@ namespace RageCoop.Core /// /// Sent to the host when a direct connection has been established /// - 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(10); diff --git a/RageCoop.Core/Packets/Packets.cs b/RageCoop.Core/Packets/Packets.cs index 4d2e677..8c9e45f 100644 --- a/RageCoop.Core/Packets/Packets.cs +++ b/RageCoop.Core/Packets/Packets.cs @@ -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 diff --git a/RageCoop.Server/Networking/Server.Connections.cs b/RageCoop.Server/Networking/Server.Connections.cs new file mode 100644 index 0000000..73eecc1 --- /dev/null +++ b/RageCoop.Server/Networking/Server.Connections.cs @@ -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 + { + 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); + } + } +} diff --git a/RageCoop.Server/Networking/Server.EntitySync.cs b/RageCoop.Server/Networking/Server.EntitySync.cs new file mode 100644 index 0000000..f0782e7 --- /dev/null +++ b/RageCoop.Server/Networking/Server.EntitySync.cs @@ -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); + } + } + + } +} diff --git a/RageCoop.Server/Networking/Server.HolePunch.cs b/RageCoop.Server/Networking/Server.HolePunch.cs new file mode 100644 index 0000000..ad98512 --- /dev/null +++ b/RageCoop.Server/Networking/Server.HolePunch.cs @@ -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); + } + } +} diff --git a/RageCoop.Server/Server.cs b/RageCoop.Server/Networking/Server.cs similarity index 70% rename from RageCoop.Server/Server.cs rename to RageCoop.Server/Networking/Server.cs index 650e162..9026526 100644 --- a/RageCoop.Server/Server.cs +++ b/RageCoop.Server/Networking/Server.cs @@ -32,7 +32,7 @@ namespace RageCoop.Server /// /// The instantiable RageCoop server class /// - public class Server + public partial class Server { /// /// 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()); } 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(), 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(), sender); break; + case PacketType.ProjectileSync: - { - Packets.ProjectileSync packet = new(); - packet.Deserialize(data); - ProjectileSync(packet, sender); - } + ProjectileSync(data.GetPacket(), 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 - { - 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 callback) { diff --git a/RageCoop.Server/ServerSettings.cs b/RageCoop.Server/ServerSettings.cs index 1774571..059a3e4 100644 --- a/RageCoop.Server/ServerSettings.cs +++ b/RageCoop.Server/ServerSettings.cs @@ -88,6 +88,6 @@ /// /// Whether to use direct connection between players to send entity information /// - public bool UseP2P { get; set; } = true; + public bool UseP2P { get; set; } = false; } }