diff --git a/build/premake/premake4.lua b/build/premake/premake4.lua index 1c18dcd350..a8ebb0d72f 100644 --- a/build/premake/premake4.lua +++ b/build/premake/premake4.lua @@ -695,7 +695,7 @@ function setup_all_libs () source_dirs = { "ps", "ps/scripting", - "ps/Network", + "network/scripting", "ps/GameSetup", "ps/XML", "soundmanager", diff --git a/source/gui/scripting/ScriptFunctions.cpp b/source/gui/scripting/ScriptFunctions.cpp index 7f881186b3..12c73ad425 100644 --- a/source/gui/scripting/ScriptFunctions.cpp +++ b/source/gui/scripting/ScriptFunctions.cpp @@ -29,16 +29,13 @@ #include "gui/scripting/JSInterface_GUITypes.h" #include "i18n/L10n.h" #include "i18n/scripting/JSInterface_L10n.h" -#include "lib/external_libraries/enet.h" #include "lib/svn_revision.h" #include "lib/sysdep/sysdep.h" #include "lib/utf8.h" #include "lobby/scripting/JSInterface_Lobby.h" -#include "lobby/IXmppClient.h" #include "network/NetClient.h" -#include "network/NetMessage.h" #include "network/NetServer.h" -#include "network/StunClient.h" +#include "network/scripting/JSInterface_Network.h" #include "ps/CConsole.h" #include "ps/CLogger.h" #include "ps/Errors.h" @@ -226,17 +223,6 @@ void SetViewedPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int id) g_Game->SetViewedPlayerID(id); } -JS::Value FindStunEndpoint(ScriptInterface::CxPrivate* pCxPrivate, int port) -{ - return StunClient::FindStunEndpointHost(*(pCxPrivate->pScriptInterface), port); -} - -void StartNetworkGame(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) -{ - ENSURE(g_NetClient); - g_NetClient->SendStartGameMessage(); -} - void StartGame(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue attribs, int playerID) { ENSURE(!g_NetServer); @@ -257,168 +243,6 @@ void StartGame(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue attribs, g_Game->StartGame(&gameAttribs, ""); } -void 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 StartNetworkHost(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& playerName, const u16 serverPort) -{ - ENSURE(!g_NetClient); - ENSURE(!g_NetServer); - ENSURE(!g_Game); - - g_NetServer = new CNetServer(); - 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); - - if (!g_NetClient->SetupConnection("127.0.0.1", serverPort)) - { - pCxPrivate->pScriptInterface->ReportError("Failed to connect to server"); - SAFE_DELETE(g_NetClient); - SAFE_DELETE(g_Game); - } -} - -void StartNetworkJoin(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& playerName, const CStr& serverAddress, u16 serverPort, bool useSTUN, const std::string& 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; - } - - StunClient::StunEndpoint* stunEndpoint = StunClient::FindStunEndpointJoin(enetClient); - if (!stunEndpoint) - { - pCxPrivate->pScriptInterface->ReportError("Could not find the STUN endpoint"); - return; - } - - g_XmppClient->SendStunEndpointToHost(stunEndpoint, hostJID); - delete stunEndpoint; - - SDL_Delay(1000); - } - - g_Game = new CGame(); - g_NetClient = new CNetClient(g_Game, false); - g_NetClient->SetUserName(playerName); - - if (g_XmppClient && useSTUN) - StunClient::SendHolePunchingMessages(enetClient, serverAddress.c_str(), serverPort); - - if (!g_NetClient->SetupConnection(serverAddress, serverPort, enetClient)) - { - pCxPrivate->pScriptInterface->ReportError("Failed to connect to server"); - SAFE_DELETE(g_NetClient); - SAFE_DELETE(g_Game); - } -} - -u16 GetDefaultPort(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) -{ - return PS_DEFAULT_PORT; -} - -void 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); -} - -std::string GetPlayerGUID(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) -{ - if (!g_NetClient) - return "local"; - - return g_NetClient->GetGUID(); -} - -void KickPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStrW& playerName, bool ban) -{ - ENSURE(g_NetClient); - - g_NetClient->SendKickPlayerMessage(playerName, ban); -} - -JS::Value 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 AssignNetworkPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int playerID, const std::string& guid) -{ - ENSURE(g_NetClient); - - g_NetClient->SendAssignPlayerMessage(playerID, guid); -} - -void ClearAllPlayerReady (ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) -{ - ENSURE(g_NetClient); - - g_NetClient->SendClearAllReadyMessage(); -} - -void SendNetworkChat(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::wstring& message) -{ - ENSURE(g_NetClient); - - g_NetClient->SendChatMessage(message); -} - -void SendNetworkReady(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int message) -{ - ENSURE(g_NetClient); - - g_NetClient->SendReadyMessage(message); -} - JS::Value GetAIs(ScriptInterface::CxPrivate* pCxPrivate) { return ICmpAIManager::GetAIs(*(pCxPrivate->pScriptInterface)); @@ -524,14 +348,6 @@ float GetSimRate(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) return g_Game->GetSimRate(); } -void SetTurnLength(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int length) -{ - if (g_NetServer) - g_NetServer->SetTurnLength(length); - else - LOGERROR("Only network host can change turn length"); -} - // Deliberately cause the game to crash. // Currently implemented via access violation (read of address 0). // Useful for testing the crashlog/stack trace code. @@ -765,6 +581,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface) JSI_Console::RegisterScriptFunctions(scriptInterface); JSI_ConfigDB::RegisterScriptFunctions(scriptInterface); JSI_Mod::RegisterScriptFunctions(scriptInterface); + JSI_Network::RegisterScriptFunctions(scriptInterface); JSI_SavedGame::RegisterScriptFunctions(scriptInterface); JSI_Sound::RegisterScriptFunctions(scriptInterface); JSI_L10n::RegisterScriptFunctions(scriptInterface); @@ -791,24 +608,9 @@ void GuiScriptingInit(ScriptInterface& scriptInterface) scriptInterface.RegisterFunction, &PickNonGaiaEntitiesOnScreen>("PickNonGaiaEntitiesOnScreen"); scriptInterface.RegisterFunction, std::string, bool, bool, bool, &PickSimilarPlayerEntities>("PickSimilarPlayerEntities"); - // Network / game setup functions - scriptInterface.RegisterFunction("StartNetworkGame"); scriptInterface.RegisterFunction("StartGame"); scriptInterface.RegisterFunction("EndGame"); - scriptInterface.RegisterFunction("StartNetworkHost"); - scriptInterface.RegisterFunction("StartNetworkJoin"); - scriptInterface.RegisterFunction("GetDefaultPort"); - scriptInterface.RegisterFunction("DisconnectNetworkGame"); - scriptInterface.RegisterFunction("GetPlayerGUID"); - scriptInterface.RegisterFunction("KickPlayer"); - scriptInterface.RegisterFunction("PollNetworkClient"); - scriptInterface.RegisterFunction("SetNetworkGameAttributes"); - scriptInterface.RegisterFunction("AssignNetworkPlayer"); - scriptInterface.RegisterFunction("ClearAllPlayerReady"); - scriptInterface.RegisterFunction("SendNetworkChat"); - scriptInterface.RegisterFunction("SendNetworkReady"); scriptInterface.RegisterFunction("GetAIs"); - scriptInterface.RegisterFunction("FindStunEndpoint"); // Misc functions scriptInterface.RegisterFunction("SetCursor"); @@ -847,7 +649,6 @@ void GuiScriptingInit(ScriptInterface& scriptInterface) scriptInterface.RegisterFunction("GetMicroseconds"); scriptInterface.RegisterFunction("SetSimRate"); scriptInterface.RegisterFunction("GetSimRate"); - scriptInterface.RegisterFunction("SetTurnLength"); scriptInterface.RegisterFunction("Crash"); scriptInterface.RegisterFunction("DebugWarn"); scriptInterface.RegisterFunction("ForceGC"); diff --git a/source/network/scripting/JSInterface_Network.cpp b/source/network/scripting/JSInterface_Network.cpp new file mode 100644 index 0000000000..46c7ff2aa6 --- /dev/null +++ b/source/network/scripting/JSInterface_Network.cpp @@ -0,0 +1,230 @@ +/* Copyright (C) 2017 Wildfire Games. + * 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 . + */ + +#include "precompiled.h" + +#include "network/scripting/JSInterface_Network.h" + +#include "lib/external_libraries/enet.h" +#include "lib/external_libraries/libsdl.h" +#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/CStr.h" +#include "ps/Game.h" + +u16 JSI_Network::GetDefaultPort(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) +{ + return PS_DEFAULT_PORT; +} + +JS::Value JSI_Network::FindStunEndpoint(ScriptInterface::CxPrivate* pCxPrivate, int port) +{ + return StunClient::FindStunEndpointHost(*(pCxPrivate->pScriptInterface), port); +} + +void JSI_Network::StartNetworkHost(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& playerName, const u16 serverPort) +{ + ENSURE(!g_NetClient); + ENSURE(!g_NetServer); + ENSURE(!g_Game); + + g_NetServer = new CNetServer(); + 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); + + if (!g_NetClient->SetupConnection("127.0.0.1", serverPort)) + { + 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; + } + + StunClient::StunEndpoint* stunEndpoint = StunClient::FindStunEndpointJoin(enetClient); + if (!stunEndpoint) + { + pCxPrivate->pScriptInterface->ReportError("Could not find the STUN endpoint"); + return; + } + + g_XmppClient->SendStunEndpointToHost(stunEndpoint, hostJID); + delete stunEndpoint; + + SDL_Delay(1000); + } + + g_Game = new CGame(); + g_NetClient = new CNetClient(g_Game, false); + g_NetClient->SetUserName(playerName); + + if (g_XmppClient && useSTUN) + StunClient::SendHolePunchingMessages(enetClient, serverAddress.c_str(), serverPort); + + 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("GetDefaultPort"); + scriptInterface.RegisterFunction("FindStunEndpoint"); + scriptInterface.RegisterFunction("StartNetworkHost"); + scriptInterface.RegisterFunction("StartNetworkJoin"); + scriptInterface.RegisterFunction("DisconnectNetworkGame"); + scriptInterface.RegisterFunction("GetPlayerGUID"); + scriptInterface.RegisterFunction("PollNetworkClient"); + scriptInterface.RegisterFunction("SetNetworkGameAttributes"); + scriptInterface.RegisterFunction("AssignNetworkPlayer"); + scriptInterface.RegisterFunction("KickPlayer"); + scriptInterface.RegisterFunction("SendNetworkChat"); + scriptInterface.RegisterFunction("SendNetworkReady"); + scriptInterface.RegisterFunction("ClearAllPlayerReady"); + scriptInterface.RegisterFunction("StartNetworkGame"); + scriptInterface.RegisterFunction("SetTurnLength"); +} diff --git a/source/network/scripting/JSInterface_Network.h b/source/network/scripting/JSInterface_Network.h new file mode 100644 index 0000000000..0562125444 --- /dev/null +++ b/source/network/scripting/JSInterface_Network.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2017 Wildfire Games. + * 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 . + */ + +#ifndef INCLUDED_JSI_NETWORK +#define INCLUDED_JSI_NETWORK + +#include "scriptinterface/ScriptInterface.h" + +namespace JSI_Network +{ + u16 GetDefaultPort(ScriptInterface::CxPrivate* pCxPrivate); + void StartNetworkGame(ScriptInterface::CxPrivate* pCxPrivate); + void SetNetworkGameAttributes(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue attribs1); + void StartNetworkHost(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& playerName, const u16 serverPort); + void StartNetworkJoin(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& playerName, const CStr& serverAddress, u16 serverPort, bool useSTUN, const CStr& hostJID); + JS::Value FindStunEndpoint(ScriptInterface::CxPrivate* pCxPrivate, int port); + void DisconnectNetworkGame(ScriptInterface::CxPrivate* pCxPrivate); + JS::Value PollNetworkClient(ScriptInterface::CxPrivate* pCxPrivate); + CStr GetPlayerGUID(ScriptInterface::CxPrivate* pCxPrivate); + void KickPlayer(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& playerName, bool ban); + void AssignNetworkPlayer(ScriptInterface::CxPrivate* pCxPrivate, int playerID, const CStr& guid); + void ClearAllPlayerReady (ScriptInterface::CxPrivate* pCxPrivate); + void SendNetworkChat(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& message); + void SendNetworkReady(ScriptInterface::CxPrivate* pCxPrivate, int message); + void SetTurnLength(ScriptInterface::CxPrivate* pCxPrivate, int length); + + void RegisterScriptFunctions(const ScriptInterface& scriptInterface); +} + +#endif