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;
}
}