1 /* Copyright (C) 2018 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
20 #include "JSInterface_Network.h"
22 #include "lib/external_libraries/enet.h"
23 #include "lib/external_libraries/libsdl.h"
24 #include "lib/types.h"
25 #include "lobby/IXmppClient.h"
26 #include "network/NetClient.h"
27 #include "network/NetMessage.h"
28 #include "network/NetServer.h"
29 #include "network/StunClient.h"
30 #include "ps/CLogger.h"
32 #include "scriptinterface/ScriptInterface.h"
34 u16
JSI_Network::GetDefaultPort(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
36 return PS_DEFAULT_PORT
;
39 bool JSI_Network::HasNetServer(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
44 bool JSI_Network::HasNetClient(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
49 JS::Value
JSI_Network::FindStunEndpoint(ScriptInterface::CxPrivate
* pCxPrivate
, int port
)
51 return StunClient::FindStunEndpointHost(*(pCxPrivate
->pScriptInterface
), port
);
54 void JSI_Network::StartNetworkHost(ScriptInterface::CxPrivate
* pCxPrivate
, const CStrW
& playerName
, const u16 serverPort
, const CStr
& hostLobbyName
)
60 // 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.
61 g_NetServer
= new CNetServer(static_cast<bool>(g_XmppClient
));
62 if (!g_NetServer
->SetupConnection(serverPort
))
64 pCxPrivate
->pScriptInterface
->ReportError("Failed to start server");
65 SAFE_DELETE(g_NetServer
);
70 g_NetClient
= new CNetClient(g_Game
, true);
71 g_NetClient
->SetUserName(playerName
);
72 g_NetClient
->SetHostingPlayerName(hostLobbyName
);
74 if (!g_NetClient
->SetupConnection("127.0.0.1", serverPort
))
76 pCxPrivate
->pScriptInterface
->ReportError("Failed to connect to server");
77 SAFE_DELETE(g_NetClient
);
82 void JSI_Network::StartNetworkJoin(ScriptInterface::CxPrivate
* pCxPrivate
, const CStrW
& playerName
, const CStr
& serverAddress
, u16 serverPort
, bool useSTUN
, const CStr
& hostJID
)
88 ENetHost
* enetClient
= nullptr;
89 if (g_XmppClient
&& useSTUN
)
91 // Find an unused port
92 for (int i
= 0; i
< 5 && !enetClient
; ++i
)
94 // Ports below 1024 are privileged on unix
95 u16 port
= 1024 + rand() % (UINT16_MAX
- 1024);
96 ENetAddress hostAddr
{ENET_HOST_ANY
, port
};
97 enetClient
= enet_host_create(&hostAddr
, 1, 1, 0, 0);
103 pCxPrivate
->pScriptInterface
->ReportError("Could not find an unused port for the enet STUN client");
107 StunClient::StunEndpoint
* stunEndpoint
= StunClient::FindStunEndpointJoin(enetClient
);
110 pCxPrivate
->pScriptInterface
->ReportError("Could not find the STUN endpoint");
114 g_XmppClient
->SendStunEndpointToHost(stunEndpoint
, hostJID
);
120 g_Game
= new CGame();
121 g_NetClient
= new CNetClient(g_Game
, false);
122 g_NetClient
->SetUserName(playerName
);
123 g_NetClient
->SetHostingPlayerName(hostJID
.substr(0, hostJID
.find("@")));
125 if (g_XmppClient
&& useSTUN
)
126 StunClient::SendHolePunchingMessages(enetClient
, serverAddress
.c_str(), serverPort
);
128 if (!g_NetClient
->SetupConnection(serverAddress
, serverPort
, enetClient
))
130 pCxPrivate
->pScriptInterface
->ReportError("Failed to connect to server");
131 SAFE_DELETE(g_NetClient
);
136 void JSI_Network::DisconnectNetworkGame(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
138 // TODO: we ought to do async reliable disconnections
140 SAFE_DELETE(g_NetServer
);
141 SAFE_DELETE(g_NetClient
);
145 CStr
JSI_Network::GetPlayerGUID(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
150 return g_NetClient
->GetGUID();
153 JS::Value
JSI_Network::PollNetworkClient(ScriptInterface::CxPrivate
* pCxPrivate
)
156 return JS::UndefinedValue();
158 // Convert from net client context to GUI script context
159 JSContext
* cxNet
= g_NetClient
->GetScriptInterface().GetContext();
160 JSAutoRequest
rqNet(cxNet
);
161 JS::RootedValue
pollNet(cxNet
);
162 g_NetClient
->GuiPoll(&pollNet
);
163 return pCxPrivate
->pScriptInterface
->CloneValueFromOtherContext(g_NetClient
->GetScriptInterface(), pollNet
);
166 void JSI_Network::SetNetworkGameAttributes(ScriptInterface::CxPrivate
* pCxPrivate
, JS::HandleValue attribs1
)
170 // TODO: This is a workaround because we need to pass a MutableHandle to a JSAPI functions somewhere (with no obvious reason).
171 JSContext
* cx
= pCxPrivate
->pScriptInterface
->GetContext();
172 JSAutoRequest
rq(cx
);
173 JS::RootedValue
attribs(cx
, attribs1
);
175 g_NetClient
->SendGameSetupMessage(&attribs
, *(pCxPrivate
->pScriptInterface
));
178 void JSI_Network::AssignNetworkPlayer(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), int playerID
, const CStr
& guid
)
182 g_NetClient
->SendAssignPlayerMessage(playerID
, guid
);
185 void JSI_Network::KickPlayer(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const CStrW
& playerName
, bool ban
)
189 g_NetClient
->SendKickPlayerMessage(playerName
, ban
);
192 void JSI_Network::SendNetworkChat(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), const CStrW
& message
)
196 g_NetClient
->SendChatMessage(message
);
199 void JSI_Network::SendNetworkReady(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), int message
)
203 g_NetClient
->SendReadyMessage(message
);
206 void JSI_Network::ClearAllPlayerReady (ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
210 g_NetClient
->SendClearAllReadyMessage();
213 void JSI_Network::StartNetworkGame(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
))
216 g_NetClient
->SendStartGameMessage();
219 void JSI_Network::SetTurnLength(ScriptInterface::CxPrivate
* UNUSED(pCxPrivate
), int length
)
222 g_NetServer
->SetTurnLength(length
);
224 LOGERROR("Only network host can change turn length");
227 void JSI_Network::RegisterScriptFunctions(const ScriptInterface
& scriptInterface
)
229 scriptInterface
.RegisterFunction
<u16
, &GetDefaultPort
>("GetDefaultPort");
230 scriptInterface
.RegisterFunction
<bool, &HasNetServer
>("HasNetServer");
231 scriptInterface
.RegisterFunction
<bool, &HasNetClient
>("HasNetClient");
232 scriptInterface
.RegisterFunction
<JS::Value
, int, &FindStunEndpoint
>("FindStunEndpoint");
233 scriptInterface
.RegisterFunction
<void, CStrW
, u16
, CStr
, &StartNetworkHost
>("StartNetworkHost");
234 scriptInterface
.RegisterFunction
<void, CStrW
, CStr
, u16
, bool, CStr
, &StartNetworkJoin
>("StartNetworkJoin");
235 scriptInterface
.RegisterFunction
<void, &DisconnectNetworkGame
>("DisconnectNetworkGame");
236 scriptInterface
.RegisterFunction
<CStr
, &GetPlayerGUID
>("GetPlayerGUID");
237 scriptInterface
.RegisterFunction
<JS::Value
, &PollNetworkClient
>("PollNetworkClient");
238 scriptInterface
.RegisterFunction
<void, JS::HandleValue
, &SetNetworkGameAttributes
>("SetNetworkGameAttributes");
239 scriptInterface
.RegisterFunction
<void, int, CStr
, &AssignNetworkPlayer
>("AssignNetworkPlayer");
240 scriptInterface
.RegisterFunction
<void, CStrW
, bool, &KickPlayer
>("KickPlayer");
241 scriptInterface
.RegisterFunction
<void, CStrW
, &SendNetworkChat
>("SendNetworkChat");
242 scriptInterface
.RegisterFunction
<void, int, &SendNetworkReady
>("SendNetworkReady");
243 scriptInterface
.RegisterFunction
<void, &ClearAllPlayerReady
>("ClearAllPlayerReady");
244 scriptInterface
.RegisterFunction
<void, &StartNetworkGame
>("StartNetworkGame");
245 scriptInterface
.RegisterFunction
<void, int, &SetTurnLength
>("SetTurnLength");