diff --git a/binaries/data/mods/public/autostart/autostart.js b/binaries/data/mods/public/autostart/autostart.js index d997164873..5953ceadc0 100644 --- a/binaries/data/mods/public/autostart/autostart.js +++ b/binaries/data/mods/public/autostart/autostart.js @@ -1,36 +1,22 @@ -class AutoStart +async function autoStart(cmdLineArgs) { - constructor(cmdLineArgs) - { - this.playerAssignments = { - "local": { - "player": +(cmdLineArgs?.['autostart-player'] ?? 1), - "name": "anonymous", - }, - }; - this.settings = new GameSettings().init(); + const playerAssignments = { + "local": { + "player": +(cmdLineArgs?.['autostart-player'] ?? 1), + "name": "anonymous", + }, + }; + const settings = new GameSettings().init(); - // Enable cheats in SP - this.settings.cheats.setEnabled(true); + // Enable cheats in SP + settings.cheats.setEnabled(true); - parseCmdLineArgs(this.settings, cmdLineArgs); + parseCmdLineArgs(settings, cmdLineArgs); - this.settings.launchGame(this.playerAssignments, !('autostart-disable-replay' in cmdLineArgs)); - this.onLaunch(); - } + settings.launchGame(playerAssignments, !('autostart-disable-replay' in cmdLineArgs)); - onTick() - { - } - - /** - * In the visual autostart path, we need to show the loading screen. - */ - onLaunch() - { - Engine.SwitchGuiPage("page_loading.xml", { - "attribs": this.settings.finalizedAttributes, - "playerAssignments": this.playerAssignments - }); - } + return ["page_loading.xml", { + "attribs": settings.finalizedAttributes, + "playerAssignments": playerAssignments + }]; } diff --git a/binaries/data/mods/public/autostart/autostart_client.js b/binaries/data/mods/public/autostart/autostart_client.js index b3f49de13d..cb73230e06 100644 --- a/binaries/data/mods/public/autostart/autostart_client.js +++ b/binaries/data/mods/public/autostart/autostart_client.js @@ -1,61 +1,36 @@ -class AutoStartClient +async function autoStartClient(cmdLineArgs) { - done = false; - constructor(cmdLineArgs) + try { - this.playerAssignments = {}; - - try - { - const playerName = cmdLineArgs['autostart-playername'] || "anonymous"; - const ip = cmdLineArgs['autostart-client'] ?? "127.0.0.1"; - const port = +(cmdLineArgs['autostart-port'] ?? 5073); - Engine.StartNetworkJoin(playerName, ip, port, !('autostart-disable-replay' in cmdLineArgs)); - } - catch(e) - { - const message = sprintf(translate("Cannot join game: %(message)s."), { "message": e.message }); - messageBox(400, 200, message, translate("Error")); - } - - (async() => - { - while (true) - { - const message = await Engine.PollNetworkClient(); - - switch (message.type) - { - case "players": - this.playerAssignments = message.newAssignments; - Engine.SendNetworkReady(2); - break; - case "start": - this.onLaunch(message); - // Process further pending netmessages in the session page. - this.done = true; - return; - default: - } - } - })(); + const playerName = cmdLineArgs['autostart-playername'] || "anonymous"; + const ip = cmdLineArgs['autostart-client'] ?? "127.0.0.1"; + const port = +(cmdLineArgs['autostart-port'] ?? 5073); + Engine.StartNetworkJoin(playerName, ip, port, !('autostart-disable-replay' in cmdLineArgs)); + } + catch(e) + { + const message = sprintf(translate("Cannot join game: %(message)s."), { "message": e.message }); + messageBox(400, 200, message, translate("Error")); } - onTick() + let playerAssignments = {}; + while (true) { - return this.done; - } + const message = await Engine.PollNetworkClient(); - /** - * In the visual autostart path, we need to show the loading screen. - * Overload this as appropriate, the default implementation works for the public mod. - */ - onLaunch(message) - { - Engine.SwitchGuiPage("page_loading.xml", { - "attribs": message.initAttributes, - "isRejoining": true, - "playerAssignments": this.playerAssignments - }); + switch (message.type) + { + case "players": + playerAssignments = message.newAssignments; + Engine.SendNetworkReady(2); + break; + case "start": + return ["page_loading.xml", { + "attribs": message.initAttributes, + "isRejoining": true, + "playerAssignments": playerAssignments + }]; + default: + } } } diff --git a/binaries/data/mods/public/autostart/autostart_host.js b/binaries/data/mods/public/autostart/autostart_host.js index 9209df6a19..a6bd9ca5c7 100644 --- a/binaries/data/mods/public/autostart/autostart_host.js +++ b/binaries/data/mods/public/autostart/autostart_host.js @@ -1,97 +1,74 @@ -class AutoStartHost +async function autoStartHost(cmdLineArgs) { - done = false; - constructor(cmdLineArgs) + const maxPlayers = +(cmdLineArgs['autostart-host-players'] ?? 2); + + try { - this.launched = false; - this.playerAssignments = {}; + const playerName = cmdLineArgs['autostart-playername'] || "anonymous"; + const port = +(cmdLineArgs['autostart-port'] ?? 5073); - this.maxPlayers = +(cmdLineArgs['autostart-host-players'] ?? 2); - this.cmdLineArgs = cmdLineArgs; - - try - { - const playerName = cmdLineArgs['autostart-playername'] || "anonymous"; - const port = +(cmdLineArgs['autostart-port'] ?? 5073); - - // Password not implemented for autostart. - Engine.StartNetworkHost(playerName, port, "", false, !('autostart-disable-replay' in cmdLineArgs)); - } - catch(e) - { - const message = sprintf(translate("Cannot host game: %(message)s."), { "message": e.message }); - messageBox(400, 200, message, translate("Error")); - } - - /** - * Handles a simple implementation of player assignments. - * Should not need be overloaded in mods unless you want to change that logic. - */ - (async() => - { - while (true) - { - const message = await Engine.PollNetworkClient(); - switch (message.type) - { - case "players": - { - this.playerAssignments = message.newAssignments; - Engine.SendNetworkReady(2); - let max = 0; - for (const uid in this.playerAssignments) - { - max = Math.max(this.playerAssignments[uid].player, max); - if (this.playerAssignments[uid].player == -1) - Engine.AssignNetworkPlayer(++max, uid); - } - break; - } - case "ready": - this.playerAssignments[message.guid].status = message.status; - break; - case "start": - this.done = true; - return; - default: - } - - if (!this.launched) - { - const assignementArray = Object.values(this.playerAssignments); - if (assignementArray.length === this.maxPlayers && - assignementArray.every(assignement => - assignement.player !== -1 || assignement.status !== 0)) - { - this.onLaunch(); - } - } - } - })(); + // Password not implemented for autostart. + Engine.StartNetworkHost(playerName, port, "", false, !('autostart-disable-replay' in cmdLineArgs)); } - - onTick() + catch(e) { - return this.done; + const message = sprintf(translate("Cannot host game: %(message)s."), { "message": e.message }); + messageBox(400, 200, message, translate("Error")); } /** - * In the visual autostart path, we need to show the loading screen. - * Overload this as appropriate. + * Handles a simple implementation of player assignments. + * Should not need be overloaded in mods unless you want to change that logic. */ - onLaunch() + + let playerAssignments = {}; + while (true) { - this.launched = true; + const message = await Engine.PollNetworkClient(); + switch (message.type) + { + case "players": + { + playerAssignments = message.newAssignments; + Engine.SendNetworkReady(2); + let max = 0; + for (const uid in playerAssignments) + { + max = Math.max(playerAssignments[uid].player, max); + if (playerAssignments[uid].player == -1) + Engine.AssignNetworkPlayer(++max, uid); + } + break; + } + case "ready": + playerAssignments[message.guid].status = message.status; + break; + default: + } - this.settings = new GameSettings().init(); - - parseCmdLineArgs(this.settings, this.cmdLineArgs); - - this.settings.playerCount.setNb(Object.keys(this.playerAssignments).length); - this.settings.launchGame(this.playerAssignments, this.storeReplay); - Engine.SwitchGuiPage("page_loading.xml", { - "attribs": this.settings.finalizedAttributes, - "playerAssignments": this.playerAssignments - }); + const assignementArray = Object.values(playerAssignments); + if (assignementArray.length === maxPlayers && + assignementArray.every(assignement => + assignement.player !== -1 || assignement.status !== 0)) + { + break; + } } + + const settings = new GameSettings().init(); + + parseCmdLineArgs(settings, cmdLineArgs); + + settings.playerCount.setNb(Object.keys(playerAssignments).length); + settings.launchGame(playerAssignments, false); + + while ((await Engine.PollNetworkClient()).type !== "start") + { + // Just wait for condition + } + + return ["page_loading.xml", { + "attribs": settings.finalizedAttributes, + "playerAssignments": playerAssignments + }]; } diff --git a/binaries/data/mods/public/autostart/entrypoint.js b/binaries/data/mods/public/autostart/entrypoint.js index cc782fdcfd..0fe0caf5e8 100644 --- a/binaries/data/mods/public/autostart/entrypoint.js +++ b/binaries/data/mods/public/autostart/entrypoint.js @@ -7,8 +7,6 @@ Engine.HasXmppClient = () => false; Engine.SetRankedGame = () => {}; Engine.TextureExists = () => false; -Engine.OpenChildPage = () => {}; -Engine.SwitchGuiPage = () => {}; var translateObjectKeys = () => {}; var translate = x => x; @@ -20,11 +18,9 @@ Engine.LoadScript("globalscripts/"); // TODO: clean this up and show errors better in the non-visual path. Engine.LoadScript("gui/common/functions_msgbox.js"); -var autostartInstance; - function autostartClient(cmdLineArgs) { - autostartInstance = new AutoStartClient(cmdLineArgs); + return autoStartClient(cmdLineArgs); } /** @@ -44,15 +40,6 @@ function autostartHost(cmdLineArgs, networked = false) Engine.LoadScript("gamesettings/attributes/"); if (networked) - autostartInstance = new AutoStartHost(cmdLineArgs); - else - autostartInstance = new AutoStart(cmdLineArgs); -} - -/** - * @returns false if the loop should carry on. - */ -function onTick() -{ - return autostartInstance.onTick(); + return autoStartHost(cmdLineArgs); + return autoStart(cmdLineArgs); } diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index 7fa8c0bc68..ee806b74f0 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -109,6 +109,7 @@ #include #include #include +#include #include #include #include @@ -841,39 +842,47 @@ bool Autostart(const CmdLineArgs& args) JS::RootedValue cmdLineArgs(rq.cx); Script::ToJSVal(rq, &cmdLineArgs, args); - if (args.Has("autostart-client") || args.Has("autostart-host")) - { - // Pass the default port if undefined, to avoid duplicating it in JS. - if (!Script::HasProperty(rq, cmdLineArgs, "autostart-port")) - Script::SetProperty(rq, cmdLineArgs, "autostart-port", PS_DEFAULT_PORT); + // Pass the default port if undefined, to avoid duplicating it in JS. + if (!Script::HasProperty(rq, cmdLineArgs, "autostart-port")) + Script::SetProperty(rq, cmdLineArgs, "autostart-port", PS_DEFAULT_PORT); - JS::RootedValue global(rq.cx, rq.globalValue()); - if (!ScriptFunction::CallVoid(rq, global, args.Has("autostart-client") ? "autostartClient" : "autostartHost", cmdLineArgs, true)) - return false; - - bool shouldQuit = false; - while (!shouldQuit) - { - g_NetClient->Poll(); - g_ScriptContext->RunJobs(); - if (!ScriptFunction::Call(rq, global, "onTick", shouldQuit)) - return false; - std::this_thread::sleep_for(std::chrono::microseconds(200)); - } - } - else + JS::RootedValue resultValue{rq.cx}; + JS::RootedValue global(rq.cx, rq.globalValue()); + if (!ScriptFunction::Call(rq, global, + args.Has("autostart-client") ? "autostartClient" : "autostartHost", &resultValue, + cmdLineArgs, args.Has("autostart-host")) && !resultValue.isObject()) { - JS::RootedValue global(rq.cx, rq.globalValue()); - if (!ScriptFunction::CallVoid(rq, global, "autostartHost", cmdLineArgs, false)) - return false; + return false; } + JS::RootedObject result{rq.cx, &resultValue.toObject()}; + while (JS::IsPromiseObject(result) && JS::GetPromiseState(result) == JS::PromiseState::Pending) + { + g_ScriptContext->RunJobs(); + g_NetClient->Poll(); + std::this_thread::sleep_for(std::chrono::microseconds(200)); + } + if (JS::IsPromiseObject(result) && JS::GetPromiseState(result) == JS::PromiseState::Rejected) + return false; + + JS::RootedValue pageData{rq.cx, JS::IsPromiseObject(result) ? JS::GetPromiseResult(result) : + resultValue}; + if (args.Has("autostart-nonvisual")) { PS::Loader::NonprogressiveLoad(); g_Game->ReallyStartGame(); + return true; } + std::wstring pageName; + Script::GetPropertyInt(rq, pageData, 0, pageName); + JS::RootedValue pageArgs{rq.cx}; + Script::GetPropertyInt(rq, pageData, 1, &pageArgs); + Script::StructuredClone clonedpageArgs{Script::WriteStructuredClone(rq, pageArgs)}; + + g_GUI->OpenChildPage(pageName, std::move(clonedpageArgs)); + return true; }