void InitGraphics(const CmdLineArgs& args, int flags) { const bool setup_vmode = (flags & INIT_HAVE_VMODE) == 0; if(setup_vmode) { InitSDL(); if (!g_VideoMode.InitSDL()) throw PSERROR_System_VmodeFailed(); // abort startup #if !SDL_VERSION_ATLEAST(2, 0, 0) SDL_WM_SetCaption("0 A.D.", "0 A.D."); #endif } RunHardwareDetection(); const int quality = SANE_TEX_QUALITY_DEFAULT; // TODO: set value from config file SetTextureQuality(quality); ogl_WarnIfError(); // Optionally start profiler GPU timings automatically // (By default it's only enabled by a hotkey, for performance/compatibility) bool profilerGPUEnable = false; CFG_GET_VAL("profiler2.autoenable", profilerGPUEnable); if (profilerGPUEnable) g_Profiler2.EnableGPU(); if(!g_Quickstart) { WriteSystemInfo(); // note: no longer vfs_display here. it's dog-slow due to unbuffered // file output and very rarely needed. } if(g_DisableAudio) ISoundManager::SetEnabled(false); g_GUI = new CGUIManager(); // (must come after SetVideoMode, since it calls ogl_Init) if (ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", NULL) != 0 // ARB && ogl_HaveExtensions(0, "GL_ARB_vertex_shader", "GL_ARB_fragment_shader", NULL) != 0) // GLSL { DEBUG_DISPLAY_ERROR( L"Your graphics card doesn't appear to be fully compatible with OpenGL shaders." L" In the future, the game will not support pre-shader graphics cards." L" You are advised to try installing newer drivers and/or upgrade your graphics card." L" For more information, please see http://www.wildfiregames.com/forum/index.php?showtopic=16734" ); // TODO: actually quit once fixed function support is dropped } const char* missing = ogl_HaveExtensions(0, "GL_ARB_multitexture", "GL_EXT_draw_range_elements", "GL_ARB_texture_env_combine", "GL_ARB_texture_env_dot3", NULL); if(missing) { wchar_t buf[500]; swprintf_s(buf, ARRAY_SIZE(buf), L"The %hs extension doesn't appear to be available on your computer." L" The game may still work, though - you are welcome to try at your own risk." L" If not or it doesn't look right, upgrade your graphics card.", missing ); DEBUG_DISPLAY_ERROR(buf); // TODO: i18n } if (!ogl_HaveExtension("GL_ARB_texture_env_crossbar")) { DEBUG_DISPLAY_ERROR( L"The GL_ARB_texture_env_crossbar extension doesn't appear to be available on your computer." L" Shadows are not available and overall graphics quality might suffer." L" You are advised to try installing newer drivers and/or upgrade your graphics card."); g_Shadows = false; } ogl_WarnIfError(); InitRenderer(); InitInput(); ogl_WarnIfError(); try { if (!Autostart(args)) { const bool setup_gui = ((flags & INIT_NO_GUI) == 0); // We only want to display the splash screen at startup shared_ptr<ScriptInterface> scriptInterface = g_GUI->GetScriptInterface(); JSContext* cx = scriptInterface->GetContext(); JSAutoRequest rq(cx); JS::RootedValue data(cx); if (g_GUI) { scriptInterface->Eval("({})", &data); scriptInterface->SetProperty(data, "isStartup", true); } InitPs(setup_gui, L"page_pregame.xml", g_GUI->GetScriptInterface().get(), data); } } catch (PSERROR_Game_World_MapLoadFailed& e) { // Map Loading failed // Start the engine so we have a GUI InitPs(true, L"page_pregame.xml", NULL, JS::UndefinedHandleValue); // Call script function to do the actual work // (delete game data, switch GUI page, show error, etc.) CancelLoad(CStr(e.what()).FromUTF8()); } }
void InitGraphics(const CmdLineArgs& args, int flags) { const bool setup_vmode = (flags & INIT_HAVE_VMODE) == 0; if(setup_vmode) { InitSDL(); if (!g_VideoMode.InitSDL()) throw PSERROR_System_VmodeFailed(); // abort startup #if !SDL_VERSION_ATLEAST(2, 0, 0) SDL_WM_SetCaption("0 A.D.", "0 A.D."); #endif } RunHardwareDetection(); tex_codec_register_all(); const int quality = SANE_TEX_QUALITY_DEFAULT; // TODO: set value from config file SetTextureQuality(quality); ogl_WarnIfError(); // Optionally start profiler GPU timings automatically // (By default it's only enabled by a hotkey, for performance/compatibility) bool profilerGPUEnable = false; CFG_GET_VAL("profiler2.gpu.autoenable", Bool, profilerGPUEnable); if (profilerGPUEnable) g_Profiler2.EnableGPU(); if(!g_Quickstart) { WriteSystemInfo(); // note: no longer vfs_display here. it's dog-slow due to unbuffered // file output and very rarely needed. } if(g_DisableAudio) { // speed up startup by disabling all sound // (OpenAL init will be skipped). // must be called before first snd_open. #if CONFIG2_AUDIO CSoundManager::SetEnabled(false); #endif } g_GUI = new CGUIManager(g_ScriptingHost.GetScriptInterface()); // (must come after SetVideoMode, since it calls ogl_Init) const char* missing = ogl_HaveExtensions(0, "GL_ARB_multitexture", "GL_EXT_draw_range_elements", "GL_ARB_texture_env_combine", "GL_ARB_texture_env_dot3", NULL); if(missing) { wchar_t buf[500]; swprintf_s(buf, ARRAY_SIZE(buf), L"The %hs extension doesn't appear to be available on your computer." L" The game may still work, though - you are welcome to try at your own risk." L" If not or it doesn't look right, upgrade your graphics card.", missing ); DEBUG_DISPLAY_ERROR(buf); // TODO: i18n } if (!ogl_HaveExtension("GL_ARB_texture_env_crossbar")) { DEBUG_DISPLAY_ERROR( L"The GL_ARB_texture_env_crossbar extension doesn't appear to be available on your computer." L" Shadows are not available and overall graphics quality might suffer." L" You are advised to try installing newer drivers and/or upgrade your graphics card."); g_Shadows = false; } ogl_WarnIfError(); InitRenderer(); InitInput(); ogl_WarnIfError(); try { if (!Autostart(args)) { const bool setup_gui = ((flags & INIT_NO_GUI) == 0); // We only want to display the splash screen at startup CScriptValRooted data; if (g_GUI) { ScriptInterface& scriptInterface = g_GUI->GetScriptInterface(); scriptInterface.Eval("({})", data); scriptInterface.SetProperty(data.get(), "isStartup", true); } InitPs(setup_gui, L"page_pregame.xml", data.get()); } } catch (PSERROR_Game_World_MapLoadFailed e) { // Map Loading failed // Start the engine so we have a GUI InitPs(true, L"page_pregame.xml", JSVAL_VOID); // Call script function to do the actual work // (delete game data, switch GUI page, show error, etc.) CancelLoad(CStr(e.what()).FromUTF8()); } }
/* * 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; }
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; }