This commit is contained in:
Sardelka
2022-08-10 20:42:47 +08:00
parent 01c668b159
commit ff9e16bd5e
21 changed files with 598 additions and 415 deletions

13
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# These are supported funding model platforms
github: [RAGECOOP] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
#patreon: # Replace with a single Patreon username
#open_collective: # Replace with a single Open Collective username
#ko_fi: # Replace with a single Ko-fi username
#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
#liberapay: # Replace with a single Liberapay username
#issuehunt: # Replace with a single IssueHunt username
#otechie: # Replace with a single Otechie username
#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: ['https://www.buymeacoffee.com/xEntenKoeniqx', 'https://patreon.com/Sardelka']

View File

@ -1,6 +1,7 @@
# 🌐 RAGECOOP # 🌐 RAGECOOP
[![Downloads][downloads-shield]][downloads-url]
[![Contributors][contributors-shield]][contributors-url] [![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url] [![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-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. Please note that this is incompatible with all previous versions of ragecoop, remove old files before installing.
# Support us
<a href="https://patreon.com/Sardelka"><img src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3DSardelka%26type%3Dpatrons&style=for-the-badge" /></a>
# 🦆 Special thanks to # 🦆 Special thanks to
- [Makinolo](https://github.com/Makinolo), [oldnapalm](https://github.com/oldnapalm) - [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 # 📝 License
This project is licensed under [MIT license](https://github.com/RAGECOOP/RAGECOOP-V/blob/main/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-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 [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 [forks-shield]: https://img.shields.io/github/forks/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge

View File

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using RageCoop.Core;
using Lidgren.Network;
using System.Net;
namespace RageCoop.Client
{
internal static partial class HolePunch
{
static HolePunch()
{
// Periodically send hole punch message as needed
var timer=new Timer(1000);
timer.Elapsed+=DoPunch;
timer.Enabled=true;
}
private static void DoPunch(object sender, ElapsedEventArgs e)
{
try
{
if (!Networking.IsOnServer) { return; }
foreach (var p in PlayerList.Players.Values.ToArray())
{
if (p.InternalEndPoint!=null && p.ExternalEndPoint!=null && (p.Connection==null || p.Connection.Status==NetConnectionStatus.Disconnected))
{
Main.Logger.Trace($"Sending HolePunch message to {p.InternalEndPoint},{p.ExternalEndPoint}. {p.Username}:{p.PedID}");
var msg = Networking.Peer.CreateMessage();
new Packets.HolePunch
{
Puncher=Main.LocalPlayerID,
Status=p.HolePunchStatus
}.Pack(msg);
Networking.Peer.SendUnconnectedMessage(msg, new List<IPEndPoint> { p.InternalEndPoint, p.ExternalEndPoint });
}
}
}
catch (Exception ex)
{
Main.Logger.Error(ex);
}
}
public static void Add(Packets.HolePunchInit p)
{
if(PlayerList.Players.TryGetValue(p.TargetID,out var player))
{
Main.Logger.Debug($"{p.TargetID},{player.Username} added to HolePunch target");
player.InternalEndPoint = CoreUtils.StringToEndPoint(p.TargetInternal);
player.ExternalEndPoint = CoreUtils.StringToEndPoint(p.TargetExternal);
player.ConnectWhenPunched=p.Connect;
}
else
{
Main.Logger.Warning("No player with specified TargetID found for hole punching:"+p.TargetID);
}
}
public static void Punched(Packets.HolePunch p,IPEndPoint from)
{
Main.Logger.Debug($"HolePunch message received from:{from}, status:{p.Status}");
if(PlayerList.Players.TryGetValue(p.Puncher,out var puncher))
{
Main.Logger.Debug("Puncher identified as: "+puncher.Username);
puncher.HolePunchStatus=(byte)(p.Status+1);
if (p.Status>=3)
{
Main.Logger.Debug("HolePunch sucess: "+from+", "+puncher.PedID);
if (puncher.ConnectWhenPunched && (puncher.Connection==null || puncher.Connection.Status==NetConnectionStatus.Disconnected))
{
Main.Logger.Debug("Connecting to peer: "+from);
var msg = Networking.Peer.CreateMessage();
new Packets.P2PConnect { ID=Main.LocalPlayerID }.Pack(msg);
puncher.Connection=Networking.Peer.Connect(from,msg);
Networking.Peer.FlushSendQueue();
}
}
}
}
}
}

View File

@ -146,19 +146,20 @@ namespace RageCoop.Client
PedID = packet.PedID, PedID = packet.PedID,
Username= packet.Username, Username= packet.Username,
}; };
GTA.UI.Notification.Show($"~h~{p.Username}~h~ connected.");
PlayerList.SetPlayer(packet.PedID, packet.Username); PlayerList.SetPlayer(packet.PedID, packet.Username);
Main.Logger.Debug($"player connected:{p.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) private static void PlayerDisconnect(Packets.PlayerDisconnect packet)
{ {
var name = PlayerList.GetPlayer(packet.PedID).Username; var name = PlayerList.GetPlayer(packet.PedID).Username;
GTA.UI.Notification.Show($"~h~{name}~h~ left.");
PlayerList.RemovePlayer(packet.PedID); PlayerList.RemovePlayer(packet.PedID);
EntityPool.RemoveAllFromPlayer(packet.PedID); EntityPool.RemoveAllFromPlayer(packet.PedID);
Main.QueueAction(() =>
GTA.UI.Notification.Show($"~h~{name}~h~ left."));
} }
#endregion // -- PLAYER -- #endregion // -- PLAYER --

View File

@ -70,16 +70,22 @@ namespace RageCoop.Client
Main.Logger.Info(">> Connected <<"); Main.Logger.Info(">> Connected <<");
} }
else if (PlayerList.PendingConnections.TryGetValue(message.SenderEndPoint.ToString(),out var player)) else
{ {
PlayerList.PendingConnections.Remove(message.SenderEndPoint.ToString()); // Self-initiated connection
Main.Logger.Debug($"Connection to {player.Username},{player.PedID},{player.Connection.Status} established, sending ID..."); if (message.SenderConnection.RemoteHailMessage==null) { return; }
// Inform target our ID
SendTo(new Packets.ConnectionEstablished() var p = message.SenderConnection.RemoteHailMessage.GetPacket<Packets.P2PConnect>();
if (PlayerList.Players.TryGetValue(p.ID,out var player))
{ {
ID=Main.LocalPlayerID player.Connection=message.SenderConnection;
}, player.Connection, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered); Main.Logger.Debug($"Direct connectionn to {player.Username} established");
Peer.FlushSendQueue(); }
else
{
Main.Logger.Info($"Unidentified peer connection from {message.SenderEndPoint} was rejected.");
message.SenderConnection.Disconnect("eat poop");
}
} }
break; break;
case NetConnectionStatus.Disconnected: case NetConnectionStatus.Disconnected:
@ -103,22 +109,6 @@ namespace RageCoop.Client
break; break;
} }
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: case NetIncomingMessageType.Data:
{ {
@ -184,14 +174,23 @@ namespace RageCoop.Client
var packetType = (PacketType)message.ReadByte(); var packetType = (PacketType)message.ReadByte();
int len = message.ReadInt32(); int len = message.ReadInt32();
byte[] data = message.ReadBytes(len); byte[] data = message.ReadBytes(len);
if (packetType==PacketType.PublicKeyResponse) switch (packetType)
{ {
var packet = new Packets.PublicKeyResponse();
packet.Deserialize(data); case PacketType.HolePunch:
{
HolePunch.Punched(data.GetPacket<Packets.HolePunch>(), message.SenderEndPoint);
break;
}
case PacketType.PublicKeyResponse:
{
var packet = data.GetPacket<Packets.PublicKeyResponse>();
Security.SetServerPublicKey(packet.Modulus, packet.Exponent); Security.SetServerPublicKey(packet.Modulus, packet.Exponent);
_publicKeyReceived.Set(); _publicKeyReceived.Set();
break;
}
} }
break; break;
} }
case NetIncomingMessageType.DebugMessage: case NetIncomingMessageType.DebugMessage:
@ -211,69 +210,33 @@ namespace RageCoop.Client
switch (packetType) switch (packetType)
{ {
case PacketType.ConnectionEstablished: case PacketType.HolePunchInit:
{ HolePunch.Add(data.GetPacket<Packets.HolePunchInit>());
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; break;
}
case PacketType.PlayerConnect: case PacketType.PlayerConnect:
{ PlayerConnect(data.GetPacket<Packets.PlayerConnect>());
Packets.PlayerConnect packet = new Packets.PlayerConnect();
packet.Deserialize(data);
Main.QueueAction(() => PlayerConnect(packet));
}
break; break;
case PacketType.PlayerDisconnect: case PacketType.PlayerDisconnect:
{ PlayerDisconnect(data.GetPacket<Packets.PlayerDisconnect>());
Packets.PlayerDisconnect packet = new Packets.PlayerDisconnect();
packet.Deserialize(data);
Main.QueueAction(() => PlayerDisconnect(packet));
}
break; break;
case PacketType.PlayerInfoUpdate: case PacketType.PlayerInfoUpdate:
{ PlayerList.UpdatePlayer(data.GetPacket<Packets.PlayerInfoUpdate>());
var packet = new Packets.PlayerInfoUpdate();
packet.Deserialize(data);
PlayerList.UpdatePlayer(packet);
break; break;
}
#region ENTITY SYNC
case PacketType.VehicleSync: case PacketType.VehicleSync:
{ VehicleSync(data.GetPacket<Packets.VehicleSync>());
Packets.VehicleSync packet = new Packets.VehicleSync();
packet.Deserialize(data);
VehicleSync(packet);
}
break; break;
case PacketType.PedSync: case PacketType.PedSync:
{ PedSync(data.GetPacket<Packets.PedSync>());
Packets.PedSync packet = new Packets.PedSync();
packet.Deserialize(data);
PedSync(packet);
}
break; break;
case PacketType.ProjectileSync: case PacketType.ProjectileSync:
{ ProjectileSync(data.GetPacket<Packets.ProjectileSync>());
Packets.ProjectileSync packet = new Packets.ProjectileSync();
packet.Deserialize(data);
ProjectileSync(packet);
break; break;
}
#endregion
case PacketType.ChatMessage: case PacketType.ChatMessage:
{ {
@ -284,10 +247,9 @@ namespace RageCoop.Client
packet.Deserialize(data); packet.Deserialize(data);
Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message); return true; }); Main.QueueAction(() => { Main.MainChat.AddMessage(packet.Username, packet.Message); return true; });
} }
break; break;
case PacketType.CustomEvent: case PacketType.CustomEvent:
{ {
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle); Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
@ -295,6 +257,7 @@ namespace RageCoop.Client
Scripting.API.Events.InvokeCustomEventReceived(packet); Scripting.API.Events.InvokeCustomEventReceived(packet);
} }
break; break;
case PacketType.CustomEventQueued: case PacketType.CustomEventQueued:
{ {
Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle); Packets.CustomEvent packet = new Packets.CustomEvent(_resolveHandle);
@ -305,6 +268,7 @@ namespace RageCoop.Client
}); });
} }
break; break;
case PacketType.FileTransferChunk: case PacketType.FileTransferChunk:
{ {
Packets.FileTransferChunk packet = new Packets.FileTransferChunk(); Packets.FileTransferChunk packet = new Packets.FileTransferChunk();
@ -312,10 +276,11 @@ namespace RageCoop.Client
DownloadManager.Write(packet.ID, packet.FileChunk); DownloadManager.Write(packet.ID, packet.FileChunk);
} }
break; break;
default: default:
if (packetType.IsSyncEvent()) if (packetType.IsSyncEvent())
{ {
// Dispatch to main thread // Dispatch to script thread
Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, data); return true; }); Main.QueueAction(() => { SyncEvents.HandleEvent(packetType, data); return true; });
} }
break; break;

View File

@ -108,7 +108,7 @@ namespace RageCoop.Client
OwnerID=v.OwnerID, OwnerID=v.OwnerID,
Flags = veh.GetVehicleFlags(), Flags = veh.GetVehicleFlags(),
SteeringAngle = veh.SteeringAngle, SteeringAngle = veh.SteeringAngle,
Position = veh.PredictPosition(), Position = veh.Position,
Velocity=veh.Velocity, Velocity=veh.Velocity,
Quaternion=veh.ReadQuaternion(), Quaternion=veh.ReadQuaternion(),
RotationVelocity=veh.RotationVelocity, RotationVelocity=veh.RotationVelocity,
@ -160,11 +160,11 @@ namespace RageCoop.Client
ID =sp.ID, ID =sp.ID,
ShooterID=sp.ShooterID, ShooterID=sp.ShooterID,
Rotation=p.Rotation, Rotation=p.Rotation,
Position=p.Position,
Velocity=p.Velocity, Velocity=p.Velocity,
WeaponHash=(uint)p.WeaponHash, WeaponHash=(uint)p.WeaponHash,
Exploded=p.IsDead Exploded=p.IsDead
}; };
packet.Position=p.Position+packet.Velocity*Latency;
if (p.IsDead) { EntityPool.RemoveProjectile(sp.ID, "Dead"); } if (p.IsDead) { EntityPool.RemoveProjectile(sp.ID, "Dead"); }
Send(packet, ConnectionChannel.ProjectileSync); Send(packet, ConnectionChannel.ProjectileSync);
} }

View File

@ -18,7 +18,6 @@ namespace RageCoop.Client
public static bool LeftAlign = true; public static bool LeftAlign = true;
public static Dictionary<int, Player> Players = new Dictionary<int, Player> { }; public static Dictionary<int, Player> Players = new Dictionary<int, Player> { };
public static Dictionary<string, Player> PendingConnections = new Dictionary<string, Player>();
public static void Tick() public static void Tick()
{ {
if (!Networking.IsOnServer) if (!Networking.IsOnServer)
@ -138,6 +137,7 @@ namespace RageCoop.Client
internal class Player internal class Player
{ {
public byte HolePunchStatus { get; set; } = 1;
public string Username { get; internal set; } public string Username { get; internal set; }
/// <summary> /// <summary>
/// Universal character ID. /// Universal character ID.
@ -146,6 +146,9 @@ namespace RageCoop.Client
{ {
get; internal set; get; internal set;
} }
public IPEndPoint InternalEndPoint { get; set; }
public IPEndPoint ExternalEndPoint { get; set; }
public bool ConnectWhenPunched { get; set; }
public Blip FakeBlip { get; set; } public Blip FakeBlip { get; set; }
public Vector3 Position { get; set; } public Vector3 Position { get; set; }
public SyncedPed Character { get; set; } public SyncedPed Character { get; set; }

View File

@ -37,6 +37,7 @@
<Compile Include="Menus\Sub\UpdateMenu.cs" /> <Compile Include="Menus\Sub\UpdateMenu.cs" />
<Compile Include="Networking\Chat.cs" /> <Compile Include="Networking\Chat.cs" />
<Compile Include="Networking\DownloadManager.cs" /> <Compile Include="Networking\DownloadManager.cs" />
<Compile Include="Networking\HolePunch.cs" />
<Compile Include="Networking\Networking.cs" /> <Compile Include="Networking\Networking.cs" />
<Compile Include="Networking\Receive.cs" /> <Compile Include="Networking\Receive.cs" />
<Compile Include="Networking\Send.cs" /> <Compile Include="Networking\Send.cs" />

View File

@ -221,7 +221,8 @@ namespace RageCoop.Client
new ScaledText(toDraw, Player.Username, 0.4f, GTA.UI.Font.ChaletLondon) new ScaledText(toDraw, Player.Username, 0.4f, GTA.UI.Font.ChaletLondon)
{ {
Outline = true, Outline = true,
Alignment = GTA.UI.Alignment.Center Alignment = GTA.UI.Alignment.Center,
Color=Owner.HasDirectConnection? Color.FromArgb(179, 229, 252) : Color.White,
}.Draw(); }.Draw();
} }

View File

@ -67,7 +67,7 @@ namespace RageCoop.Client
CreateProjectile(); CreateProjectile();
return; return;
} }
MainProjectile.Velocity=Velocity+(Position+Networking.Latency*Velocity-MainProjectile.Position); MainProjectile.Velocity=Velocity+(Position+Shooter.Owner.PacketTravelTime*Velocity-MainProjectile.Position);
MainProjectile.Rotation=Rotation; MainProjectile.Rotation=Rotation;
LastUpdated=Main.Ticked; LastUpdated=Main.Ticked;
} }

View File

@ -353,23 +353,22 @@ namespace RageCoop.Client
LastUpdated=Main.Ticked; LastUpdated=Main.Ticked;
} }
float _elapsed; float _elapsed;
Vector3 _predictedVel;
Vector3 _predictedPos; Vector3 _predictedPos;
void DisplayVehicle(bool touching) void DisplayVehicle(bool touching)
{ {
// predict velocity/position // predict velocity/position
_elapsed = Owner.PacketTravelTime+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(); // new LemonUI.Elements.ScaledText(new System.Drawing.PointF(50, 50), Owner.HasDirectConnection+" "+LastSyncedStopWatch.ElapsedMilliseconds).Draw();
_predictedVel = Velocity+Acceleration*_elapsed; // _predictedVel = Velocity+Acceleration*_elapsed;
_predictedPos = Position+_elapsed*(LastVelocity+_predictedVel)/2; _predictedPos = Position+_elapsed*Velocity;
LastVelocity=_predictedVel; // LastVelocity=_predictedVel;
var current = MainVehicle.ReadPosition(); var current = MainVehicle.ReadPosition();
var dist = current.DistanceTo(Position); var dist = current.DistanceTo(Position);
var cali = ((Velocity.Length()<0.1 && !touching)?dist*4:dist)*(_predictedPos - current); var cali = ((Velocity.Length()<0.1 && !touching)?dist*4:dist)*(_predictedPos - current);
if (dist<8) if (dist<8)
{ {
MainVehicle.Velocity = _predictedVel; MainVehicle.Velocity = Velocity;
MainVehicle.ApplyForce(cali); MainVehicle.ApplyForce(cali);
if (IsFlipped) if (IsFlipped)
{ {
@ -393,7 +392,7 @@ namespace RageCoop.Client
else else
{ {
MainVehicle.Position=_predictedPos; MainVehicle.Position=_predictedPos;
MainVehicle.Velocity=_predictedVel; MainVehicle.Velocity=Velocity;
MainVehicle.Quaternion=Quaternion; MainVehicle.Quaternion=Quaternion;
} }
} }

View File

@ -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) public static Vehicle CreateVehicle(Model model, Vector3 position, float heading = 0f)
{ {

View File

@ -9,6 +9,7 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Lidgren.Network;
[assembly: InternalsVisibleTo("RageCoop.Server")] [assembly: InternalsVisibleTo("RageCoop.Server")]
[assembly: InternalsVisibleTo("RageCoop.Client")] [assembly: InternalsVisibleTo("RageCoop.Client")]
@ -259,6 +260,18 @@ namespace RageCoop.Core
return new List<byte[]>() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4); return new List<byte[]>() { BitConverter.GetBytes(qua.X), BitConverter.GetBytes(qua.Y), BitConverter.GetBytes(qua.Z), BitConverter.GetBytes(qua.W) }.Join(4);
} }
public static T GetPacket<T>(this NetIncomingMessage msg) where T : Packet, new()
{
msg.ReadByte();
return GetPacket<T>(msg.ReadBytes(msg.ReadInt32()));
}
public static T GetPacket<T>(this byte[] data) where T : Packet, new()
{
T p = new T();
p.Deserialize(data);
return p;
}
public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag) public static bool HasPedFlag(this PedDataFlags flagToCheck, PedDataFlags flag)
{ {
return (flagToCheck & flag)!=0; return (flagToCheck & flag)!=0;

View File

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using Lidgren.Network;
namespace RageCoop.Core
{
internal partial class Packets
{
internal class HolePunchInit : Packet
{
public override PacketType Type => PacketType.HolePunchInit;
public int TargetID { get; set; }
public string TargetInternal { get; set; }
public string TargetExternal { get; set; }
public bool Connect { get; set; }
public override byte[] Serialize()
{
List<byte> byteArray = new List<byte>();
byteArray.AddInt(TargetID);
byteArray.AddString(TargetInternal);
byteArray.AddString(TargetExternal);
byteArray.AddBool(Connect);
return byteArray.ToArray();
}
public override void Deserialize(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
TargetID = reader.ReadInt32();
TargetInternal = reader.ReadString();
TargetExternal = reader.ReadString();
Connect=reader.ReadBoolean();
#endregion
}
}
internal class HolePunch : Packet
{
public override PacketType Type => PacketType.HolePunch;
public int Puncher { get; set; }
/// <summary>
/// 1:initial, 2:acknowledged, 3:confirmed
/// </summary>
public byte Status { get;set;}
public override byte[] Serialize()
{
List<byte> byteArray = new List<byte>();
byteArray.AddInt(Puncher);
byteArray.Add(Status);
return byteArray.ToArray();
}
public override void Deserialize(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
Puncher = reader.ReadInt32();
Status = reader.ReadByte();
#endregion
}
}
}
}

View File

@ -29,10 +29,10 @@ namespace RageCoop.Core
/// <summary> /// <summary>
/// Sent to the host when a direct connection has been established /// Sent to the host when a direct connection has been established
/// </summary> /// </summary>
internal class ConnectionEstablished : Packet internal class P2PConnect : Packet
{ {
public int ID { get; set; } public int ID { get; set; }
public override PacketType Type => PacketType.ConnectionEstablished; public override PacketType Type => PacketType.P2PConnect;
public override byte[] Serialize() public override byte[] Serialize()
{ {
var data = new List<byte>(10); var data = new List<byte>(10);

View File

@ -31,12 +31,12 @@ namespace RageCoop.Core
CustomEventQueued = 17, CustomEventQueued = 17,
ConnectionRequest=18, ConnectionRequest=18,
ConnectionEstablished = 19, P2PConnect = 19,
HolePunchInit=20,
HolePunch=21,
#region Sync #region Sync
#region INTERVAL
VehicleSync = 20,
PedSync = 22, PedSync = 22,
VehicleSync = 23,
ProjectileSync =24, ProjectileSync =24,
#endregion #endregion
@ -53,7 +53,6 @@ namespace RageCoop.Core
#endregion #endregion
#endregion
Unknown=255 Unknown=255
} }
internal static class PacketExtensions internal static class PacketExtensions

View File

@ -0,0 +1,198 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
namespace RageCoop.Server
{
public partial class Server
{
private void DisconnectAndLog(NetConnection senderConnection, PacketType type, Exception e)
{
Logger?.Error($"Error receiving a packet of type {type}");
Logger?.Error(e.Message);
Logger?.Error(e.StackTrace);
senderConnection.Disconnect(e.Message);
}
private void GetHandshake(NetConnection connection, Packets.Handshake packet)
{
Logger?.Debug("New handshake from: [Name: " + packet.Username + " | Address: " + connection.RemoteEndPoint.Address.ToString() + "]");
if (!packet.ModVersion.StartsWith(_compatibleVersion))
{
connection.Deny($"RAGECOOP version {_compatibleVersion.Replace('_', '.')}.x required!");
return;
}
if (string.IsNullOrWhiteSpace(packet.Username))
{
connection.Deny("Username is empty or contains spaces!");
return;
}
if (packet.Username.Any(p => !_allowedCharacterSet.Contains(p)))
{
connection.Deny("Username contains special chars!");
return;
}
if (ClientsByNetHandle.Values.Any(x => x.Username.ToLower() == packet.Username.ToLower()))
{
connection.Deny("Username is already taken!");
return;
}
try
{
Security.AddConnection(connection.RemoteEndPoint, packet.AesKeyCrypted, packet.AesIVCrypted);
var args = new HandshakeEventArgs()
{
EndPoint=connection.RemoteEndPoint,
ID=packet.PedID,
Username=packet.Username,
PasswordHash=Security.Decrypt(packet.PasswordEncrypted, connection.RemoteEndPoint).GetString().GetSHA256Hash().ToHexString(),
};
API.Events.InvokePlayerHandshake(args);
if (args.Cancel)
{
connection.Deny(args.DenyReason);
return;
}
}
catch (Exception ex)
{
Logger?.Error($"Cannot process handshake packet from {connection.RemoteEndPoint}");
Logger?.Error(ex);
connection.Deny("Malformed handshak packet!");
return;
}
var handshakeSuccess = MainNetServer.CreateMessage();
var currentClients = ClientsByID.Values.ToArray();
var players = new Packets.PlayerData[currentClients.Length];
for (int i = 0; i<players.Length; i++)
{
players[i]=new Packets.PlayerData()
{
ID=currentClients[i].Player.ID,
Username=currentClients[i].Username,
};
}
new Packets.HandshakeSuccess()
{
Players=players
}.Pack(handshakeSuccess);
connection.Approve(handshakeSuccess);
Client tmpClient;
// Add the player to Players
lock (ClientsByNetHandle)
{
var player = new ServerPed(this)
{
ID= packet.PedID,
};
Entities.Add(player);
ClientsByNetHandle.Add(connection.RemoteUniqueIdentifier,
tmpClient = new Client(this)
{
NetHandle = connection.RemoteUniqueIdentifier,
Connection=connection,
Username=packet.Username,
Player = player,
InternalEndPoint=packet.InternalEndPoint,
}
);
ClientsByName.Add(packet.Username.ToLower(), tmpClient);
ClientsByID.Add(player.ID, tmpClient);
if (ClientsByNetHandle.Count==1)
{
_hostClient=tmpClient;
}
}
Logger?.Debug($"Handshake sucess, Player:{packet.Username} PedID:{packet.PedID}");
}
// The connection has been approved, now we need to send all other players to the new player and the new player to all players
private void PlayerConnected(Client newClient)
{
if (newClient==_hostClient)
{
API.SendCustomEvent(new() { newClient }, CustomEvents.IsHost, true);
}
// Send new client to all players
var cons = MainNetServer.Connections.Exclude(newClient.Connection);
if (cons.Count!=0)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerConnect()
{
PedID=newClient.Player.ID,
Username = newClient.Username
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, cons, NetDeliveryMethod.ReliableOrdered, 0);
}
// Send all props to this player
BaseScript.SendServerPropsTo(new(Entities.ServerProps.Values), new() { newClient });
// Send all blips to this player
BaseScript.SendServerBlipsTo(new(Entities.Blips.Values), new() { newClient });
// Create P2P connection
if (Settings.UseP2P)
{
ClientsByNetHandle.Values.ForEach(target =>
{
if (target==newClient) { return; }
HolePunch(target,newClient);
});
}
Logger?.Info($"Player {newClient.Username} connected!");
if (!string.IsNullOrEmpty(Settings.WelcomeMessage))
{
SendChatMessage("Server", Settings.WelcomeMessage, newClient);
}
}
// Send all players a message that someone has left the server
private void PlayerDisconnected(Client localClient)
{
var cons = MainNetServer.Connections.Exclude(localClient.Connection);
if (cons.Count!=0)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerDisconnect()
{
PedID=localClient.Player.ID,
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, cons, NetDeliveryMethod.ReliableOrdered, 0);
}
Entities.CleanUp(localClient);
_worker.QueueJob(() => API.Events.InvokePlayerDisconnected(localClient));
Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.Player.ID}");
if (ClientsByNetHandle.ContainsKey(localClient.NetHandle)) { ClientsByNetHandle.Remove(localClient.NetHandle); }
if (ClientsByName.ContainsKey(localClient.Username.ToLower())) { ClientsByName.Remove(localClient.Username.ToLower()); }
if (ClientsByID.ContainsKey(localClient.Player.ID)) { ClientsByID.Remove(localClient.Player.ID); }
if (localClient==_hostClient)
{
_hostClient = ClientsByNetHandle.Values.FirstOrDefault();
_hostClient?.SendCustomEvent(CustomEvents.IsHost, true);
}
Security.RemoveConnection(localClient.Connection.RemoteEndPoint);
}
}
}

View File

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
namespace RageCoop.Server
{
public partial class Server
{
private void PedSync(Packets.PedSync packet, Client client)
{
_worker.QueueJob(() => Entities.Update(packet, client));
bool isPlayer = packet.ID==client.Player.ID;
if (isPlayer)
{
_worker.QueueJob(() => API.Events.InvokePlayerUpdate(client));
}
if (Settings.UseP2P) { return; }
foreach (var c in ClientsByNetHandle.Values)
{
// Don't send data back
if (c.NetHandle==client.NetHandle) { continue; }
// Check streaming distance
if (isPlayer)
{
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
{
continue;
}
}
else if ((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
{
continue;
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
}
private void VehicleSync(Packets.VehicleSync packet, Client client)
{
_worker.QueueJob(() => Entities.Update(packet, client));
bool isPlayer = packet.ID==client.Player?.LastVehicle?.ID;
if (Settings.UseP2P) { return; }
foreach (var c in ClientsByNetHandle.Values)
{
if (c.NetHandle==client.NetHandle) { continue; }
if (isPlayer)
{
// Player's vehicle
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
{
continue;
}
}
else if ((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
{
continue;
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
}
private void ProjectileSync(Packets.ProjectileSync packet, Client client)
{
if (Settings.UseP2P) { return; }
foreach (var c in ClientsByNetHandle.Values)
{
if (c.NetHandle==client.NetHandle) { continue; }
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
}
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using RageCoop.Server.Scripting;
namespace RageCoop.Server
{
public partial class Server
{
private void HolePunch(Client host, Client client)
{
// Send to host
Send(new Packets.HolePunchInit
{
Connect=false,
TargetID=client.Player.ID,
TargetInternal=client.InternalEndPoint.ToString(),
TargetExternal=client.EndPoint.ToString()
}, host, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
// Send to client
Send(new Packets.HolePunchInit
{
Connect=true,
TargetID=host.Player.ID,
TargetInternal=host.InternalEndPoint.ToString(),
TargetExternal=host.EndPoint.ToString()
}, client, ConnectionChannel.Default, NetDeliveryMethod.ReliableOrdered);
}
}
}

View File

@ -32,7 +32,7 @@ namespace RageCoop.Server
/// <summary> /// <summary>
/// The instantiable RageCoop server class /// The instantiable RageCoop server class
/// </summary> /// </summary>
public class Server public partial class Server
{ {
/// <summary> /// <summary>
/// The API for controlling server and hooking events. /// The API for controlling server and hooking events.
@ -108,7 +108,7 @@ namespace RageCoop.Server
Logger?.Error(ex); Logger?.Error(ex);
} }
} }
Thread.Sleep(5000); Thread.Sleep(1000);
} }
}); });
_announceThread=new Thread(async () => _announceThread=new Thread(async () =>
@ -316,10 +316,7 @@ namespace RageCoop.Server
{ {
int len = message.ReadInt32(); int len = message.ReadInt32();
byte[] data = message.ReadBytes(len); byte[] data = message.ReadBytes(len);
GetHandshake(message.SenderConnection, data.GetPacket<Packets.Handshake>());
Packets.Handshake packet = new();
packet.Deserialize(data);
GetHandshake(message.SenderConnection, packet);
} }
catch (Exception e) catch (Exception e)
{ {
@ -457,33 +454,20 @@ namespace RageCoop.Server
} }
private void HandlePacket(PacketType type,byte[] data,Client sender) private void HandlePacket(PacketType type,byte[] data,Client sender)
{ {
try try
{ {
switch (type) switch (type)
{ {
case PacketType.PedSync: case PacketType.PedSync:
{ PedSync(data.GetPacket<Packets.PedSync>(), sender);
break;
Packets.PedSync packet = new();
packet.Deserialize(data);
PedSync(packet, sender);
}
break;
case PacketType.VehicleSync: case PacketType.VehicleSync:
{ VehicleSync(data.GetPacket<Packets.VehicleSync>(), sender);
Packets.VehicleSync packet = new();
packet.Deserialize(data);
VehicleSync(packet, sender);
}
break; break;
case PacketType.ProjectileSync: case PacketType.ProjectileSync:
{ ProjectileSync(data.GetPacket<Packets.ProjectileSync>(), sender);
Packets.ProjectileSync packet = new();
packet.Deserialize(data);
ProjectileSync(packet, sender);
}
break; break;
case PacketType.ChatMessage: case PacketType.ChatMessage:
@ -496,6 +480,7 @@ namespace RageCoop.Server
ChatMessageReceived(packet.Username,packet.Message, sender); ChatMessageReceived(packet.Username,packet.Message, sender);
} }
break; break;
case PacketType.CustomEvent: case PacketType.CustomEvent:
{ {
Packets.CustomEvent packet = new Packets.CustomEvent(); Packets.CustomEvent packet = new Packets.CustomEvent();
@ -503,19 +488,10 @@ namespace RageCoop.Server
_worker.QueueJob(() => API.Events.InvokeCustomEventReceived(packet, sender)); _worker.QueueJob(() => API.Events.InvokeCustomEventReceived(packet, sender));
} }
break; 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: default:
Logger?.Error("Unhandled Data / Packet type"); Logger?.Error("Unhandled Data / Packet type");
break; break;
} }
} }
catch (Exception e) catch (Exception e)
@ -524,268 +500,6 @@ namespace RageCoop.Server
} }
} }
private void DisconnectAndLog(NetConnection senderConnection,PacketType type, Exception e)
{
Logger?.Error($"Error receiving a packet of type {type}");
Logger?.Error(e.Message);
Logger?.Error(e.StackTrace);
senderConnection.Disconnect(e.Message);
}
private void GetHandshake(NetConnection connection, Packets.Handshake packet)
{
Logger?.Debug("New handshake from: [Name: " + packet.Username + " | Address: " + connection.RemoteEndPoint.Address.ToString() + "]");
if (!packet.ModVersion.StartsWith(_compatibleVersion))
{
connection.Deny($"RAGECOOP version {_compatibleVersion.Replace('_', '.')}.x required!");
return;
}
if (string.IsNullOrWhiteSpace(packet.Username))
{
connection.Deny("Username is empty or contains spaces!");
return;
}
if (packet.Username.Any(p => !_allowedCharacterSet.Contains(p)))
{
connection.Deny("Username contains special chars!");
return;
}
if (ClientsByNetHandle.Values.Any(x => x.Username.ToLower() == packet.Username.ToLower()))
{
connection.Deny("Username is already taken!");
return;
}
try
{
Security.AddConnection(connection.RemoteEndPoint, packet.AesKeyCrypted,packet.AesIVCrypted);
var args = new HandshakeEventArgs()
{
EndPoint=connection.RemoteEndPoint,
ID=packet.PedID,
Username=packet.Username,
PasswordHash=Security.Decrypt(packet.PasswordEncrypted, connection.RemoteEndPoint).GetString().GetSHA256Hash().ToHexString(),
};
API.Events.InvokePlayerHandshake(args);
if (args.Cancel)
{
connection.Deny(args.DenyReason);
return;
}
}
catch (Exception ex)
{
Logger?.Error($"Cannot process handshake packet from {connection.RemoteEndPoint}");
Logger?.Error(ex);
connection.Deny("Malformed handshak packet!");
return;
}
var handshakeSuccess = MainNetServer.CreateMessage();
var currentClients = ClientsByID.Values.ToArray();
var players = new Packets.PlayerData[currentClients.Length];
for(int i= 0; i<players.Length; i++)
{
players[i]=new Packets.PlayerData()
{
ID=currentClients[i].Player.ID,
Username=currentClients[i].Username,
};
}
new Packets.HandshakeSuccess()
{
Players=players
}.Pack(handshakeSuccess);
connection.Approve(handshakeSuccess);
Client tmpClient;
// Add the player to Players
lock (ClientsByNetHandle)
{
var player = new ServerPed(this)
{
ID= packet.PedID,
};
Entities.Add(player);
ClientsByNetHandle.Add(connection.RemoteUniqueIdentifier,
tmpClient = new Client(this)
{
NetHandle = connection.RemoteUniqueIdentifier,
Connection=connection,
Username=packet.Username,
Player = player,
InternalEndPoint=packet.InternalEndPoint,
}
);
ClientsByName.Add(packet.Username.ToLower(), tmpClient);
ClientsByID.Add(player.ID, tmpClient);
if (ClientsByNetHandle.Count==1) {
_hostClient=tmpClient;
}
}
Logger?.Debug($"Handshake sucess, Player:{packet.Username} PedID:{packet.PedID}");
}
// The connection has been approved, now we need to send all other players to the new player and the new player to all players
private void PlayerConnected(Client newClient)
{
if (newClient==_hostClient)
{
API.SendCustomEvent(new() { newClient },CustomEvents.IsHost, true);
}
// Send new client to all players
var cons = MainNetServer.Connections.Exclude(newClient.Connection);
if (cons.Count!=0)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerConnect()
{
PedID=newClient.Player.ID,
Username = newClient.Username
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, cons, NetDeliveryMethod.ReliableOrdered, 0);
}
// Send all props to this player
BaseScript.SendServerPropsTo( new(Entities.ServerProps.Values), new() { newClient});
// Send all blips to this player
BaseScript.SendServerBlipsTo(new(Entities.Blips.Values), new() { newClient});
// Create P2P connection
if (Settings.UseP2P)
{
ClientsByNetHandle.Values.ForEach(target =>
{
if (target==newClient) { return; }
MainNetServer.Introduce(target.InternalEndPoint, target.EndPoint, newClient.InternalEndPoint, newClient.EndPoint, target.Player.ID.ToString());
});
}
Logger?.Info($"Player {newClient.Username} connected!");
if (!string.IsNullOrEmpty(Settings.WelcomeMessage))
{
SendChatMessage("Server",Settings.WelcomeMessage , newClient);
}
}
// Send all players a message that someone has left the server
private void PlayerDisconnected(Client localClient)
{
var cons = MainNetServer.Connections.Exclude(localClient.Connection);
if (cons.Count!=0)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerDisconnect()
{
PedID=localClient.Player.ID,
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage,cons , NetDeliveryMethod.ReliableOrdered, 0);
}
Entities.CleanUp(localClient);
_worker.QueueJob(() => API.Events.InvokePlayerDisconnected(localClient));
Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.Player.ID}");
if (ClientsByNetHandle.ContainsKey(localClient.NetHandle)) { ClientsByNetHandle.Remove(localClient.NetHandle); }
if (ClientsByName.ContainsKey(localClient.Username.ToLower())) { ClientsByName.Remove(localClient.Username.ToLower()); }
if (ClientsByID.ContainsKey(localClient.Player.ID)) { ClientsByID.Remove(localClient.Player.ID); }
if (localClient==_hostClient)
{
_hostClient = ClientsByNetHandle.Values.FirstOrDefault();
_hostClient?.SendCustomEvent(CustomEvents.IsHost, true);
}
Security.RemoveConnection(localClient.Connection.RemoteEndPoint);
}
#region -- SYNC --
#region SyncEntities
private void PedSync(Packets.PedSync packet, Client client)
{
_worker.QueueJob(() => Entities.Update(packet, client));
bool isPlayer = packet.ID==client.Player.ID;
if (isPlayer)
{
_worker.QueueJob(() => API.Events.InvokePlayerUpdate(client));
}
if (Settings.UseP2P) { return; }
foreach (var c in ClientsByNetHandle.Values)
{
// Don't send data back
if (c.NetHandle==client.NetHandle) { continue; }
// Check streaming distance
if (isPlayer)
{
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
{
continue;
}
}
else if ((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
{
continue;
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage,c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
}
private void VehicleSync(Packets.VehicleSync packet, Client client)
{
_worker.QueueJob(() => Entities.Update(packet, client));
bool isPlayer = packet.ID==client.Player?.LastVehicle?.ID;
if (Settings.UseP2P) { return; }
foreach (var c in ClientsByNetHandle.Values)
{
if (c.NetHandle==client.NetHandle) { continue; }
if (isPlayer)
{
// Player's vehicle
if ((Settings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.PlayerStreamingDistance))
{
continue;
}
}
else if((Settings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>Settings.NpcStreamingDistance))
{
continue;
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
}
private void ProjectileSync(Packets.ProjectileSync packet, Client client)
{
if (Settings.UseP2P) { return; }
foreach (var c in ClientsByNetHandle.Values)
{
if (c.NetHandle==client.NetHandle) { continue; }
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
}
#endregion
// Send a message to targets or all players // Send a message to targets or all players
internal void ChatMessageReceived(string name, string message,Client sender=null) internal void ChatMessageReceived(string name, string message,Client sender=null)
{ {
@ -831,8 +545,6 @@ namespace RageCoop.Server
}.Pack(msg); }.Pack(msg);
MainNetServer.SendMessage(msg, target.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat); MainNetServer.SendMessage(msg, target.Connection, NetDeliveryMethod.ReliableOrdered, (int)ConnectionChannel.Chat);
} }
#endregion
internal void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback) internal void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback)
{ {

View File

@ -88,6 +88,6 @@
/// <summary> /// <summary>
/// Whether to use direct connection between players to send entity information /// Whether to use direct connection between players to send entity information
/// </summary> /// </summary>
public bool UseP2P { get; set; } = true; public bool UseP2P { get; set; } = false;
} }
} }