Let the Gamesetup.cpp Autostart (starting from cl) use the gamesettings via the autostart gui page.

Comments and testing By: Langbart
Fixes #6433
Differential Revision: D4492
Includes/Obsoletes D4287

This was SVN commit r26584.
This commit is contained in:
bb 2022-03-06 21:12:16 +00:00
parent 2af020bab1
commit 8eecc39e71
21 changed files with 288 additions and 238 deletions

View File

@ -1,17 +1,12 @@
function init(initData)
{
let settings = new GameSettings().init();
settings.fromInitAttributes(initData);
let assignments = {
"local": {
"player": 1,
"name": Engine.ConfigDB_GetValue("user", "playername.singleplayer") || Engine.GetSystemUsername()
}
};
settings.launchGame(assignments);
settings.fromInitAttributes(initData.attribs);
settings.launchGame(initData.playerAssignments, initData.storeReplay);
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": settings.finalizedAttributes,
"playerAssignments": assignments
"playerAssignments": initData.playerAssignments
});
}

View File

@ -4,5 +4,5 @@
<script directory="gui/common/"/>
<script directory="gui/gamesettings/"/>
<script directory="gui/gamesettings/attributes/"/>
<script directory="gui/autostart/"/>
<script file="gui/autostart/autostart.js"/>
</objects>

View File

@ -0,0 +1,54 @@
class AutoStartClient
{
constructor(initData)
{
Engine.GetGUIObjectByName("ticker").onTick = this.onTick.bind(this);
this.playerAssignments = {};
try
{
Engine.StartNetworkJoin(initData.playerName, initData.ip, initData.port, initData.storeReplay);
}
catch (e)
{
messageBox(
400, 200,
sprintf(translate("Cannot join game: %(message)s."), { "message": e.message }),
translate("Error")
);
}
}
onTick()
{
while (true)
{
const message = Engine.PollNetworkClient();
if (!message)
break;
switch (message.type)
{
case "players":
this.playerAssignments = message.newAssignments;
break;
case "start":
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": message.initAttributes,
"isRejoining": true,
"playerAssignments": this.playerAssignments
});
// Process further pending netmessages in the session page.
return;
default:
}
}
}
}
function init(initData)
{
new AutoStartClient(initData);
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<objects>
<script directory="gui/common/"/>
<script file="gui/autostart/autostart_client.js"/>
<object name="ticker"/>
</objects>

View File

@ -0,0 +1,61 @@
class AutoStartHost
{
constructor(initData)
{
this.maxPlayers = initData.maxPlayers;
this.storeReplay = initData.storeReplay;
this.playerAssignments = {};
Engine.GetGUIObjectByName("ticker").onTick = this.onTick.bind(this);
try
{
// Stun and password not implemented for autostart.
Engine.StartNetworkHost(initData.playerName, initData.port, false, "", initData.storeReplay);
}
catch (e)
{
messageBox(
400, 200,
sprintf(translate("Cannot host game: %(message)s."), { "message": e.message }),
translate("Error")
);
}
this.settings = new GameSettings().init();
this.settings.fromInitAttributes(initData.attribs);
}
onTick()
{
while (true)
{
const message = Engine.PollNetworkClient();
if (!message)
break;
switch (message.type)
{
case "players":
this.playerAssignments = message.newAssignments;
break;
default:
}
}
if (Object.keys(this.playerAssignments).length == this.maxPlayers)
{
this.settings.launchGame(this.playerAssignments, this.storeReplay);
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": this.settings.finalizedAttributes,
"playerAssignments": this.playerAssignments
});
}
}
}
function init(initData)
{
new AutoStartHost(initData);
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<objects>
<script file="gui/maps/MapCache.js"/>
<script directory="gui/common/"/>
<script directory="gui/gamesettings/"/>
<script directory="gui/gamesettings/attributes/"/>
<script file="gui/autostart/autostart_host.js"/>
<object name="ticker"/>
</objects>

View File

@ -95,7 +95,7 @@ class CampaignMenu extends AutoWatcher
return;
}
gameSettings.launchGame(assignments);
gameSettings.launchGame(assignments, true);
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": gameSettings.finalizedAttributes,
"playerAssignments": assignments

View File

@ -125,7 +125,7 @@ class GameSettings
* since you'll need a GameSettings object anyways.
* @param playerAssignments - A dict of 'local'/GUID per player and their name/slot.
*/
launchGame(playerAssignments)
launchGame(playerAssignments, storeReplay)
{
this.pickRandomItems();
@ -142,9 +142,9 @@ class GameSettings
// NB: for multiplayer support, the clients must be listening to "start" net messages.
if (this.isNetworked)
Engine.StartNetworkGame(this.finalizedAttributes);
Engine.StartNetworkGame(this.finalizedAttributes, storeReplay);
else
Engine.StartGame(this.finalizedAttributes, playerAssignments.local.player);
Engine.StartGame(this.finalizedAttributes, playerAssignments.local.player, storeReplay);
}
}

View File

@ -16,7 +16,8 @@ GameSettings.prototype.Attributes.CircularMap = class CircularMap extends GameSe
fromInitAttributes(attribs)
{
this.value = !!this.getLegacySetting(attribs, "CircularMap");
if (this.getLegacySetting(attribs, "CircularMap") !== undefined)
this.value = !!this.getLegacySetting(attribs, "CircularMap");
}
onMapChange()

View File

@ -270,7 +270,7 @@ class GameSettingsController
// This will resolve random settings & send game start messages.
// TODO: this will trigger observers, which is somewhat wasteful.
g_GameSettings.launchGame(g_PlayerAssignments);
g_GameSettings.launchGame(g_PlayerAssignments, true);
// Switch to the loading page right away,
// the GUI will otherwise show the unrandomised settings.

View File

@ -366,7 +366,7 @@ function startHost(playername, servername, port, password)
try
{
Engine.StartNetworkHost(playername + (g_UserRating ? " (" + g_UserRating + ")" : ""), port, useSTUN, password);
Engine.StartNetworkHost(playername + (g_UserRating ? " (" + g_UserRating + ")" : ""), port, useSTUN, password, true);
}
catch (e)
{
@ -395,7 +395,7 @@ function startJoin(playername, ip, port)
{
try
{
Engine.StartNetworkJoin(playername, ip, port);
Engine.StartNetworkJoin(playername, ip, port, true);
}
catch (e)
{

View File

@ -2,7 +2,7 @@
<page>
<!--
This is simply a convenience forwarder to avoid loading
the gameSettings from the main menu.
the gameSettings from the main menu or command line.
-->
<include>autostart/autostart.xml</include>
</page>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<page>
<!--
This is simply a convenience forwarder to avoid loading
the gamesetup_mp when autostarting from command line.
-->
<include>common/modern/styles.xml</include>
<include>autostart/autostart_client.xml</include>
</page>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<page>
<!--
This is simply a convenience forwarder to avoid loading
the gamesetup_mp when autostarting from command line.
-->
<include>common/modern/styles.xml</include>
<include>autostart/autostart_host.xml</include>
</page>

View File

@ -15,11 +15,20 @@ var g_MainMenuItems = [
"tooltip": translate("Start the economic tutorial."),
"onPress": () => {
Engine.SwitchGuiPage("page_autostart.xml", {
"mapType": "scenario",
"map": "maps/tutorials/starting_economy_walkthrough",
"settings": {
"CheatsEnabled": true
}
"attribs": {
"mapType": "scenario",
"map": "maps/tutorials/starting_economy_walkthrough",
"settings": {
"CheatsEnabled": true
},
},
"playerAssignments": {
"local": {
"player": 1,
"name": Engine.ConfigDB_GetValue("user", "playername.singleplayer") || Engine.GetSystemUsername()
}
},
"storeReplay": true
});
}
},

View File

@ -13,7 +13,7 @@ Autostart:
-autostart-aidiff=PLAYER:DIFF sets the DIFFiculty of PLAYER's AI (0: sandbox, 5: very hard)
-autostart-aiseed=AISEED sets the seed used for the AI random generator (default 0, use -1 for random)
-autostart-player=NUMBER sets the playerID in non-networked games (default 1, use -1 for observer)
-autostart-civ=PLAYER:CIV sets PLAYER's civilisation to CIV (skirmish and random maps only)
-autostart-civ=PLAYER:CIV sets PLAYER's civilisation to CIV (skirmish and random maps only). Use random for a random civ.
-autostart-team=PLAYER:TEAM sets the team for PLAYER (e.g. 2:2).
-autostart-ceasefire=NUM sets a ceasefire duration NUM (default 0 minutes)
-autostart-nonvisual disable any graphics and sounds
@ -33,9 +33,9 @@ Multiplayer:
Examples:
1) "Bob" will host a 2 player game on the Arcadia map:
-autostart="scenarios/Arcadia" -autostart-host -autostart-host-players=2 -autostart-playername="Bob"
-autostart="scenarios/arcadia" -autostart-host -autostart-host-players=2 -autostart-playername="Bob"
"Alice" joins the match as player 2:
-autostart="scenarios/Arcadia" -autostart-client=127.0.0.1 -autostart-playername="Alice"
-autostart-client=127.0.0.1 -autostart-playername="Alice"
The players use the developer overlay to control players.
2) Load Alpine Lakes random map with random seed, 2 players (Athens and Britons), and player 2 is PetraBot:
-autostart="random/alpine_lakes" -autostart-seed=-1 -autostart-players=2 -autostart-civ=1:athen -autostart-civ=2:brit -autostart-ai=2:petra

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2022 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -145,8 +145,7 @@ private:
* See http://trac.wildfiregames.com/ticket/654
*/
CNetServerWorker::CNetServerWorker(bool useLobbyAuth, int autostartPlayers) :
m_AutostartPlayers(autostartPlayers),
CNetServerWorker::CNetServerWorker(bool useLobbyAuth) :
m_LobbyAuth(useLobbyAuth),
m_Shutdown(false),
m_ScriptInterface(NULL),
@ -429,10 +428,6 @@ void CNetServerWorker::Run()
if (!RunStep())
break;
// Implement autostart mode
if (m_State == SERVER_STATE_PREGAME && (int)m_PlayerAssignments.size() == m_AutostartPlayers)
StartGame(Script::StringifyJSON(ScriptRequest(m_ScriptInterface), &m_InitAttributes));
// Update profiler stats
m_Stats->LatchHostState(m_Host);
}
@ -1624,8 +1619,8 @@ void CNetServerWorker::SendHolePunchingMessage(const CStr& ipStr, u16 port)
CNetServer::CNetServer(bool useLobbyAuth, int autostartPlayers) :
m_Worker(new CNetServerWorker(useLobbyAuth, autostartPlayers)),
CNetServer::CNetServer(bool useLobbyAuth) :
m_Worker(new CNetServerWorker(useLobbyAuth)),
m_LobbyAuth(useLobbyAuth), m_UseSTUN(false), m_PublicIp(""), m_PublicPort(20595), m_Password()
{
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2022 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -110,10 +110,9 @@ class CNetServer
public:
/**
* Construct a new network server.
* @param autostartPlayers - if positive then StartGame will be called automatically
* once this many players are connected (intended for the command-line testing mode).
*/
CNetServer(bool useLobbyAuth = false, int autostartPlayers = -1);
CNetServer(bool useLobbyAuth = false);
~CNetServer();
@ -236,7 +235,7 @@ private:
friend class CNetServer;
friend class CNetFileReceiveTask_ServerRejoin;
CNetServerWorker(bool useLobbyAuth, int autostartPlayers);
CNetServerWorker(bool useLobbyAuth);
~CNetServerWorker();
bool CheckPassword(const std::string& password, const std::string& salt) const;
@ -350,8 +349,6 @@ private:
*/
JS::PersistentRootedValue m_InitAttributes;
int m_AutostartPlayers;
/**
* Whether this match requires lobby authentication.
*/

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2022 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -62,7 +62,7 @@ bool HasNetClient()
return !!g_NetClient;
}
void StartNetworkHost(const ScriptRequest& rq, const CStrW& playerName, const u16 serverPort, bool useSTUN, const CStr& password)
void StartNetworkHost(const ScriptRequest& rq, const CStrW& playerName, const u16 serverPort, bool useSTUN, const CStr& password, bool storeReplay)
{
ENSURE(!g_NetClient);
ENSURE(!g_NetServer);
@ -100,7 +100,7 @@ void StartNetworkHost(const ScriptRequest& rq, const CStrW& playerName, const u1
std::string secret = ps_generate_guid();
g_NetServer->SetControllerSecret(secret);
g_Game = new CGame(true);
g_Game = new CGame(storeReplay);
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(playerName);
@ -141,13 +141,13 @@ void StartNetworkHost(const ScriptRequest& rq, const CStrW& playerName, const u1
}
}
void StartNetworkJoin(const ScriptRequest& rq, const CStrW& playerName, const CStr& serverAddress, u16 serverPort)
void StartNetworkJoin(const ScriptRequest& rq, const CStrW& playerName, const CStr& serverAddress, u16 serverPort, bool storeReplay)
{
ENSURE(!g_NetClient);
ENSURE(!g_NetServer);
ENSURE(!g_Game);
g_Game = new CGame(true);
g_Game = new CGame(storeReplay);
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(playerName);
g_NetClient->SetupServerData(serverAddress, serverPort, false);

View File

@ -219,34 +219,6 @@ static void InitPs(bool setup_gui, const CStrW& gui_page, ScriptInterface* srcSc
g_GUI->SwitchPage(gui_page, srcScriptInterface, initData);
}
void InitPsAutostart(bool networked, JS::HandleValue attrs)
{
// The GUI has not been initialized yet, so use the simulation scriptinterface for this variable
ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
ScriptRequest rq(scriptInterface);
JS::RootedValue playerAssignments(rq.cx);
Script::CreateObject(rq, &playerAssignments);
if (!networked)
{
JS::RootedValue localPlayer(rq.cx);
Script::CreateObject(rq, &localPlayer, "player", g_Game->GetPlayerID());
Script::SetProperty(rq, playerAssignments, "local", localPlayer);
}
JS::RootedValue sessionInitData(rq.cx);
Script::CreateObject(
rq,
&sessionInitData,
"attribs", attrs,
"playerAssignments", playerAssignments);
InitPs(true, L"page_loading.xml", &scriptInterface, sessionInitData);
}
void InitInput()
{
g_Joystick.Initialise();
@ -807,8 +779,8 @@ CStr8 LoadSettingsOfScenarioMap(const VfsPath &mapPath)
* -autostart-aiseed=AISEED sets the seed used for the AI random
* generator (default 0, use -1 for random)
* -autostart-player=NUMBER sets the playerID in non-networked games (default 1, use -1 for observer)
* -autostart-civ=PLAYER:CIV sets PLAYER's civilisation to CIV
* (skirmish and random maps only)
* -autostart-civ=PLAYER:CIV sets PLAYER's civilisation to CIV (skirmish and random maps only).
* Use random for a random civ.
* -autostart-team=PLAYER:TEAM sets the team for PLAYER (e.g. 2:2).
* -autostart-ceasefire=NUM sets a ceasefire duration NUM
* (default 0 minutes)
@ -839,9 +811,9 @@ CStr8 LoadSettingsOfScenarioMap(const VfsPath &mapPath)
*
* Examples:
* 1) "Bob" will host a 2 player game on the Arcadia map:
* -autostart="scenarios/Arcadia" -autostart-host -autostart-host-players=2 -autostart-playername="Bob"
* -autostart="scenarios/arcadia" -autostart-host -autostart-host-players=2 -autostart-playername="Bob"
* "Alice" joins the match as player 2:
* -autostart="scenarios/Arcadia" -autostart-client=127.0.0.1 -autostart-playername="Alice"
* -autostart-client=127.0.0.1 -autostart-playername="Alice"
* The players use the developer overlay to control players.
*
* 2) Load Alpine Lakes random map with random seed, 2 players (Athens and Britons), and player 2 is PetraBot:
@ -852,16 +824,41 @@ CStr8 LoadSettingsOfScenarioMap(const VfsPath &mapPath)
*/
bool Autostart(const CmdLineArgs& args)
{
// Get optional playername.
CStrW userName = L"anonymous";
if (args.Has("autostart-playername"))
userName = args.Get("autostart-playername").FromUTF8();
// Create some scriptinterface to store the js values for the settings.
ScriptInterface scriptInterface("Engine", "Game Setup", g_ScriptContext);
ScriptRequest rq(scriptInterface);
JS::RootedValue sessionInitData(rq.cx);
if (args.Has("autostart-client"))
{
CStr ip = args.Get("autostart-client");
if (ip.empty())
ip = "127.0.0.1";
Script::CreateObject(
rq,
&sessionInitData,
"playerName", userName,
"ip", ip,
"port", PS_DEFAULT_PORT,
"storeReplay", !args.Has("autostart-disable-replay"));
InitPs(true, L"page_autostart_client.xml", &scriptInterface, sessionInitData);
return true;
}
CStr autoStartName = args.Get("autostart");
if (autoStartName.empty())
return false;
g_Game = new CGame(!args.Has("autostart-disable-replay"));
ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
ScriptRequest rq(scriptInterface);
JS::RootedValue attrs(rq.cx);
JS::RootedValue settings(rq.cx);
JS::RootedValue playerData(rq.cx);
@ -881,28 +878,6 @@ bool Autostart(const CmdLineArgs& args)
if (mapDirectory == L"random")
{
// Random map definition will be loaded from JSON file, so we need to parse it
std::wstring scriptPath = L"maps/" + autoStartName.FromUTF8() + L".json";
JS::RootedValue scriptData(rq.cx);
Script::ReadJSONFile(rq, scriptPath, &scriptData);
if (!scriptData.isUndefined() && Script::GetProperty(rq, scriptData, "settings", &settings))
{
// JSON loaded ok - copy script name over to game attributes
std::wstring scriptFile;
if (!Script::GetProperty(rq, settings, "Script", scriptFile))
{
LOGERROR("Autostart: random map '%s' data has no 'Script' property.", utf8_from_wstring(scriptPath));
throw PSERROR_Game_World_MapLoadFailed("Error reading random map script.\nCheck application log for details.");
}
Script::SetProperty(rq, attrs, "script", scriptFile); // RMS filename
}
else
{
// Problem with JSON file
LOGERROR("Autostart: Error reading random map script '%s'", utf8_from_wstring(scriptPath));
throw PSERROR_Game_World_MapLoadFailed("Error reading random map script.\nCheck application log for details.");
}
// Get optional map size argument (default 192)
uint mapSize = 192;
if (args.Has("autostart-size"))
@ -934,23 +909,10 @@ bool Autostart(const CmdLineArgs& args)
}
mapType = "random";
}
else if (mapDirectory == L"scenarios" || mapDirectory == L"skirmishes")
{
// Initialize general settings from the map data so some values
// (e.g. name of map) are always present, even when autostart is
// partially configured
CStr8 mapSettingsJSON = LoadSettingsOfScenarioMap("maps/" + autoStartName + ".xml");
Script::ParseJSON(rq, mapSettingsJSON, &settings);
// Initialize the playerData array being modified by autostart
// with the real map data, so sensible values are present:
Script::GetProperty(rq, settings, "PlayerData", &playerData);
if (mapDirectory == L"scenarios")
mapType = "scenario";
else
mapType = "skirmish";
}
else if (mapDirectory == L"scenarios")
mapType = "scenario";
else if (mapDirectory == L"skirmishes")
mapType = "skirmish";
else
{
LOGERROR("Autostart: Unrecognized map type '%s'", utf8_from_wstring(mapDirectory));
@ -1005,15 +967,7 @@ bool Autostart(const CmdLineArgs& args)
// Instead of overwriting existing player data, modify the array
JS::RootedValue currentPlayer(rq.cx);
if (!Script::GetPropertyInt(rq, playerData, playerID-offset, &currentPlayer) || currentPlayer.isUndefined())
{
if (mapDirectory == L"skirmishes")
{
// playerID is certainly bigger than this map player number
LOGWARNING("Autostart: Invalid player %d in autostart-team option", playerID);
continue;
}
Script::CreateObject(rq, &currentPlayer);
}
int teamID = civArgs[i].AfterFirst(":").ToInt() - 1;
Script::SetProperty(rq, currentPlayer, "Team", teamID);
@ -1036,15 +990,7 @@ bool Autostart(const CmdLineArgs& args)
// Instead of overwriting existing player data, modify the array
JS::RootedValue currentPlayer(rq.cx);
if (!Script::GetPropertyInt(rq, playerData, playerID-offset, &currentPlayer) || currentPlayer.isUndefined())
{
if (mapDirectory == L"scenarios" || mapDirectory == L"skirmishes")
{
// playerID is certainly bigger than this map player number
LOGWARNING("Autostart: Invalid player %d in autostart-ai option", playerID);
continue;
}
Script::CreateObject(rq, &currentPlayer);
}
Script::SetProperty(rq, currentPlayer, "AI", aiArgs[i].AfterFirst(":"));
Script::SetProperty(rq, currentPlayer, "AIDiff", 3);
@ -1063,15 +1009,7 @@ bool Autostart(const CmdLineArgs& args)
// Instead of overwriting existing player data, modify the array
JS::RootedValue currentPlayer(rq.cx);
if (!Script::GetPropertyInt(rq, playerData, playerID-offset, &currentPlayer) || currentPlayer.isUndefined())
{
if (mapDirectory == L"scenarios" || mapDirectory == L"skirmishes")
{
// playerID is certainly bigger than this map player number
LOGWARNING("Autostart: Invalid player %d in autostart-aidiff option", playerID);
continue;
}
Script::CreateObject(rq, &currentPlayer);
}
Script::SetProperty(rq, currentPlayer, "AIDiff", civArgs[i].AfterFirst(":").ToInt());
Script::SetPropertyInt(rq, playerData, playerID-offset, currentPlayer);
@ -1090,15 +1028,7 @@ bool Autostart(const CmdLineArgs& args)
// Instead of overwriting existing player data, modify the array
JS::RootedValue currentPlayer(rq.cx);
if (!Script::GetPropertyInt(rq, playerData, playerID-offset, &currentPlayer) || currentPlayer.isUndefined())
{
if (mapDirectory == L"skirmishes")
{
// playerID is certainly bigger than this map player number
LOGWARNING("Autostart: Invalid player %d in autostart-civ option", playerID);
continue;
}
Script::CreateObject(rq, &currentPlayer);
}
Script::SetProperty(rq, currentPlayer, "Civ", civArgs[i].AfterFirst(":"));
Script::SetPropertyInt(rq, playerData, playerID-offset, currentPlayer);
@ -1108,17 +1038,6 @@ bool Autostart(const CmdLineArgs& args)
LOGWARNING("Autostart: Option 'autostart-civ' is invalid for scenarios");
}
// Add player data to map settings
Script::SetProperty(rq, settings, "PlayerData", playerData);
// Add map settings to game attributes
Script::SetProperty(rq, attrs, "settings", settings);
// Get optional playername
CStrW userName = L"anonymous";
if (args.Has("autostart-playername"))
userName = args.Get("autostart-playername").FromUTF8();
// Add additional scripts to the TriggerScripts property
std::vector<CStrW> triggerScriptsVector;
JS::RootedValue triggerScripts(rq.cx);
@ -1135,6 +1054,9 @@ bool Autostart(const CmdLineArgs& args)
triggerScriptsVector.push_back(nonVisualScript.FromUTF8());
}
Script::ToJSVal(rq, &triggerScripts, triggerScriptsVector);
Script::SetProperty(rq, settings, "TriggerScripts", triggerScripts);
std::vector<CStr> victoryConditions(1, "conquest");
if (args.Has("autostart-victory"))
victoryConditions = args.GetMultiple("autostart-victory");
@ -1144,31 +1066,6 @@ bool Autostart(const CmdLineArgs& args)
Script::SetProperty(rq, settings, "VictoryConditions", victoryConditions);
for (const CStr& victory : victoryConditions)
{
JS::RootedValue scriptData(rq.cx);
JS::RootedValue data(rq.cx);
JS::RootedValue victoryScripts(rq.cx);
CStrW scriptPath = L"simulation/data/settings/victory_conditions/" + victory.FromUTF8() + L".json";
Script::ReadJSONFile(rq, scriptPath, &scriptData);
if (!scriptData.isUndefined() && Script::GetProperty(rq, scriptData, "Data", &data) && !data.isUndefined()
&& Script::GetProperty(rq, data, "Scripts", &victoryScripts) && !victoryScripts.isUndefined())
{
std::vector<CStrW> victoryScriptsVector;
Script::FromJSVal(rq, victoryScripts, victoryScriptsVector);
triggerScriptsVector.insert(triggerScriptsVector.end(), victoryScriptsVector.begin(), victoryScriptsVector.end());
}
else
{
LOGERROR("Autostart: Error reading victory script '%s'", utf8_from_wstring(scriptPath));
throw PSERROR_Game_World_MapLoadFailed("Error reading victory script.\nCheck application log for details.");
}
}
Script::ToJSVal(rq, &triggerScripts, triggerScriptsVector);
Script::SetProperty(rq, settings, "TriggerScripts", triggerScripts);
int wonderDuration = 10;
if (args.Has("autostart-wonderduration"))
wonderDuration = args.Get("autostart-wonderduration").ToInt();
@ -1184,60 +1081,50 @@ bool Autostart(const CmdLineArgs& args)
relicCount = args.Get("autostart-reliccount").ToInt();
Script::SetProperty(rq, settings, "RelicCount", relicCount);
// Add player data to map settings.
Script::SetProperty(rq, settings, "PlayerData", playerData);
// Add map settings to game attributes.
Script::SetProperty(rq, attrs, "settings", settings);
if (args.Has("autostart-host"))
{
InitPsAutostart(true, attrs);
size_t maxPlayers = 2;
int maxPlayers = 2;
if (args.Has("autostart-host-players"))
maxPlayers = args.Get("autostart-host-players").ToUInt();
// Generate a secret to identify the host client.
std::string secret = ps_generate_guid();
Script::CreateObject(
rq,
&sessionInitData,
"attribs", attrs,
"playerName", userName,
"port", PS_DEFAULT_PORT,
"maxPlayers", maxPlayers,
"storeReplay", !args.Has("autostart-disable-replay"));
g_NetServer = new CNetServer(false, maxPlayers);
g_NetServer->SetControllerSecret(secret);
g_NetServer->UpdateInitAttributes(&attrs, scriptInterface);
bool ok = g_NetServer->SetupConnection(PS_DEFAULT_PORT);
ENSURE(ok);
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(userName);
g_NetClient->SetupServerData("127.0.0.1", PS_DEFAULT_PORT, false);
g_NetClient->SetControllerSecret(secret);
g_NetClient->SetupConnection(nullptr);
}
else if (args.Has("autostart-client"))
{
InitPsAutostart(true, attrs);
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(userName);
CStr ip = args.Get("autostart-client");
if (ip.empty())
ip = "127.0.0.1";
g_NetClient->SetupServerData(ip, PS_DEFAULT_PORT, false);
ENSURE(g_NetClient->SetupConnection(nullptr));
InitPs(true, L"page_autostart_host.xml", &scriptInterface, sessionInitData);
}
else
{
g_Game->SetPlayerID(args.Has("autostart-player") ? args.Get("autostart-player").ToInt() : 1);
JS::RootedValue localPlayer(rq.cx);
Script::CreateObject(
rq,
&localPlayer,
"player", args.Has("autostart-player") ? args.Get("autostart-player").ToInt() : 1,
"name", userName);
g_Game->StartGame(&attrs, "");
JS::RootedValue playerAssignments(rq.cx);
Script::CreateObject(rq, &playerAssignments);
Script::SetProperty(rq, playerAssignments, "local", localPlayer);
if (CRenderer::IsInitialised())
{
InitPsAutostart(false, attrs);
}
else
{
// TODO: Non progressive load can fail - need a decent way to handle this
LDR_NonprogressiveLoad();
ENSURE(g_Game->ReallyStartGame() == PSRETURN_OK);
}
Script::CreateObject(
rq,
&sessionInitData,
"attribs", attrs,
"playerAssignments", playerAssignments,
"storeReplay", !args.Has("autostart-disable-replay"));
InitPs(true, L"page_autostart.xml", &scriptInterface, sessionInitData);
}
return true;
@ -1256,7 +1143,21 @@ bool AutostartVisualReplay(const std::string& replayFile)
ScriptRequest rq(scriptInterface);
JS::RootedValue attrs(rq.cx, g_Game->GetSimulation2()->GetInitAttributes());
InitPsAutostart(false, attrs);
JS::RootedValue playerAssignments(rq.cx);
Script::CreateObject(rq, &playerAssignments);
JS::RootedValue localPlayer(rq.cx);
Script::CreateObject(rq, &localPlayer, "player", g_Game->GetPlayerID());
Script::SetProperty(rq, playerAssignments, "local", localPlayer);
JS::RootedValue sessionInitData(rq.cx);
Script::CreateObject(
rq,
&sessionInitData,
"attribs", attrs,
"playerAssignments", playerAssignments);
InitPs(true, L"page_loading.xml", &scriptInterface, sessionInitData);
return true;
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2022 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -41,13 +41,13 @@ bool IsGameStarted()
return g_Game;
}
void StartGame(const ScriptInterface& guiInterface, JS::HandleValue attribs, int playerID)
void StartGame(const ScriptInterface& guiInterface, JS::HandleValue attribs, int playerID, bool storeReplay)
{
ENSURE(!g_NetServer);
ENSURE(!g_NetClient);
ENSURE(!g_Game);
g_Game = new CGame(true);
g_Game = new CGame(storeReplay);
// Convert from GUI script context to sim script context/
CSimulation2* sim = g_Game->GetSimulation2();