2022-09-06 21:46:35 +08:00
using GTA.UI ;
using Lidgren.Network ;
2022-10-08 23:49:48 +08:00
using RageCoop.Client.Scripting ;
2022-05-22 15:55:26 +08:00
using RageCoop.Core ;
2022-07-20 17:50:01 +08:00
using System ;
2022-07-29 21:15:23 +08:00
using System.Collections.Generic ;
2022-09-06 21:46:35 +08:00
using System.Net ;
2022-07-19 17:15:53 +08:00
using System.Security.Cryptography ;
2022-07-20 17:50:01 +08:00
using System.Threading ;
using System.Threading.Tasks ;
2022-08-18 17:45:08 +08:00
2022-05-22 15:55:26 +08:00
namespace RageCoop.Client
{
2022-05-25 10:09:59 +08:00
internal static partial class Networking
2022-05-22 15:55:26 +08:00
{
2022-08-11 16:25:38 +08:00
public static CoopPeer Peer ;
2022-09-06 21:46:35 +08:00
public static float Latency = > ServerConnection . AverageRoundtripTime / 2 ;
2022-05-23 16:59:49 +08:00
public static bool ShowNetworkInfo = false ;
2022-08-06 12:32:04 +08:00
public static Security Security ;
public static NetConnection ServerConnection ;
2022-09-08 12:37:06 -07:00
private static readonly Dictionary < int , Action < PacketType , NetIncomingMessage > > PendingResponses = new Dictionary < int , Action < PacketType , NetIncomingMessage > > ( ) ;
internal static readonly Dictionary < PacketType , Func < NetIncomingMessage , Packet > > RequestHandlers = new Dictionary < PacketType , Func < NetIncomingMessage , Packet > > ( ) ;
2022-09-06 21:46:35 +08:00
internal static float SimulatedLatency = 0 ;
2022-07-31 20:47:04 +08:00
public static bool IsConnecting { get ; private set ; }
2022-08-18 18:59:38 +08:00
public static IPEndPoint _targetServerEP ;
2022-06-11 18:41:10 +08:00
static Networking ( )
{
2022-09-06 21:46:35 +08:00
Security = new Security ( Main . Logger ) ;
2022-10-05 15:53:57 +08:00
Packets . CustomEvent . ResolveHandle = _resolveHandle ;
2022-06-11 18:41:10 +08:00
}
2022-05-23 16:59:49 +08:00
2022-09-06 21:46:35 +08:00
public static void ToggleConnection ( string address , string username = null , string password = null , PublicKey publicKey = null )
2022-05-22 15:55:26 +08:00
{
2022-09-06 21:46:35 +08:00
Menus . CoopMenu . Menu . Visible = false ;
2022-08-11 18:25:01 +08:00
Peer ? . Shutdown ( "Bye" ) ;
2022-05-25 11:32:34 +08:00
if ( IsOnServer )
2022-05-22 15:55:26 +08:00
{
2022-08-11 14:59:09 +02:00
// ?
2022-05-22 15:55:26 +08:00
}
2022-09-06 21:46:35 +08:00
else if ( IsConnecting )
{
2022-07-31 20:47:04 +08:00
_publicKeyReceived . Set ( ) ;
IsConnecting = false ;
2022-08-12 20:40:50 +08:00
Notification . Show ( "Connection has been canceled" ) ;
2022-07-31 20:47:04 +08:00
}
2022-05-22 15:55:26 +08:00
else
{
2022-08-11 18:25:01 +08:00
Peer ? . Dispose ( ) ;
2022-07-31 20:47:04 +08:00
IsConnecting = true ;
2022-06-24 10:33:36 +08:00
password = password ? ? Main . Settings . Password ;
2022-09-06 21:46:35 +08:00
username = username ? ? Main . Settings . Username ;
2022-08-04 17:14:07 +08:00
2022-05-22 15:55:26 +08:00
// 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP
NetPeerConfiguration config = new NetPeerConfiguration ( "623c92c287cc392406e7aaaac1c0f3b0" )
{
2022-08-02 17:43:18 +08:00
AutoFlushSendQueue = false ,
2022-09-06 21:46:35 +08:00
SimulatedMinimumLatency = SimulatedLatency ,
SimulatedRandomLatency = 0 ,
2022-08-08 17:03:41 +08:00
AcceptIncomingConnections = true ,
MaximumConnections = 32 ,
PingInterval = 5
2022-05-22 15:55:26 +08:00
} ;
2022-06-24 10:33:36 +08:00
config . EnableMessageType ( NetIncomingMessageType . UnconnectedData ) ;
2022-08-06 12:32:04 +08:00
config . EnableMessageType ( NetIncomingMessageType . NatIntroductionSuccess ) ;
2022-05-22 15:55:26 +08:00
string [ ] ip = new string [ 2 ] ;
int idx = address . LastIndexOf ( ':' ) ;
if ( idx ! = - 1 )
{
ip [ 0 ] = address . Substring ( 0 , idx ) ;
ip [ 1 ] = address . Substring ( idx + 1 ) ;
}
if ( ip . Length ! = 2 )
{
throw new Exception ( "Malformed URL" ) ;
}
2022-06-06 17:37:11 +08:00
2022-07-29 20:35:39 +08:00
PlayerList . Cleanup ( ) ;
2022-05-22 15:55:26 +08:00
EntityPool . AddPlayer ( ) ;
2022-09-06 21:46:35 +08:00
if ( publicKey = = null & & ! string . IsNullOrEmpty ( password ) & & ! Menus . CoopMenu . ShowPopUp ( "" , "WARNING" , "Server's IP can be spoofed when using direct connection, do you wish to continue?" , "" , true ) )
2022-08-18 17:45:08 +08:00
{
2022-09-06 21:46:35 +08:00
IsConnecting = false ;
2022-08-18 17:45:08 +08:00
return ;
}
2022-06-24 10:33:36 +08:00
Task . Run ( ( ) = >
2022-05-22 15:55:26 +08:00
{
2022-07-02 17:14:56 +08:00
try
{
2022-09-06 21:46:35 +08:00
_targetServerEP = CoreUtils . StringToEndPoint ( address ) ;
2022-09-08 12:37:06 -07:00
// Ensure static constructor invocation
2022-07-02 17:14:56 +08:00
DownloadManager . Cleanup ( ) ;
2022-08-11 16:25:38 +08:00
Peer = new CoopPeer ( config ) ;
2022-09-06 21:46:35 +08:00
Peer . OnMessageReceived + = ( s , m ) = >
2022-08-11 16:25:38 +08:00
{
try { ProcessMessage ( m ) ; }
catch ( Exception ex ) { Main . Logger . Error ( ex ) ; }
} ;
2022-10-09 11:15:09 +08:00
API . QueueAction ( ( ) = > { Notification . Show ( $"~y~Trying to connect..." ) ; } ) ;
2022-09-06 21:46:35 +08:00
Menus . CoopMenu . _serverConnectItem . Enabled = false ;
2022-07-02 17:14:56 +08:00
Security . Regen ( ) ;
2022-09-06 21:46:35 +08:00
if ( publicKey = = null )
{
if ( ! GetServerPublicKey ( ip [ 0 ] , int . Parse ( ip [ 1 ] ) ) )
2022-08-18 17:45:08 +08:00
{
2022-09-06 21:46:35 +08:00
Menus . CoopMenu . _serverConnectItem . Enabled = true ;
2022-08-18 17:45:08 +08:00
throw new TimeoutException ( "Failed to retrive server's public key" ) ;
}
}
2022-09-06 21:46:35 +08:00
else
{
Security . SetServerPublicKey ( publicKey . Modulus , publicKey . Exponent ) ;
2022-07-11 11:06:31 +08:00
}
2022-07-20 17:50:01 +08:00
2022-08-18 17:45:08 +08:00
// Send handshake packet
2022-08-06 12:32:04 +08:00
NetOutgoingMessage outgoingMessage = Peer . CreateMessage ( ) ;
2022-07-02 17:14:56 +08:00
var handshake = new Packets . Handshake ( )
{
2022-09-06 21:46:35 +08:00
PedID = Main . LocalPlayerID ,
Username = username ,
2022-08-16 23:17:04 +08:00
ModVersion = Main . Version . ToString ( ) ,
2022-09-06 21:46:35 +08:00
PasswordEncrypted = Security . Encrypt ( password . GetBytes ( ) ) ,
2022-08-11 16:25:38 +08:00
InternalEndPoint = new System . Net . IPEndPoint ( CoreUtils . GetLocalAddress ( ip [ 0 ] ) , Peer . Port )
2022-07-02 17:14:56 +08:00
} ;
Security . GetSymmetricKeysCrypted ( out handshake . AesKeyCrypted , out handshake . AesIVCrypted ) ;
handshake . Pack ( outgoingMessage ) ;
2022-08-06 12:32:04 +08:00
ServerConnection = Peer . Connect ( ip [ 0 ] , short . Parse ( ip [ 1 ] ) , outgoingMessage ) ;
2022-06-24 10:33:36 +08:00
2022-07-02 17:14:56 +08:00
}
2022-07-20 17:50:01 +08:00
catch ( Exception ex )
2022-06-24 10:33:36 +08:00
{
2022-07-11 11:06:31 +08:00
Main . Logger . Error ( "Cannot connect to server: " , ex ) ;
2022-10-09 11:15:09 +08:00
API . QueueAction ( ( ) = > Notification . Show ( "Cannot connect to server: " + ex . Message ) ) ;
2022-07-02 17:14:56 +08:00
}
2022-09-06 21:46:35 +08:00
IsConnecting = false ;
2022-06-24 10:33:36 +08:00
} ) ;
2022-05-22 15:55:26 +08:00
}
}
2022-10-09 22:07:52 +08:00
public static bool IsOnServer = > ServerConnection ? . Status = = NetConnectionStatus . Connected ;
2022-07-20 17:50:01 +08:00
2022-05-22 15:55:26 +08:00
#region - - PLAYER - -
2022-05-23 16:59:49 +08:00
private static void PlayerConnect ( Packets . PlayerConnect packet )
2022-05-22 15:55:26 +08:00
{
2022-08-08 17:03:41 +08:00
var p = new Player
2022-05-22 15:55:26 +08:00
{
2022-08-27 14:17:10 +08:00
ID = packet . PedID ,
2022-09-06 21:46:35 +08:00
Username = packet . Username ,
2022-05-22 15:55:26 +08:00
} ;
2022-05-23 16:59:49 +08:00
PlayerList . SetPlayer ( packet . PedID , packet . Username ) ;
2022-05-22 15:55:26 +08:00
Main . Logger . Debug ( $"player connected:{p.Username}" ) ;
2022-10-09 11:15:09 +08:00
API . QueueAction ( ( ) = >
2022-08-10 20:42:47 +08:00
GTA . UI . Notification . Show ( $"~h~{p.Username}~h~ connected." ) ) ;
2022-05-22 15:55:26 +08:00
}
2022-05-23 16:59:49 +08:00
private static void PlayerDisconnect ( Packets . PlayerDisconnect packet )
2022-05-22 15:55:26 +08:00
{
2022-08-11 15:38:19 +08:00
var player = PlayerList . GetPlayer ( packet . PedID ) ;
2022-09-06 21:46:35 +08:00
if ( player = = null ) { return ; }
2022-05-23 16:59:49 +08:00
PlayerList . RemovePlayer ( packet . PedID ) ;
2022-10-09 11:15:09 +08:00
API . QueueAction ( ( ) = >
2022-09-06 21:46:35 +08:00
{
2022-08-11 15:38:19 +08:00
EntityPool . RemoveAllFromPlayer ( packet . PedID ) ;
GTA . UI . Notification . Show ( $"~h~{player.Username}~h~ left." ) ;
} ) ;
2022-05-22 15:55:26 +08:00
}
#endregion // -- PLAYER --
2022-07-29 20:35:39 +08:00
#region - - GET - -
2022-09-06 21:46:35 +08:00
private static bool GetServerPublicKey ( string host , int port , int timeout = 10000 )
2022-06-24 10:33:36 +08:00
{
2022-09-06 21:46:35 +08:00
Security . ServerRSA = null ;
2022-08-06 12:32:04 +08:00
var msg = Peer . CreateMessage ( ) ;
2022-07-20 17:50:01 +08:00
new Packets . PublicKeyRequest ( ) . Pack ( msg ) ;
2022-08-12 20:40:50 +08:00
Peer . SendUnconnectedMessage ( msg , host , port ) ;
2022-09-06 21:46:35 +08:00
return _publicKeyReceived . WaitOne ( timeout ) & & Security . ServerRSA ! = null ;
2022-06-24 10:33:36 +08:00
}
2022-07-29 20:35:39 +08:00
public static void GetResponse < T > ( Packet request , Action < T > callback , ConnectionChannel channel = ConnectionChannel . RequestResponse ) where T : Packet , new ( )
2022-07-19 17:15:53 +08:00
{
var received = new AutoResetEvent ( false ) ;
var id = NewRequestID ( ) ;
PendingResponses . Add ( id , ( type , p ) = >
{
var result = new T ( ) ;
2022-08-06 10:43:24 +08:00
result . Deserialize ( p ) ;
2022-07-19 17:15:53 +08:00
callback ( result ) ;
} ) ;
2022-08-06 12:32:04 +08:00
var msg = Peer . CreateMessage ( ) ;
2022-07-19 17:15:53 +08:00
msg . Write ( ( byte ) PacketType . Request ) ;
msg . Write ( id ) ;
request . Pack ( msg ) ;
2022-09-06 21:46:35 +08:00
Peer . SendMessage ( msg , ServerConnection , NetDeliveryMethod . ReliableOrdered , ( int ) channel ) ;
2022-07-19 17:15:53 +08:00
}
2022-07-29 20:35:39 +08:00
#endregion
2022-07-19 17:15:53 +08:00
private static int NewRequestID ( )
{
int ID = 0 ;
2022-09-06 21:46:35 +08:00
while ( ( ID = = 0 ) | | PendingResponses . ContainsKey ( ID ) )
2022-07-19 17:15:53 +08:00
{
byte [ ] rngBytes = new byte [ 4 ] ;
RandomNumberGenerator . Create ( ) . GetBytes ( rngBytes ) ;
// Convert the bytes into an integer
ID = BitConverter . ToInt32 ( rngBytes , 0 ) ;
}
return ID ;
}
2022-05-22 15:55:26 +08:00
}
}