static void ProcessCommandLineArgs(const CmdLineArgs& args) { // TODO: all these options (and the ones processed elsewhere) should // be documented somewhere for users. // Handle "-conf=key:value" (potentially multiple times) std::vector<CStr> conf = args.GetMultiple("conf"); for (size_t i = 0; i < conf.size(); ++i) { CStr name_value = conf[i]; if (name_value.Find(':') != -1) { CStr name = name_value.BeforeFirst(":"); CStr value = name_value.AfterFirst(":"); g_ConfigDB.SetValueString(CFG_COMMAND, name, value); } } if (args.Has("g")) { g_Gamma = args.Get("g").ToFloat(); if (g_Gamma == 0.0f) g_Gamma = 1.0f; } // if (args.Has("listfiles")) // trace_enable(true); if (args.Has("profile")) g_ConfigDB.SetValueString(CFG_COMMAND, "profile", args.Get("profile")); if (args.Has("quickstart")) { g_Quickstart = true; g_DisableAudio = true; // do this for backward-compatibility with user expectations } if (args.Has("nosound")) g_DisableAudio = true; if (args.Has("shadows")) g_ConfigDB.SetValueString(CFG_COMMAND, "shadows", "true"); if (args.Has("xres")) g_ConfigDB.SetValueString(CFG_COMMAND, "xres", args.Get("xres")); if (args.Has("yres")) g_ConfigDB.SetValueString(CFG_COMMAND, "yres", args.Get("yres")); if (args.Has("vsync")) g_ConfigDB.SetValueString(CFG_COMMAND, "vsync", "true"); if (args.Has("ooslog")) g_ConfigDB.SetValueString(CFG_COMMAND, "ooslog", "true"); if (args.Has("serializationtest")) g_ConfigDB.SetValueString(CFG_COMMAND, "serializationtest", "true"); }
static void ProcessCommandLineArgs(const CmdLineArgs& args) { // TODO: all these options (and the ones processed elsewhere) should // be documented somewhere for users. if (args.Has("buildarchive")) { // note: VFS init is sure to have been completed by now // (since CONFIG_Init reads from file); therefore, // it is safe to call this from here directly. // vfs_opt_rebuild_main_archive("mods/official/official1.zip", "../logs/trace.txt"); } // Handle "-conf=key:value" (potentially multiple times) std::vector<CStr> conf = args.GetMultiple("conf"); for (size_t i = 0; i < conf.size(); ++i) { CStr name_value = conf[i]; if (name_value.Find(':') != -1) { CStr name = name_value.BeforeFirst(":"); CStr value = name_value.AfterFirst(":"); g_ConfigDB.CreateValue(CFG_COMMAND, name)->m_String = value; } } if (args.Has("g")) { g_Gamma = args.Get("g").ToFloat(); if (g_Gamma == 0.0f) g_Gamma = 1.0f; } // if (args.Has("listfiles")) // trace_enable(true); if (args.Has("profile")) g_ConfigDB.CreateValue(CFG_COMMAND, "profile")->m_String = args.Get("profile"); if (args.Has("quickstart")) { g_Quickstart = true; g_DisableAudio = true; // do this for backward-compatibility with user expectations } if (args.Has("nosound")) g_DisableAudio = true; if (args.Has("shadows")) g_ConfigDB.CreateValue(CFG_COMMAND, "shadows")->m_String = "true"; if (args.Has("xres")) g_ConfigDB.CreateValue(CFG_COMMAND, "xres")->m_String = args.Get("xres"); if (args.Has("yres")) g_ConfigDB.CreateValue(CFG_COMMAND, "yres")->m_String = args.Get("yres"); if (args.Has("vsync")) g_ConfigDB.CreateValue(CFG_COMMAND, "vsync")->m_String = "true"; }
// starts the Atlas UI if an "-editor" switch is found on the command line. // this is the alternative to starting the main menu and clicking on // the editor button; it is much faster because it's called during early // init and therefore skips GUI setup. // notes: // - GUI init still runs, but some GUI setup will be skipped since // ATLAS_IsRunning() will return true. bool ATLAS_RunIfOnCmdLine(const CmdLineArgs& args, bool force) { if (force || args.Has("editor")) { ATLAS_Run(args, ATLAS_NO_GUI); return true; } return false; }
static void InitVfs(const CmdLineArgs& args) { TIMER(L"InitVfs"); const Paths paths(args); OsPath logs(paths.Logs()); CreateDirectories(logs, 0700); psSetLogDir(logs); // desired location for crashlog is now known. update AppHooks ASAP // (particularly before the following error-prone operations): AppHooks hooks = {0}; hooks.bundle_logs = psBundleLogs; hooks.get_log_dir = psLogDir; hooks.display_error = psDisplayError; app_hooks_update(&hooks); const size_t cacheSize = ChooseCacheSize(); g_VFS = CreateVfs(cacheSize); std::vector<CStr> mods = args.GetMultiple("mod"); mods.insert(mods.begin(), "public"); if (!args.Has("noUserMod")) mods.push_back("user"); OsPath modPath = paths.RData()/"mods"; OsPath modUserPath = paths.UserData()/"mods"; for (size_t i = 0; i < mods.size(); ++i) { size_t priority = i+1; // mods are higher priority than regular mountings, which default to priority 0 size_t flags = VFS_MOUNT_WATCH|VFS_MOUNT_ARCHIVABLE|VFS_MOUNT_MUST_EXIST; OsPath modName(mods[i]); g_VFS->Mount(L"", modPath / modName/"", flags, priority); g_VFS->Mount(L"", modUserPath / modName/"", flags, priority); } // We mount these dirs last as otherwise writing could result in files being placed in a mod's dir. g_VFS->Mount(L"screenshots/", paths.UserData()/"screenshots"/""); g_VFS->Mount(L"saves/", paths.UserData()/"saves"/"", VFS_MOUNT_WATCH); const OsPath readonlyConfig = paths.RData()/"config"/""; // Mounting with highest priority, so that a mod supplied user.cfg is harmless g_VFS->Mount(L"config/", readonlyConfig, 0, (size_t)-1); if(readonlyConfig != paths.Config()) g_VFS->Mount(L"config/", paths.Config(), 0, (size_t)-1); g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE); // (adding XMBs to archive speeds up subsequent reads) // note: don't bother with g_VFS->TextRepresentation - directories // haven't yet been populated and are empty. }
static std::vector<CStr> GetMods(const CmdLineArgs& args, bool dev) { std::vector<CStr> mods = args.GetMultiple("mod"); // TODO: It would be nice to remove this hard-coding mods.insert(mods.begin(), "public"); // Add the user mod if not explicitly disabled or we have a dev copy so // that saved files end up in version control and not in the user mod. if (!dev && !args.Has("noUserMod")) mods.push_back("user"); return mods; }
Paths::Paths(const CmdLineArgs& args) { m_root = Root(args.GetArg0()); #ifdef INSTALLED_DATADIR m_rdata = OsPath(STRINGIZE(INSTALLED_DATADIR))/""; #else m_rdata = m_root/"data"/""; #endif const char* subdirectoryName = args.Has("writableRoot")? 0 : "0ad"; // everything is a subdirectory of the root if(!subdirectoryName) { m_data = m_rdata; m_config = m_data/"config"/""; m_cache = m_data/"cache"/""; m_logs = m_root/"logs"/""; } else { #if OS_WIN const OsPath appdata = wutil_AppdataPath() / subdirectoryName/""; m_data = appdata/"data"/""; m_config = appdata/"config"/""; m_cache = appdata/"cache"/""; m_logs = appdata/"logs"/""; #else const char* envHome = getenv("HOME"); ENSURE(envHome); const OsPath home(envHome); const OsPath xdgData = XDG_Path("XDG_DATA_HOME", home, home/".local/share/") / subdirectoryName; const OsPath xdgConfig = XDG_Path("XDG_CONFIG_HOME", home, home/".config/" ) / subdirectoryName; const OsPath xdgCache = XDG_Path("XDG_CACHE_HOME", home, home/".cache/" ) / subdirectoryName; m_data = xdgData/""; m_cache = xdgCache/""; m_config = xdgConfig/"config"/""; m_logs = xdgConfig/"logs"/""; #endif } }
Paths::Paths(const CmdLineArgs& args) { m_root = Root(args.GetArg0()); m_rdata = RootData(args.GetArg0()); const char* subdirectoryName = args.Has("writableRoot")? 0 : "0ad"; if(!subdirectoryName) { // Note: if writableRoot option is passed to the game, then // all the data is a subdirectory of the root m_gameData = m_rdata; m_userData = m_gameData; m_config = m_gameData / "config"/""; m_cache = m_gameData / "cache"/""; m_logs = m_root / "logs"/""; } else // OS-specific path handling { #if OS_ANDROID const OsPath appdata = OsPath("/sdcard/0ad/appdata"); // We don't make the game vs. user data distinction on Android m_gameData = appdata/"data"/""; m_userData = m_gameData; m_config = appdata/"config"/""; m_cache = appdata/"cache"/""; m_logs = appdata/"logs"/""; #elif OS_WIN /* For reasoning behind our Windows paths, see the discussion here: * http://www.wildfiregames.com/forum/index.php?showtopic=14759 * * Summary: * 1. Local appdata: for bulky unfriendly data like the cache, * which can be recreated if deleted; doesn't need backing up. * 2. Roaming appdata: for slightly less unfriendly data like config * files that might theoretically be shared between different * machines on a domain. * 3. Personal / My Documents: for data explicitly created by the user, * and which should be visible and easily accessed. We use a non- * localized My Games subfolder for improved organization. */ // %localappdata%/0ad/ const OsPath localAppdata = wutil_LocalAppdataPath() / subdirectoryName/""; // %appdata%/0ad/ const OsPath roamingAppData = wutil_RoamingAppdataPath() / subdirectoryName/""; // My Documents/My Games/0ad/ const OsPath personalData = wutil_PersonalPath() / "My Games" / subdirectoryName/""; m_cache = localAppdata / "cache"/""; m_gameData = roamingAppData / "data"/""; m_userData = personalData/""; m_config = roamingAppData / "config"/""; m_logs = localAppdata / "logs"/""; #elif OS_MACOSX /* For reasoning behind our OS X paths, see the discussion here: * http://www.wildfiregames.com/forum/index.php?showtopic=15511 * * Summary: * 1. Application Support: most data associated with the app * should be stored here, with few exceptions (e.g. temporary * data, cached data, and managed media files). * 2. Caches: used for non-critial app data that can be easily * regenerated if this directory is deleted. It is not * included in backups by default. * * Note: the paths returned by osx_Get*Path are not guaranteed to exist, * but that's OK since we always create them on demand. */ // We probably want to use the same subdirectoryName regardless // of whether running a bundle or from SVN. Apple recommends using // company name, bundle name or bundle identifier. OsPath appSupportPath; // ~/Library/Application Support/0ad OsPath cachePath; // ~/Library/Caches/0ad { std::string path = osx_GetAppSupportPath(); ENSURE(!path.empty()); appSupportPath = OsPath(path) / subdirectoryName; } { std::string path = osx_GetCachesPath(); ENSURE(!path.empty()); cachePath = OsPath(path) / subdirectoryName; } // We don't make the game vs. user data distinction on OS X m_gameData = appSupportPath /""; m_userData = m_gameData; m_cache = cachePath/""; m_config = appSupportPath / "config"/""; m_logs = appSupportPath / "logs"/""; #else // OS_UNIX const char* envHome = getenv("HOME"); ENSURE(envHome); const OsPath home(envHome); const OsPath xdgData = XDG_Path("XDG_DATA_HOME", home, home/".local/share/") / subdirectoryName; const OsPath xdgConfig = XDG_Path("XDG_CONFIG_HOME", home, home/".config/" ) / subdirectoryName; const OsPath xdgCache = XDG_Path("XDG_CACHE_HOME", home, home/".cache/" ) / subdirectoryName; // We don't make the game vs. user data distinction on Unix m_gameData = xdgData/""; m_userData = m_gameData; m_cache = xdgCache/""; m_config = xdgConfig / "config"/""; m_logs = xdgConfig / "logs"/""; #endif } }
bool Init(const CmdLineArgs& args, int flags) { h_mgr_init(); // Do this as soon as possible, because it chdirs // and will mess up the error reporting if anything // crashes before the working directory is set. InitVfs(args, flags); // This must come after VFS init, which sets the current directory // (required for finding our output log files). g_Logger = new CLogger; new CProfileViewer; new CProfileManager; // before any script code g_ScriptStatsTable = new CScriptStatsTable; g_ProfileViewer.AddRootTable(g_ScriptStatsTable); // Set up the console early, so that debugging // messages can be logged to it. (The console's size // and fonts are set later in InitPs()) g_Console = new CConsole(); // g_ConfigDB, command line args, globals CONFIG_Init(args); // Using a global object for the runtime is a workaround until Simulation and AI use // their own threads and also their own runtimes. const int runtimeSize = 384 * 1024 * 1024; const int heapGrowthBytesGCTrigger = 20 * 1024 * 1024; g_ScriptRuntime = ScriptInterface::CreateRuntime(shared_ptr<ScriptRuntime>(), runtimeSize, heapGrowthBytesGCTrigger); // Special command-line mode to dump the entity schemas instead of running the game. // (This must be done after loading VFS etc, but should be done before wasting time // on anything else.) if (args.Has("dumpSchema")) { CSimulation2 sim(NULL, g_ScriptRuntime, NULL); sim.LoadDefaultScripts(); std::ofstream f("entity.rng", std::ios_base::out | std::ios_base::trunc); f << sim.GenerateSchema(); std::cout << "Generated entity.rng\n"; exit(0); } // override ah_translate with our i18n code. AppHooks hooks = {0}; hooks.translate = psTranslate; hooks.translate_free = psTranslateFree; app_hooks_update(&hooks); CNetHost::Initialize(); #if CONFIG2_AUDIO ISoundManager::CreateSoundManager(); #endif // Check if there are mods specified on the command line, // or if we already set the mods (~INIT_MODS), // else check if there are mods that should be loaded specified // in the config and load those (by aborting init and restarting // the engine). if (!args.Has("mod") && (flags & INIT_MODS) == INIT_MODS) { CStr modstring; CFG_GET_VAL("mod.enabledmods", modstring); if (!modstring.empty()) { std::vector<CStr> mods; boost::split(mods, modstring, boost::is_any_of(" "), boost::token_compress_on); std::swap(g_modsLoaded, mods); // Abort init and restart restart_engine(); return false; } } new L10n; // before scripting // JS debugger temporarily disabled during the SpiderMonkey upgrade (check trac ticket #2348 for details) //if (g_JSDebuggerEnabled) // g_DebuggingServer = new CDebuggingServer(); // Optionally start profiler HTTP output automatically // (By default it's only enabled by a hotkey, for security/performance) bool profilerHTTPEnable = false; CFG_GET_VAL("profiler2.autoenable", profilerHTTPEnable); if (profilerHTTPEnable) g_Profiler2.EnableHTTP(); if (!g_Quickstart) g_UserReporter.Initialize(); // after config PROFILE2_EVENT("Init finished"); return true; }
/* * Command line options for autostart (keep synchronized with binaries/system/readme.txt): * * -autostart="TYPEDIR/MAPNAME" enables autostart and sets MAPNAME; TYPEDIR is skirmishes, scenarios, or random * -autostart-ai=PLAYER:AI sets the AI for PLAYER (e.g. 2:petra) * -autostart-aidiff=PLAYER:DIFF sets the DIFFiculty of PLAYER's AI (0: sandbox, 5: very hard) * -autostart-civ=PLAYER:CIV sets PLAYER's civilisation to CIV (skirmish and random maps only) * -autostart-aiseed=AISEED sets the seed used for the AI random generator (default 0, use -1 for random) * Multiplayer: * -autostart-playername=NAME sets local player NAME (default 'anonymous') * -autostart-host sets multiplayer host mode * -autostart-host-players=NUMBER sets NUMBER of human players for multiplayer game (default 2) * -autostart-client=IP sets multiplayer client to join host at given IP address * Random maps only: * -autostart-seed=SEED sets random map SEED value (default 0, use -1 for random) * -autostart-size=TILES sets random map size in TILES (default 192) * -autostart-players=NUMBER sets NUMBER of players on random map (default 2) * * Examples: * 1) "Bob" will host a 2 player game on the Arcadia map: * -autostart="scenarios/Arcadia 02" -autostart-host -autostart-host-players=2 -autostart-playername="Bob" * 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 */ bool Autostart(const CmdLineArgs& args) { CStr autoStartName = args.Get("autostart"); if (autoStartName.empty()) return false; g_Game = new CGame(); ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); JSContext* cx = scriptInterface.GetContext(); JSAutoRequest rq(cx); JS::RootedValue attrs(cx); scriptInterface.Eval("({})", &attrs); JS::RootedValue settings(cx); scriptInterface.Eval("({})", &settings); JS::RootedValue playerData(cx); scriptInterface.Eval("([])", &playerData); // The directory in front of the actual map name indicates which type // of map is being loaded. Drawback of this approach is the association // of map types and folders is hard-coded, but benefits are: // - No need to pass the map type via command line separately // - Prevents mixing up of scenarios and skirmish maps to some degree Path mapPath = Path(autoStartName); std::wstring mapDirectory = mapPath.Parent().Filename().string(); std::string mapType; if (mapDirectory == L"random") { // Default seed is 0 u32 seed = 0; CStr seedArg = args.Get("autostart-seed"); if (!seedArg.empty()) { // Random seed value if (seedArg == "-1") seed = rand(); else seed = seedArg.ToULong(); } // 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(cx); scriptInterface.ReadJSONFile(scriptPath, &scriptData); if (!scriptData.isUndefined() && scriptInterface.GetProperty(scriptData, "settings", &settings)) { // JSON loaded ok - copy script name over to game attributes std::wstring scriptFile; scriptInterface.GetProperty(settings, "Script", scriptFile); scriptInterface.SetProperty(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")) { CStr size = args.Get("autostart-size"); mapSize = size.ToUInt(); } scriptInterface.SetProperty(settings, "Seed", seed); // Random seed scriptInterface.SetProperty(settings, "Size", mapSize); // Random map size (in patches) // Get optional number of players (default 2) size_t numPlayers = 2; if (args.Has("autostart-players")) { CStr num = args.Get("autostart-players"); numPlayers = num.ToUInt(); } // Set up player data for (size_t i = 0; i < numPlayers; ++i) { JS::RootedValue player(cx); scriptInterface.Eval("({})", &player); // We could load player_defaults.json here, but that would complicate the logic // even more and autostart is only intended for developers anyway scriptInterface.SetProperty(player, "Civ", std::string("athen")); scriptInterface.SetPropertyInt(playerData, i, player); } 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"); scriptInterface.ParseJSON(mapSettingsJSON, &settings); // Initialize the playerData array being modified by autostart // with the real map data, so sensible values are present: scriptInterface.GetProperty(settings, "PlayerData", &playerData); if (mapDirectory == L"scenarios") mapType = "scenario"; else mapType = "skirmish"; } else { LOGERROR("Autostart: Unrecognized map type '%s'", utf8_from_wstring(mapDirectory)); throw PSERROR_Game_World_MapLoadFailed("Unrecognized map type.\nConsult readme.txt for the currently supported types."); } scriptInterface.SetProperty(attrs, "mapType", mapType); scriptInterface.SetProperty(attrs, "map", std::string("maps/" + autoStartName)); scriptInterface.SetProperty(settings, "mapType", mapType); // Set seed for AIs u32 aiseed = 0; if (args.Has("autostart-aiseed")) { CStr seedArg = args.Get("autostart-aiseed"); if (seedArg == "-1") aiseed = rand(); else aiseed = seedArg.ToULong(); } scriptInterface.SetProperty(settings, "AISeed", aiseed); // Set player data for AIs // attrs.settings = { PlayerData: [ { AI: ... }, ... ] }: if (args.Has("autostart-ai")) { std::vector<CStr> aiArgs = args.GetMultiple("autostart-ai"); for (size_t i = 0; i < aiArgs.size(); ++i) { int playerID = aiArgs[i].BeforeFirst(":").ToInt(); // Instead of overwriting existing player data, modify the array JS::RootedValue player(cx); if (!scriptInterface.GetPropertyInt(playerData, playerID-1, &player) || player.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; } scriptInterface.Eval("({})", &player); } CStr name = aiArgs[i].AfterFirst(":"); scriptInterface.SetProperty(player, "AI", std::string(name)); scriptInterface.SetProperty(player, "AIDiff", 3); scriptInterface.SetPropertyInt(playerData, playerID-1, player); } } // Set AI difficulty if (args.Has("autostart-aidiff")) { std::vector<CStr> civArgs = args.GetMultiple("autostart-aidiff"); for (size_t i = 0; i < civArgs.size(); ++i) { int playerID = civArgs[i].BeforeFirst(":").ToInt(); // Instead of overwriting existing player data, modify the array JS::RootedValue player(cx); if (!scriptInterface.GetPropertyInt(playerData, playerID-1, &player) || player.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; } scriptInterface.Eval("({})", &player); } int difficulty = civArgs[i].AfterFirst(":").ToInt(); scriptInterface.SetProperty(player, "AIDiff", difficulty); scriptInterface.SetPropertyInt(playerData, playerID-1, player); } } // Set player data for Civs if (args.Has("autostart-civ")) { if (mapDirectory != L"scenarios") { std::vector<CStr> civArgs = args.GetMultiple("autostart-civ"); for (size_t i = 0; i < civArgs.size(); ++i) { int playerID = civArgs[i].BeforeFirst(":").ToInt(); // Instead of overwriting existing player data, modify the array JS::RootedValue player(cx); if (!scriptInterface.GetPropertyInt(playerData, playerID-1, &player) || player.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; } scriptInterface.Eval("({})", &player); } CStr name = civArgs[i].AfterFirst(":"); scriptInterface.SetProperty(player, "Civ", std::string(name)); scriptInterface.SetPropertyInt(playerData, playerID-1, player); } } else LOGWARNING("Autostart: Option 'autostart-civ' is invalid for scenarios"); } // Add player data to map settings scriptInterface.SetProperty(settings, "PlayerData", playerData); // Add map settings to game attributes scriptInterface.SetProperty(attrs, "settings", settings); JS::RootedValue mpInitData(cx); scriptInterface.Eval("({isNetworked:true, playerAssignments:{}})", &mpInitData); scriptInterface.SetProperty(mpInitData, "attribs", attrs); // Get optional playername CStrW userName = L"anonymous"; if (args.Has("autostart-playername")) { userName = args.Get("autostart-playername").FromUTF8(); } if (args.Has("autostart-host")) { InitPs(true, L"page_loading.xml", &scriptInterface, mpInitData); size_t maxPlayers = 2; if (args.Has("autostart-host-players")) maxPlayers = args.Get("autostart-host-players").ToUInt(); g_NetServer = new CNetServer(maxPlayers); g_NetServer->UpdateGameAttributes(&attrs, scriptInterface); bool ok = g_NetServer->SetupConnection(); ENSURE(ok); g_NetClient = new CNetClient(g_Game); g_NetClient->SetUserName(userName); g_NetClient->SetupConnection("127.0.0.1"); } else if (args.Has("autostart-client")) { InitPs(true, L"page_loading.xml", &scriptInterface, mpInitData); g_NetClient = new CNetClient(g_Game); g_NetClient->SetUserName(userName); CStr ip = args.Get("autostart-client"); if (ip.empty()) ip = "127.0.0.1"; bool ok = g_NetClient->SetupConnection(ip); ENSURE(ok); } else { g_Game->SetPlayerID(1); g_Game->StartGame(&attrs, ""); LDR_NonprogressiveLoad(); PSRETURN ret = g_Game->ReallyStartGame(); ENSURE(ret == PSRETURN_OK); InitPs(true, L"page_session.xml", NULL, JS::UndefinedHandleValue); } return true; }
void Init(const CmdLineArgs& args, int UNUSED(flags)) { h_mgr_init(); // Do this as soon as possible, because it chdirs // and will mess up the error reporting if anything // crashes before the working directory is set. InitVfs(args); // This must come after VFS init, which sets the current directory // (required for finding our output log files). g_Logger = new CLogger; // Special command-line mode to dump the entity schemas instead of running the game. // (This must be done after loading VFS etc, but should be done before wasting time // on anything else.) if (args.Has("dumpSchema")) { CSimulation2 sim(NULL, NULL); sim.LoadDefaultScripts(); std::ofstream f("entity.rng", std::ios_base::out | std::ios_base::trunc); f << sim.GenerateSchema(); std::cout << "Generated entity.rng\n"; exit(0); } // override ah_translate with our i18n code. AppHooks hooks = {0}; hooks.translate = psTranslate; hooks.translate_free = psTranslateFree; app_hooks_update(&hooks); // Set up the console early, so that debugging // messages can be logged to it. (The console's size // and fonts are set later in InitPs()) g_Console = new CConsole(); CNetHost::Initialize(); new CProfileViewer; new CProfileManager; // before any script code g_ScriptStatsTable = new CScriptStatsTable; g_ProfileViewer.AddRootTable(g_ScriptStatsTable); #if CONFIG2_AUDIO CSoundManager::CreateSoundManager(); #endif InitScripting(); // before GUI // g_ConfigDB, command line args, globals CONFIG_Init(args); // Optionally start profiler HTTP output automatically // (By default it's only enabled by a hotkey, for security/performance) bool profilerHTTPEnable = false; CFG_GET_VAL("profiler2.http.autoenable", Bool, profilerHTTPEnable); if (profilerHTTPEnable) g_Profiler2.EnableHTTP(); if (!g_Quickstart) g_UserReporter.Initialize(); // after config PROFILE2_EVENT("Init finished"); }
bool Autostart(const CmdLineArgs& args) { /* * Handle various command-line options, for quick testing of various features: * -autostart=name -- map name for scenario, or rms name for random map * -autostart-ai=1:dummybot -- adds the dummybot AI to player 1 * -autostart-playername=name -- multiplayer player name * -autostart-host -- multiplayer host mode * -autostart-players=2 -- number of players * -autostart-client -- multiplayer client mode * -autostart-ip=127.0.0.1 -- multiplayer connect to 127.0.0.1 * -autostart-random=104 -- random map, optional seed value = 104 (default is 0, random is -1) * -autostart-size=192 -- random map size in tiles = 192 (default is 192) * * Examples: * -autostart=Acropolis -autostart-host -autostart-players=2 -- Host game on Acropolis map, 2 players * -autostart=latium -autostart-random=-1 -- Start single player game on latium random map, random rng seed */ CStr autoStartName = args.Get("autostart"); #if OS_ANDROID // HACK: currently the most convenient way to test maps on Android; // should find a better solution autoStartName = "Oasis"; #endif if (autoStartName.empty()) { return false; } g_Game = new CGame(); ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface(); CScriptValRooted attrs; scriptInterface.Eval("({})", attrs); CScriptVal settings; scriptInterface.Eval("({})", settings); CScriptVal playerData; scriptInterface.Eval("([])", playerData); // Set different attributes for random or scenario game if (args.Has("autostart-random")) { CStr seedArg = args.Get("autostart-random"); // Default seed is 0 uint32 seed = 0; if (!seedArg.empty()) { if (seedArg.compare("-1") == 0) { // Random seed value seed = rand(); } else { seed = seedArg.ToULong(); } } // Random map definition will be loaded from JSON file, so we need to parse it std::wstring mapPath = L"maps/random/"; std::wstring scriptPath = mapPath + autoStartName.FromUTF8() + L".json"; CScriptValRooted scriptData = scriptInterface.ReadJSONFile(scriptPath); if (!scriptData.undefined() && scriptInterface.GetProperty(scriptData.get(), "settings", settings)) { // JSON loaded ok - copy script name over to game attributes std::wstring scriptFile; scriptInterface.GetProperty(settings.get(), "Script", scriptFile); scriptInterface.SetProperty(attrs.get(), "script", scriptFile); // RMS filename } else { // Problem with JSON file LOGERROR(L"Error reading random map script '%ls'", scriptPath.c_str()); 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")) { CStr size = args.Get("autostart-size"); mapSize = size.ToUInt(); } scriptInterface.SetProperty(attrs.get(), "map", std::string(autoStartName)); scriptInterface.SetProperty(attrs.get(), "mapPath", mapPath); scriptInterface.SetProperty(attrs.get(), "mapType", std::string("random")); scriptInterface.SetProperty(settings.get(), "Seed", seed); // Random seed scriptInterface.SetProperty(settings.get(), "Size", mapSize); // Random map size (in patches) // Get optional number of players (default 2) size_t numPlayers = 2; if (args.Has("autostart-players")) { CStr num = args.Get("autostart-players"); numPlayers = num.ToUInt(); } // Set up player data for (size_t i = 0; i < numPlayers; ++i) { CScriptVal player; scriptInterface.Eval("({})", player); // We could load player_defaults.json here, but that would complicate the logic // even more and autostart is only intended for developers anyway scriptInterface.SetProperty(player.get(), "Civ", std::string("athen")); scriptInterface.SetPropertyInt(playerData.get(), i, player); } } else { scriptInterface.SetProperty(attrs.get(), "map", std::string(autoStartName)); scriptInterface.SetProperty(attrs.get(), "mapType", std::string("scenario")); } // Set player data for AIs // attrs.settings = { PlayerData: [ { AI: ... }, ... ] }: if (args.Has("autostart-ai")) { std::vector<CStr> aiArgs = args.GetMultiple("autostart-ai"); for (size_t i = 0; i < aiArgs.size(); ++i) { // Instead of overwriting existing player data, modify the array CScriptVal player; if (!scriptInterface.GetPropertyInt(playerData.get(), i, player) || player.undefined()) { scriptInterface.Eval("({})", player); } int playerID = aiArgs[i].BeforeFirst(":").ToInt(); CStr name = aiArgs[i].AfterFirst(":"); scriptInterface.SetProperty(player.get(), "AI", std::string(name)); scriptInterface.SetPropertyInt(playerData.get(), playerID-1, player); } } // Add player data to map settings scriptInterface.SetProperty(settings.get(), "PlayerData", playerData); // Add map settings to game attributes scriptInterface.SetProperty(attrs.get(), "settings", settings); CScriptVal mpInitData; g_GUI->GetScriptInterface().Eval("({isNetworked:true, playerAssignments:{}})", mpInitData); g_GUI->GetScriptInterface().SetProperty(mpInitData.get(), "attribs", CScriptVal(g_GUI->GetScriptInterface().CloneValueFromOtherContext(scriptInterface, attrs.get()))); // Get optional playername CStrW userName = L"anonymous"; if (args.Has("autostart-playername")) { userName = args.Get("autostart-playername").FromUTF8(); } if (args.Has("autostart-host")) { InitPs(true, L"page_loading.xml", mpInitData.get()); size_t maxPlayers = 2; if (args.Has("autostart-players")) { maxPlayers = args.Get("autostart-players").ToUInt(); } g_NetServer = new CNetServer(maxPlayers); g_NetServer->UpdateGameAttributes(attrs.get(), scriptInterface); bool ok = g_NetServer->SetupConnection(); ENSURE(ok); g_NetClient = new CNetClient(g_Game); g_NetClient->SetUserName(userName); g_NetClient->SetupConnection("127.0.0.1"); } else if (args.Has("autostart-client")) { InitPs(true, L"page_loading.xml", mpInitData.get()); g_NetClient = new CNetClient(g_Game); g_NetClient->SetUserName(userName); CStr ip = "127.0.0.1"; if (args.Has("autostart-ip")) { ip = args.Get("autostart-ip"); } bool ok = g_NetClient->SetupConnection(ip); ENSURE(ok); } else { g_Game->SetPlayerID(1); g_Game->StartGame(attrs, ""); LDR_NonprogressiveLoad(); PSRETURN ret = g_Game->ReallyStartGame(); ENSURE(ret == PSRETURN_OK); InitPs(true, L"page_session.xml", JSVAL_VOID); } return true; }