2019-08-16 17:12:19 -07:00
/* Copyright (C) 2019 Wildfire Games.
2017-09-11 17:29:10 -07:00
* This file is part of 0 A . D .
*
* 0 A . D . is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 2 of the License , or
* ( at your option ) any later version .
*
* 0 A . D . is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with 0 A . D . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "precompiled.h"
2018-04-27 09:48:44 -07:00
# include "JSInterface_Network.h"
2017-09-11 17:29:10 -07:00
# include "lib/external_libraries/enet.h"
# include "lib/external_libraries/libsdl.h"
2018-08-25 07:34:30 -07:00
# include "lib/types.h"
2017-09-11 17:29:10 -07:00
# include "lobby/IXmppClient.h"
# include "network/NetClient.h"
# include "network/NetMessage.h"
# include "network/NetServer.h"
# include "network/StunClient.h"
# include "ps/CLogger.h"
# include "ps/Game.h"
2018-04-27 09:48:44 -07:00
# include "scriptinterface/ScriptInterface.h"
2017-09-11 17:29:10 -07:00
u16 JSI_Network : : GetDefaultPort ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) )
{
return PS_DEFAULT_PORT ;
}
2018-04-04 05:43:25 -07:00
bool JSI_Network : : HasNetServer ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) )
{
return g_NetServer ;
}
2018-04-04 05:20:34 -07:00
bool JSI_Network : : HasNetClient ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) )
{
2018-04-04 05:43:25 -07:00
return g_NetClient ;
2018-04-04 05:20:34 -07:00
}
2017-09-11 17:29:10 -07:00
JS : : Value JSI_Network : : FindStunEndpoint ( ScriptInterface : : CxPrivate * pCxPrivate , int port )
{
return StunClient : : FindStunEndpointHost ( * ( pCxPrivate - > pScriptInterface ) , port ) ;
}
2018-08-25 07:34:30 -07:00
void JSI_Network : : StartNetworkHost ( ScriptInterface : : CxPrivate * pCxPrivate , const CStrW & playerName , const u16 serverPort , const CStr & hostLobbyName )
2017-09-11 17:29:10 -07:00
{
ENSURE ( ! g_NetClient ) ;
ENSURE ( ! g_NetServer ) ;
ENSURE ( ! g_Game ) ;
2018-08-25 07:34:30 -07:00
// Always use lobby authentication for lobby matches to prevent impersonation and smurfing, in particular through mods that implemented an UI for arbitrary or other players nicknames.
g_NetServer = new CNetServer ( static_cast < bool > ( g_XmppClient ) ) ;
2017-09-11 17:29:10 -07:00
if ( ! g_NetServer - > SetupConnection ( serverPort ) )
{
pCxPrivate - > pScriptInterface - > ReportError ( " Failed to start server " ) ;
SAFE_DELETE ( g_NetServer ) ;
return ;
}
g_Game = new CGame ( ) ;
g_NetClient = new CNetClient ( g_Game , true ) ;
g_NetClient - > SetUserName ( playerName ) ;
2018-03-11 17:23:40 -07:00
g_NetClient - > SetHostingPlayerName ( hostLobbyName ) ;
2017-09-11 17:29:10 -07:00
2019-08-16 17:12:19 -07:00
if ( ! g_NetClient - > SetupConnection ( " 127.0.0.1 " , serverPort , nullptr ) )
2017-09-11 17:29:10 -07:00
{
pCxPrivate - > pScriptInterface - > ReportError ( " Failed to connect to server " ) ;
SAFE_DELETE ( g_NetClient ) ;
SAFE_DELETE ( g_Game ) ;
}
}
void JSI_Network : : StartNetworkJoin ( ScriptInterface : : CxPrivate * pCxPrivate , const CStrW & playerName , const CStr & serverAddress , u16 serverPort , bool useSTUN , const CStr & hostJID )
{
ENSURE ( ! g_NetClient ) ;
ENSURE ( ! g_NetServer ) ;
ENSURE ( ! g_Game ) ;
ENetHost * enetClient = nullptr ;
if ( g_XmppClient & & useSTUN )
{
// Find an unused port
for ( int i = 0 ; i < 5 & & ! enetClient ; + + i )
{
// Ports below 1024 are privileged on unix
u16 port = 1024 + rand ( ) % ( UINT16_MAX - 1024 ) ;
ENetAddress hostAddr { ENET_HOST_ANY , port } ;
enetClient = enet_host_create ( & hostAddr , 1 , 1 , 0 , 0 ) ;
+ + hostAddr . port ;
}
if ( ! enetClient )
{
pCxPrivate - > pScriptInterface - > ReportError ( " Could not find an unused port for the enet STUN client " ) ;
return ;
}
2019-08-16 17:12:19 -07:00
StunClient : : StunEndpoint stunEndpoint ;
if ( ! StunClient : : FindStunEndpointJoin ( * enetClient , stunEndpoint ) )
2017-09-11 17:29:10 -07:00
{
pCxPrivate - > pScriptInterface - > ReportError ( " Could not find the STUN endpoint " ) ;
return ;
}
g_XmppClient - > SendStunEndpointToHost ( stunEndpoint , hostJID ) ;
SDL_Delay ( 1000 ) ;
}
g_Game = new CGame ( ) ;
g_NetClient = new CNetClient ( g_Game , false ) ;
g_NetClient - > SetUserName ( playerName ) ;
2018-03-11 17:23:40 -07:00
g_NetClient - > SetHostingPlayerName ( hostJID . substr ( 0 , hostJID . find ( " @ " ) ) ) ;
2017-09-11 17:29:10 -07:00
if ( g_XmppClient & & useSTUN )
2019-08-16 17:12:19 -07:00
StunClient : : SendHolePunchingMessages ( * enetClient , serverAddress , serverPort ) ;
2017-09-11 17:29:10 -07:00
if ( ! g_NetClient - > SetupConnection ( serverAddress , serverPort , enetClient ) )
{
pCxPrivate - > pScriptInterface - > ReportError ( " Failed to connect to server " ) ;
SAFE_DELETE ( g_NetClient ) ;
SAFE_DELETE ( g_Game ) ;
}
}
void JSI_Network : : DisconnectNetworkGame ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) )
{
// TODO: we ought to do async reliable disconnections
SAFE_DELETE ( g_NetServer ) ;
SAFE_DELETE ( g_NetClient ) ;
SAFE_DELETE ( g_Game ) ;
}
CStr JSI_Network : : GetPlayerGUID ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) )
{
if ( ! g_NetClient )
return " local " ;
return g_NetClient - > GetGUID ( ) ;
}
JS : : Value JSI_Network : : PollNetworkClient ( ScriptInterface : : CxPrivate * pCxPrivate )
{
if ( ! g_NetClient )
return JS : : UndefinedValue ( ) ;
// Convert from net client context to GUI script context
JSContext * cxNet = g_NetClient - > GetScriptInterface ( ) . GetContext ( ) ;
JSAutoRequest rqNet ( cxNet ) ;
JS : : RootedValue pollNet ( cxNet ) ;
g_NetClient - > GuiPoll ( & pollNet ) ;
return pCxPrivate - > pScriptInterface - > CloneValueFromOtherContext ( g_NetClient - > GetScriptInterface ( ) , pollNet ) ;
}
void JSI_Network : : SetNetworkGameAttributes ( ScriptInterface : : CxPrivate * pCxPrivate , JS : : HandleValue attribs1 )
{
ENSURE ( g_NetClient ) ;
// TODO: This is a workaround because we need to pass a MutableHandle to a JSAPI functions somewhere (with no obvious reason).
JSContext * cx = pCxPrivate - > pScriptInterface - > GetContext ( ) ;
JSAutoRequest rq ( cx ) ;
JS : : RootedValue attribs ( cx , attribs1 ) ;
g_NetClient - > SendGameSetupMessage ( & attribs , * ( pCxPrivate - > pScriptInterface ) ) ;
}
void JSI_Network : : AssignNetworkPlayer ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) , int playerID , const CStr & guid )
{
ENSURE ( g_NetClient ) ;
g_NetClient - > SendAssignPlayerMessage ( playerID , guid ) ;
}
void JSI_Network : : KickPlayer ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) , const CStrW & playerName , bool ban )
{
ENSURE ( g_NetClient ) ;
g_NetClient - > SendKickPlayerMessage ( playerName , ban ) ;
}
void JSI_Network : : SendNetworkChat ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) , const CStrW & message )
{
ENSURE ( g_NetClient ) ;
g_NetClient - > SendChatMessage ( message ) ;
}
void JSI_Network : : SendNetworkReady ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) , int message )
{
ENSURE ( g_NetClient ) ;
g_NetClient - > SendReadyMessage ( message ) ;
}
void JSI_Network : : ClearAllPlayerReady ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) )
{
ENSURE ( g_NetClient ) ;
g_NetClient - > SendClearAllReadyMessage ( ) ;
}
void JSI_Network : : StartNetworkGame ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) )
{
ENSURE ( g_NetClient ) ;
g_NetClient - > SendStartGameMessage ( ) ;
}
void JSI_Network : : SetTurnLength ( ScriptInterface : : CxPrivate * UNUSED ( pCxPrivate ) , int length )
{
if ( g_NetServer )
g_NetServer - > SetTurnLength ( length ) ;
else
LOGERROR ( " Only network host can change turn length " ) ;
}
void JSI_Network : : RegisterScriptFunctions ( const ScriptInterface & scriptInterface )
{
scriptInterface . RegisterFunction < u16 , & GetDefaultPort > ( " GetDefaultPort " ) ;
2018-04-04 05:43:25 -07:00
scriptInterface . RegisterFunction < bool , & HasNetServer > ( " HasNetServer " ) ;
2018-04-04 05:20:34 -07:00
scriptInterface . RegisterFunction < bool , & HasNetClient > ( " HasNetClient " ) ;
2017-09-11 17:29:10 -07:00
scriptInterface . RegisterFunction < JS : : Value , int , & FindStunEndpoint > ( " FindStunEndpoint " ) ;
2018-08-25 07:34:30 -07:00
scriptInterface . RegisterFunction < void , CStrW , u16 , CStr , & StartNetworkHost > ( " StartNetworkHost " ) ;
2017-09-11 17:29:10 -07:00
scriptInterface . RegisterFunction < void , CStrW , CStr , u16 , bool , CStr , & StartNetworkJoin > ( " StartNetworkJoin " ) ;
scriptInterface . RegisterFunction < void , & DisconnectNetworkGame > ( " DisconnectNetworkGame " ) ;
scriptInterface . RegisterFunction < CStr , & GetPlayerGUID > ( " GetPlayerGUID " ) ;
scriptInterface . RegisterFunction < JS : : Value , & PollNetworkClient > ( " PollNetworkClient " ) ;
scriptInterface . RegisterFunction < void , JS : : HandleValue , & SetNetworkGameAttributes > ( " SetNetworkGameAttributes " ) ;
scriptInterface . RegisterFunction < void , int , CStr , & AssignNetworkPlayer > ( " AssignNetworkPlayer " ) ;
scriptInterface . RegisterFunction < void , CStrW , bool , & KickPlayer > ( " KickPlayer " ) ;
scriptInterface . RegisterFunction < void , CStrW , & SendNetworkChat > ( " SendNetworkChat " ) ;
scriptInterface . RegisterFunction < void , int , & SendNetworkReady > ( " SendNetworkReady " ) ;
scriptInterface . RegisterFunction < void , & ClearAllPlayerReady > ( " ClearAllPlayerReady " ) ;
scriptInterface . RegisterFunction < void , & StartNetworkGame > ( " StartNetworkGame " ) ;
scriptInterface . RegisterFunction < void , int , & SetTurnLength > ( " SetTurnLength " ) ;
}