Fixes autostart init of player data, patch by mimo, fixes #2798.

Cleans up some of the autostart logic, removes and renames a few options
for clarity.
Updates readme.txt to reflect these changes and include some examples

This was SVN commit r15761.
This commit is contained in:
historic_bruno
2014-09-17 01:15:03 +00:00
parent b5f32e720f
commit c56407a1f8
2 changed files with 110 additions and 99 deletions
+18 -13
View File
@@ -7,20 +7,25 @@ Basic gameplay:
-quickstart load faster (disables audio and some system info logging)
Autostart:
-autostart=TYPEDIR/MAPNAME enables autostart, selects map in subdirectory of maps/
-autostart-ai=PLAYER:AI adds named AI to the given PLAYER (e.g. 2:testbot)
-autostart-aidiff=PLAYER:level changes the difficulty setting for AI PLAYER (0: easy, 3: very hard)
-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: easy, 3: very hard)
-autostart-civ=PLAYER:CIV sets PLAYER's civilisation to CIV (skirmish and random maps only)
Multiplayer:
-autostart-playername=NAME multiplayer local player NAME (default 'anonymous')
-autostart-host multiplayer host mode
-autostart-players=NUMBER multiplayer host: NUMBER of client players (default 2)
-autostart-client multiplayer client mode
-autostart-ip=IP multiplayer client: connect to this host IP
Random maps only:
-autostart-random=SEED random map with SEED value (default 0, use -1 for random)
-autostart-size=TILES random map SIZE in tiles (default 192)
-autostart-players=NUMBER NUMBER of players on random map
-autostart-civ=PLAYER:CIV changes the civilisation of player PLAYER to CIV (default athen)
-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
Configuration:
-conf=KEY:VALUE set a config value
+92 -86
View File
@@ -1126,8 +1126,8 @@ CStr8 LoadSettingsOfScenarioMap(const VfsPath &mapPath)
if (INFO::OK != loadResult)
{
LOGERROR(L"Unable to load map file - maybe path typo?");
throw PSERROR_Game_World_MapLoadFailed("Unable to load map file - maybe path typo?");
LOGERROR(L"LoadSettingsOfScenarioMap: Unable to load map file '%ls'", mapPath.string().c_str());
throw PSERROR_Game_World_MapLoadFailed("Unable to load map file, check the path for typos.");
}
XMBElement mapElement = mapFile.GetRoot();
@@ -1152,38 +1152,41 @@ CStr8 LoadSettingsOfScenarioMap(const VfsPath &mapPath)
return mapElement.GetText();
}
/*
* Command line options for autostart (keep synchronized with 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: easy, 3: very hard)
* -autostart-civ=PLAYER:CIV sets PLAYER's civilisation to CIV (skirmish and random maps only)
* 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)
{
/*
* 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)
* -autostart-civ=1:hele -- set player #1 civ to "hele"
*
* 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";
autoStartName = "scenarios/Arcadia 02";
#endif
if (autoStartName.empty())
{
return false;
}
g_Game = new CGame();
@@ -1201,8 +1204,7 @@ bool Autostart(const CmdLineArgs& args)
// 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 (as was
// done by -autostart-random)
// - 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();
@@ -1212,18 +1214,15 @@ bool Autostart(const CmdLineArgs& args)
{
// Default seed is 0
u32 seed = 0;
CStr seedArg = args.Get("autostart-random");
CStr seedArg = args.Get("autostart-seed");
if (!seedArg.empty())
{
if (seedArg.compare("-1") == 0)
{ // Random seed value
// 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
@@ -1240,7 +1239,7 @@ bool Autostart(const CmdLineArgs& args)
else
{
// Problem with JSON file
LOGERROR(L"Error reading random map script '%ls'", scriptPath.c_str());
LOGERROR(L"Autostart: Error reading random map script '%ls'", scriptPath.c_str());
throw PSERROR_Game_World_MapLoadFailed("Error reading random map script.\nCheck application log for details.");
}
@@ -1270,48 +1269,36 @@ bool Autostart(const CmdLineArgs& args)
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
// 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")
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
// less-than-completely configured
// (Omitting this may cause the loading screen to display "Loading (undefined)",
// for example...)
CStr8 mapSettingsJSON = LoadSettingsOfScenarioMap("maps/" + autoStartName + ".xml");
scriptInterface.ParseJSON(mapSettingsJSON, &settings);
mapType = "scenario";
}
else if (mapDirectory == L"skirmishes")
{
// In skirmish mode, the player initialization data is taken from the
// game-setup settings (see CGame::RegisterInit(...)). If some player
// is not initialized (PlayerData[] holding fewer/more entries than
// defined in map), there's a crash.
// To prevent this, we mimic the behavior of the game setup screen by
// retrieving the map settings from the actual map xml...
// partially configured
CStr8 mapSettingsJSON = LoadSettingsOfScenarioMap("maps/" + autoStartName + ".xml");
scriptInterface.ParseJSON(mapSettingsJSON, &settings);
// ...and initialize the playerData array being edited by
// autostart-civ et.al. with the real map data, so sensible values
// are always present:
// Initialize the playerData array being modified by autostart
// with the real map data, so sensible values are present:
scriptInterface.GetProperty(settings, "PlayerData", &playerData);
mapType = "skirmish";
if (mapDirectory == L"scenarios")
mapType = "scenario";
else
mapType = "skirmish";
}
if (mapType.empty())
else
{
LOGERROR(L"Unrecognized map type '%ls' detected", mapType.c_str());
throw PSERROR_Game_World_MapLoadFailed("Unrecognized map type.\nConsult GameSetup.cpp for the currently supported types.");
LOGERROR(L"Autostart: Unrecognized map type '%ls'", mapDirectory.c_str());
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 player data for AIs
@@ -1321,16 +1308,22 @@ bool Autostart(const CmdLineArgs& args)
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, i, &player) || player.isUndefined())
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(L"Autostart: Invalid player %d in autostart-ai option", playerID);
continue;
}
scriptInterface.Eval("({})", &player);
}
int playerID = aiArgs[i].BeforeFirst(":").ToInt();
CStr name = aiArgs[i].AfterFirst(":");
scriptInterface.SetProperty(player, "AI", std::string(name));
scriptInterface.SetProperty(player, "AIDiff", 2);
scriptInterface.SetPropertyInt(playerData, playerID-1, player);
@@ -1342,16 +1335,22 @@ bool Autostart(const CmdLineArgs& args)
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, i, &player) || player.isUndefined())
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(L"Autostart: Invalid player %d in autostart-aidiff option", playerID);
continue;
}
scriptInterface.Eval("({})", &player);
}
int playerID = civArgs[i].BeforeFirst(":").ToInt();
int difficulty = civArgs[i].AfterFirst(":").ToInt();
int difficulty = civArgs[i].AfterFirst(":").ToInt();
scriptInterface.SetProperty(player, "AIDiff", difficulty);
scriptInterface.SetPropertyInt(playerData, playerID-1, player);
}
@@ -1359,22 +1358,33 @@ bool Autostart(const CmdLineArgs& args)
// Set player data for Civs
if (args.Has("autostart-civ"))
{
std::vector<CStr> civArgs = args.GetMultiple("autostart-civ");
for (size_t i = 0; i < civArgs.size(); ++i)
if (mapDirectory != L"scenarios")
{
// Instead of overwriting existing player data, modify the array
JS::RootedValue player(cx);
if (!scriptInterface.GetPropertyInt(playerData, i, &player) || player.isUndefined())
std::vector<CStr> civArgs = args.GetMultiple("autostart-civ");
for (size_t i = 0; i < civArgs.size(); ++i)
{
scriptInterface.Eval("({})", &player);
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(L"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);
}
int playerID = civArgs[i].BeforeFirst(":").ToInt();
CStr name = civArgs[i].AfterFirst(":");
scriptInterface.SetProperty(player, "Civ", std::string(name));
scriptInterface.SetPropertyInt(playerData, playerID-1, player);
}
else
LOGWARNING(L"Autostart: Option 'autostart-civ' is invalid for scenarios");
}
// Add player data to map settings
@@ -1399,10 +1409,8 @@ bool Autostart(const CmdLineArgs& args)
InitPs(true, L"page_loading.xml", &scriptInterface, mpInitData);
size_t maxPlayers = 2;
if (args.Has("autostart-players"))
{
maxPlayers = args.Get("autostart-players").ToUInt();
}
if (args.Has("autostart-host-players"))
maxPlayers = args.Get("autostart-host-players").ToUInt();
g_NetServer = new CNetServer(maxPlayers);
@@ -1422,11 +1430,9 @@ bool Autostart(const CmdLineArgs& args)
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");
}
CStr ip = args.Get("autostart-client");
if (ip == "")
ip = "127.0.0.1";
bool ok = g_NetClient->SetupConnection(ip);
ENSURE(ok);