Index: binaries/data/mods/mod/autostart/entrypoint.js =================================================================== --- /dev/null +++ binaries/data/mods/mod/autostart/entrypoint.js @@ -0,0 +1,23 @@ +/** + * This file is called from the visual & non-visual paths when autostarting. + * To avoid relying on the GUI, this script has access to a special 'LoadScript' function. + * See implementation in the public mod for more details. + */ + +function autostartClient(initData) +{ + error("Autostart is not implemented in the 'mod' mod"); +} + +function autostartHost(initData, networked = false) +{ + error("Autostart is not implemented in the 'mod' mod"); +} + +/** + * @returns false if the loop should carry on. + */ +function onTick() +{ + return true; +} Index: binaries/data/mods/public/autostart/autostart.js =================================================================== --- /dev/null +++ binaries/data/mods/public/autostart/autostart.js @@ -0,0 +1,29 @@ +class AutoStart +{ + constructor(initData) + { + this.settings = new GameSettings().init(); + this.settings.fromInitAttributes(initData.attribs); + + this.playerAssignments = initData.playerAssignments; + + this.settings.launchGame(this.playerAssignments, initData.storeReplay); + + this.onLaunch(); + } + + 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 + }); + } +} Index: binaries/data/mods/public/autostart/autostart_client.js =================================================================== --- /dev/null +++ binaries/data/mods/public/autostart/autostart_client.js @@ -0,0 +1,54 @@ +class AutoStartClient +{ + constructor(initData) + { + this.playerAssignments = {}; + + try + { + Engine.StartNetworkJoin(initData.playerName, initData.ip, initData.port, initData.storeReplay); + } + catch (e) + { + const message = sprintf(translate("Cannot join game: %(message)s."), { "message": e.message }); + messageBox(400, 200, message, translate("Error")); + } + } + + onTick() + { + while (true) + { + const message = Engine.PollNetworkClient(); + if (!message) + break; + + 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. + return true; + default: + } + } + return false; + } + + /** + * 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 + }); + } +} Index: binaries/data/mods/public/autostart/autostart_host.js =================================================================== --- /dev/null +++ binaries/data/mods/public/autostart/autostart_host.js @@ -0,0 +1,85 @@ +class AutoStartHost +{ + constructor(initData) + { + this.launched = false; + this.maxPlayers = initData.maxPlayers; + this.storeReplay = initData.storeReplay; + this.playerAssignments = {}; + + this.initAttribs = initData.attribs; + + try + { + // Stun and password not implemented for autostart. + Engine.StartNetworkHost(initData.playerName, initData.port, false, "", initData.storeReplay); + } + 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. + */ + onTick() + { + while (true) + { + const message = Engine.PollNetworkClient(); + if (!message) + break; + + 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": + return true; + default: + } + } + + if (!this.launched && Object.keys(this.playerAssignments).length == this.maxPlayers) + { + for (const uid in this.playerAssignments) + if (this.playerAssignments[uid].player == -1 || this.playerAssignments[uid].status == 0) + return false; + this.onLaunch(); + } + return false; + } + + /** + * In the visual autostart path, we need to show the loading screen. + * Overload this as appropriate. + */ + onLaunch() + { + this.launched = true; + + this.settings = new GameSettings().init(); + this.settings.fromInitAttributes(this.initAttribs); + this.settings.playerCount.setNb(Object.keys(this.playerAssignments).length); + this.settings.launchGame(this.playerAssignments, this.storeReplay); + Engine.SwitchGuiPage("page_loading.xml", { + "attribs": this.initAttribs, + "playerAssignments": this.playerAssignments + }); + } +} Index: binaries/data/mods/public/autostart/entrypoint.js =================================================================== --- /dev/null +++ binaries/data/mods/public/autostart/entrypoint.js @@ -0,0 +1,60 @@ +/** + * Implement autostart for the public mod. + * We want to avoid relying on too many specific files, so we'll mock a few engine functions. + * Depending on the path, these may get overwritten with the real function. + */ + +Engine.HasNetClient = () => false; +Engine.HasXmppClient = () => false; +Engine.GetMatchID = () => 0; +Engine.SetRankedGame = () => {}; +Engine.TextureExists = () => false; +Engine.PushGuiPage = () => {}; +Engine.SwitchGuiPage = () => {}; + +var translateObjectKeys = () => {} +var translate = x => x; +var translateWithContext = x => x; + +// MsgBox is used in the failure path. +// TODO: clean this up and show errors better in the non-visual path. +Engine.LoadScript("gui/common/functions_msgbox.js"); + +var autostartInstance; + +function autostartClient(initData) +{ + autostartInstance = new AutoStartClient(initData); +} + +/** + * This path depends on files currently stored under gui/, which should be moved. + * The best place would probably be a new 'engine' mod, independent from the 'mod' mod and the public mod. + */ +function autostartHost(initData, networked = false) +{ + Engine.LoadScript("globalscripts/"); + + Engine.LoadScript("gui/common/color.js"); + Engine.LoadScript("gui/common/functions_utility.js"); + Engine.LoadScript("gui/common/Observable.js"); + Engine.LoadScript("gui/common/settings.js"); + + Engine.LoadScript("gui/maps/MapCache.js") + + Engine.LoadScript("gamesettings/"); + Engine.LoadScript("gamesettings/attributes/"); + + if (networked) + autostartInstance = new AutoStartHost(initData); + else + autostartInstance = new AutoStart(initData); +} + +/** + * @returns false if the loop should carry on. + */ +function onTick() +{ + return autostartInstance.onTick(); +} Index: binaries/data/mods/public/gamesettings/GameSettings.js =================================================================== --- binaries/data/mods/public/gamesettings/GameSettings.js +++ binaries/data/mods/public/gamesettings/GameSettings.js @@ -11,7 +11,7 @@ */ class GameSettings { - init(mapCache) + init(mapCache, civData) { if (!mapCache) mapCache = new MapCache(); @@ -19,9 +19,11 @@ "value": mapCache, }); - // Load all possible civ data - don't presume that some will be available. + if (!civData) + // Load all possible civ data - don't presume that some will be available. + civData = loadCivData(false, false); Object.defineProperty(this, "civData", { - "value": loadCivData(false, false), + "value": civData }); Object.defineProperty(this, "isNetworked", { Index: binaries/data/mods/public/globalscripts/Civilisations.js =================================================================== --- /dev/null +++ binaries/data/mods/public/globalscripts/Civilisations.js @@ -0,0 +1,49 @@ +/** + * Loads history and gameplay data of all civs. + * + * @param selectableOnly {boolean} - Only load civs that can be selected + * in the gamesetup. Scenario maps might set non-selectable civs. + */ +function loadCivFiles(selectableOnly) +{ + let propertyNames = [ + "Code", "Culture", "Music", "CivBonuses", "StartEntities", + "AINames", "SkirmishReplacements", "SelectableInGameSetup"]; + + let civData = {}; + + for (let filename of Engine.ListDirectoryFiles("simulation/data/civs/", "*.json", false)) + { + let data = Engine.ReadJSONFile(filename); + + for (let prop of propertyNames) + if (data[prop] === undefined) + throw new Error(filename + " doesn't contain " + prop); + + if (selectableOnly && !data.SelectableInGameSetup) + continue; + + const template = Engine.GetTemplate("special/players/" + data.Code); + data.Name = template.Identity.GenericName; + data.Emblem = "session/portraits/" + template.Identity.Icon; + data.History = template.Identity.History; + + civData[data.Code] = data; + } + + return civData; +} + +function getCivCodes(selectableOnly) +{ + let codes = []; + + for (let filename of Engine.ListDirectoryFiles("simulation/data/civs/", "*.json", false)) + { + let data = Engine.ReadJSONFile(filename); + + if (selectableOnly && !data.SelectableInGameSetup) + continue; + + } +} Index: binaries/data/mods/public/gui/autostart/autostart.js =================================================================== --- binaries/data/mods/public/gui/autostart/autostart.js +++ binaries/data/mods/public/gui/autostart/autostart.js @@ -1,12 +1,4 @@ function init(initData) { - let settings = new GameSettings().init(); - settings.fromInitAttributes(initData.attribs); - - settings.launchGame(initData.playerAssignments, initData.storeReplay); - - Engine.SwitchGuiPage("page_loading.xml", { - "attribs": settings.finalizedAttributes, - "playerAssignments": initData.playerAssignments - }); + return new AutoStart(initData); } Index: binaries/data/mods/public/gui/autostart/autostart.xml =================================================================== --- binaries/data/mods/public/gui/autostart/autostart.xml +++ binaries/data/mods/public/gui/autostart/autostart.xml @@ -2,7 +2,8 @@