forked from 0ad/0ad
Add a -autostart-nonvisual option. Patch by sacha_vrand. Fixes #4577.
This allows automated testing of AIs without any GUI or sound (similar to non-visual replays). Differential Revision: https://code.wildfiregames.com/D379 This was SVN commit r19645.
This commit is contained in:
parent
1bcad4698d
commit
a533fff883
@ -177,6 +177,7 @@
|
||||
{"nick": "Riemer"},
|
||||
{"name": "Rolf Sievers"},
|
||||
{"nick": "s0600204", "name": "Matthew Norwood"},
|
||||
{"nick": "sacha_vrand", "name": "Sacha Vrand"},
|
||||
{"nick": "SafaAlfulaij"},
|
||||
{"nick": "Sandarac"},
|
||||
{"nick": "sanderd17", "name": "Sander Deryckere"},
|
||||
|
32
binaries/data/mods/public/maps/scripts/NonVisualTrigger.js
Normal file
32
binaries/data/mods/public/maps/scripts/NonVisualTrigger.js
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* This will print the statistics at the end of a game.
|
||||
* In order for this to work, the player's state has to be changed before the event.
|
||||
*/
|
||||
Trigger.prototype.EndGameAction = function()
|
||||
{
|
||||
for (let playerId = 1; playerId < TriggerHelper.GetNumberOfPlayers(); ++playerId)
|
||||
{
|
||||
let cmpPlayer = QueryPlayerIDInterface(playerId);
|
||||
if (cmpPlayer && cmpPlayer.GetState() === "active")
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.once)
|
||||
return;
|
||||
|
||||
this.once = false;
|
||||
|
||||
for (let player of Engine.GetEntitiesWithInterface(IID_StatisticsTracker))
|
||||
{
|
||||
let cmpStatisticsTracker = Engine.QueryInterface(player, IID_StatisticsTracker);
|
||||
if (cmpStatisticsTracker)
|
||||
print(cmpStatisticsTracker.GetStatisticsJSON() + "\n");
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
let cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
|
||||
cmpTrigger.RegisterTrigger("OnPlayerWon", "EndGameAction", { "enabled": true });
|
||||
cmpTrigger.RegisterTrigger("OnPlayerDefeated", "EndGameAction", { "enabled": true });
|
||||
cmpTrigger.once = true;
|
||||
}
|
@ -216,6 +216,23 @@ StatisticsTracker.prototype.GetSequences = function()
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to print statistics for non-visual autostart games.
|
||||
* @return The player's statistics as a JSON string beautified with some indentations.
|
||||
*/
|
||||
StatisticsTracker.prototype.GetStatisticsJSON = function()
|
||||
{
|
||||
let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
|
||||
|
||||
let playerStatistics = {
|
||||
"playerID": cmpPlayer.GetPlayerID(),
|
||||
"playerState": cmpPlayer.GetState(),
|
||||
"statistics": this.GetStatistics()
|
||||
};
|
||||
|
||||
return JSON.stringify(playerStatistics, null, "\t");
|
||||
};
|
||||
|
||||
/**
|
||||
* Increments counter associated with certain entity/counter and type of given entity.
|
||||
* @param cmpIdentity - the entity identity component.
|
||||
|
@ -13,6 +13,9 @@ Autostart:
|
||||
-autostart-aiseed=AISEED sets the seed used for the AI random generator (default 0, use -1 for random)
|
||||
-autostart-civ=PLAYER:CIV sets PLAYER's civilisation to CIV (skirmish and random maps only)
|
||||
-autostart-team=PLAYER:TEAM sets the team for PLAYER (e.g. 2:2).
|
||||
-autostart-nonvisual disable any graphics and sounds
|
||||
-autostart-victory=SCRIPTNAME sets the victory conditions with SCRIPTNAME located in simulation/data/settings/victory_conditions/
|
||||
-autostart-victoryduration=NUM sets the victory duration NUM for specific victory conditions
|
||||
Multiplayer:
|
||||
-autostart-playername=NAME sets local player NAME (default 'anonymous')
|
||||
-autostart-host sets multiplayer host mode
|
||||
|
@ -77,6 +77,7 @@ that of Atlas depending on commandline parameters.
|
||||
#include "renderer/Renderer.h"
|
||||
#include "scriptinterface/ScriptEngine.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "simulation2/system/TurnManager.h"
|
||||
|
||||
#if OS_UNIX
|
||||
#include <unistd.h> // geteuid
|
||||
@ -380,6 +381,24 @@ static void Frame()
|
||||
LimitFPS();
|
||||
}
|
||||
|
||||
static void NonVisualFrame()
|
||||
{
|
||||
g_Profiler2.RecordFrameStart();
|
||||
PROFILE2("frame");
|
||||
g_Profiler2.IncrementFrameNumber();
|
||||
PROFILE2_ATTR("%d", g_Profiler2.GetFrameNumber());
|
||||
|
||||
static u32 turn = 0;
|
||||
debug_printf("Turn %u (%u)...\n", turn++, DEFAULT_TURN_LENGTH_SP);
|
||||
|
||||
g_Game->GetSimulation2()->Update(DEFAULT_TURN_LENGTH_SP);
|
||||
|
||||
g_Profiler.Frame();
|
||||
|
||||
if (g_Game->IsGameFinished())
|
||||
kill_mainloop();
|
||||
}
|
||||
|
||||
|
||||
static void MainControllerInit()
|
||||
{
|
||||
@ -438,8 +457,15 @@ static void RunGameOrAtlas(int argc, const char* argv[])
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Has("autostart-nonvisual") && args.Get("autostart").empty())
|
||||
{
|
||||
LOGERROR("-autostart-nonvisual cant be used alone. A map with -autostart=\"TYPEDIR/MAPNAME\" is needed.");
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isVisualReplay = args.Has("replay-visual");
|
||||
const bool isNonVisualReplay = args.Has("replay");
|
||||
const bool isNonVisual = args.Has("autostart-nonvisual");
|
||||
|
||||
const CStr replayFile =
|
||||
isVisualReplay ? args.Get("replay-visual") :
|
||||
@ -540,10 +566,21 @@ static void RunGameOrAtlas(int argc, const char* argv[])
|
||||
Shutdown(SHUTDOWN_FROM_CONFIG);
|
||||
continue;
|
||||
}
|
||||
InitGraphics(args, 0);
|
||||
MainControllerInit();
|
||||
while (!quit)
|
||||
Frame();
|
||||
|
||||
if (isNonVisual)
|
||||
{
|
||||
InitNonVisual(args);
|
||||
while (!quit)
|
||||
NonVisualFrame();
|
||||
}
|
||||
else
|
||||
{
|
||||
InitGraphics(args, 0);
|
||||
MainControllerInit();
|
||||
while (!quit)
|
||||
Frame();
|
||||
}
|
||||
|
||||
Shutdown(0);
|
||||
MainControllerShutdown();
|
||||
flags &= ~INIT_MODS;
|
||||
|
@ -461,3 +461,15 @@ CColor CGame::GetPlayerColor(player_id_t player) const
|
||||
|
||||
return m_PlayerColors[player];
|
||||
}
|
||||
|
||||
bool CGame::IsGameFinished() const
|
||||
{
|
||||
for (const std::pair<entity_id_t, IComponent*>& p : m_Simulation2->GetEntitiesWithInterface(IID_Player))
|
||||
{
|
||||
CmpPtr<ICmpPlayer> cmpPlayer(*m_Simulation2, p.first);
|
||||
if (cmpPlayer && cmpPlayer->GetState() == "won")
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -112,6 +112,14 @@ public:
|
||||
int GetViewedPlayerID();
|
||||
void SetViewedPlayerID(player_id_t playerID);
|
||||
|
||||
/**
|
||||
* Check if the game is finished by testing if there's a winner.
|
||||
* It is used to end a non visual autostarted game.
|
||||
*
|
||||
* @return true if there's a winner, false otherwise.
|
||||
*/
|
||||
bool IsGameFinished() const;
|
||||
|
||||
/**
|
||||
* Retrieving player colors from scripts is slow, so this updates an
|
||||
* internal cache of all players' colors.
|
||||
@ -132,6 +140,16 @@ public:
|
||||
return m_GameStarted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the graphics is disabled.
|
||||
*
|
||||
* @return bool true if the m_GameView is NULL, false otherwise.
|
||||
*/
|
||||
inline bool IsGraphicsDisabled() const
|
||||
{
|
||||
return !m_GameView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get m_IsVisualReplay.
|
||||
*
|
||||
|
@ -85,6 +85,7 @@
|
||||
#include "renderer/ModelRenderer.h"
|
||||
#include "scriptinterface/ScriptInterface.h"
|
||||
#include "scriptinterface/ScriptStats.h"
|
||||
#include "scriptinterface/ScriptConversions.h"
|
||||
#include "simulation2/Simulation2.h"
|
||||
#include "lobby/IXmppClient.h"
|
||||
#include "soundmanager/scripting/JSInterface_Sound.h"
|
||||
@ -711,22 +712,27 @@ static void ShutdownSDL()
|
||||
|
||||
void EndGame()
|
||||
{
|
||||
const bool nonVisual = g_Game && g_Game->IsGraphicsDisabled();
|
||||
|
||||
if (g_Game && g_Game->IsGameStarted() && !g_Game->IsVisualReplay() &&
|
||||
g_AtlasGameLoop && !g_AtlasGameLoop->running)
|
||||
g_AtlasGameLoop && !g_AtlasGameLoop->running && !nonVisual)
|
||||
VisualReplay::SaveReplayMetadata(g_GUI->GetActiveGUI()->GetScriptInterface().get());
|
||||
|
||||
SAFE_DELETE(g_NetClient);
|
||||
SAFE_DELETE(g_NetServer);
|
||||
SAFE_DELETE(g_Game);
|
||||
|
||||
ISoundManager::CloseGame();
|
||||
|
||||
g_Renderer.ResetState();
|
||||
if (!nonVisual)
|
||||
{
|
||||
ISoundManager::CloseGame();
|
||||
g_Renderer.ResetState();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Shutdown(int flags)
|
||||
{
|
||||
const bool nonVisual = g_Game && g_Game->IsGraphicsDisabled();
|
||||
|
||||
if ((flags & SHUTDOWN_FROM_CONFIG))
|
||||
goto from_config;
|
||||
|
||||
@ -740,11 +746,14 @@ void Shutdown(int flags)
|
||||
delete &g_TexMan;
|
||||
TIMER_END(L"shutdown TexMan");
|
||||
|
||||
// destroy renderer
|
||||
TIMER_BEGIN(L"shutdown Renderer");
|
||||
delete &g_Renderer;
|
||||
g_VBMan.Shutdown();
|
||||
TIMER_END(L"shutdown Renderer");
|
||||
// destroy renderer if it was initialised
|
||||
if (!nonVisual)
|
||||
{
|
||||
TIMER_BEGIN(L"shutdown Renderer");
|
||||
delete &g_Renderer;
|
||||
g_VBMan.Shutdown();
|
||||
TIMER_END(L"shutdown Renderer");
|
||||
}
|
||||
|
||||
g_Profiler2.ShutdownGPU();
|
||||
|
||||
@ -761,7 +770,8 @@ void Shutdown(int flags)
|
||||
ShutdownSDL();
|
||||
TIMER_END(L"shutdown SDL");
|
||||
|
||||
g_VideoMode.Shutdown();
|
||||
if (!nonVisual)
|
||||
g_VideoMode.Shutdown();
|
||||
|
||||
TIMER_BEGIN(L"shutdown UserReporter");
|
||||
g_UserReporter.Deinitialize();
|
||||
@ -964,7 +974,8 @@ bool Init(const CmdLineArgs& args, int flags)
|
||||
CNetHost::Initialize();
|
||||
|
||||
#if CONFIG2_AUDIO
|
||||
ISoundManager::CreateSoundManager();
|
||||
if (!args.Has("autostart-nonvisual"))
|
||||
ISoundManager::CreateSoundManager();
|
||||
#endif
|
||||
|
||||
// Check if there are mods specified on the command line,
|
||||
@ -1125,6 +1136,15 @@ void InitGraphics(const CmdLineArgs& args, int flags)
|
||||
}
|
||||
}
|
||||
|
||||
void InitNonVisual(const CmdLineArgs& args)
|
||||
{
|
||||
// Need some stuff for terrain movement costs:
|
||||
// (TODO: this ought to be independent of any graphics code)
|
||||
new CTerrainTextureManager;
|
||||
g_TexMan.LoadTerrainTextures();
|
||||
Autostart(args);
|
||||
}
|
||||
|
||||
void RenderGui(bool RenderingState)
|
||||
{
|
||||
g_DoRenderGui = RenderingState;
|
||||
@ -1199,6 +1219,10 @@ CStr8 LoadSettingsOfScenarioMap(const VfsPath &mapPath)
|
||||
* -autostart-civ=PLAYER:CIV sets PLAYER's civilisation to CIV
|
||||
* (skirmish and random maps only)
|
||||
* -autostart-team=PLAYER:TEAM sets the team for PLAYER (e.g. 2:2).
|
||||
* -autostart-nonvisual disable any graphics and sounds
|
||||
* -autostart-victory=SCRIPTNAME sets the victory conditions with SCRIPTNAME
|
||||
* located in simulation/data/settings/victory_conditions/
|
||||
* -autostart-victoryduration=NUM sets the victory duration NUM for specific victory conditions
|
||||
*
|
||||
* Multiplayer:
|
||||
* -autostart-playername=NAME sets local player NAME (default 'anonymous')
|
||||
@ -1228,7 +1252,8 @@ bool Autostart(const CmdLineArgs& args)
|
||||
if (autoStartName.empty())
|
||||
return false;
|
||||
|
||||
g_Game = new CGame();
|
||||
const bool nonVisual = args.Has("autostart-nonvisual");
|
||||
g_Game = new CGame(nonVisual, !nonVisual);
|
||||
|
||||
ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
|
||||
JSContext* cx = scriptInterface.GetContext();
|
||||
@ -1488,6 +1513,47 @@ bool Autostart(const CmdLineArgs& args)
|
||||
if (args.Has("autostart-playername"))
|
||||
userName = args.Get("autostart-playername").FromUTF8();
|
||||
|
||||
// Add additional scripts to the TriggerScripts property
|
||||
std::vector<CStrW> triggerScriptsVector;
|
||||
JS::RootedValue triggerScripts(cx);
|
||||
|
||||
if (scriptInterface.HasProperty(settings, "TriggerScripts"))
|
||||
{
|
||||
scriptInterface.GetProperty(settings, "TriggerScripts", &triggerScripts);
|
||||
FromJSVal_vector(cx, triggerScripts, triggerScriptsVector);
|
||||
}
|
||||
|
||||
if (nonVisual)
|
||||
{
|
||||
CStr nonVisualScript = "scripts/NonVisualTrigger.js";
|
||||
triggerScriptsVector.push_back(nonVisualScript.FromUTF8());
|
||||
}
|
||||
|
||||
if (args.Has("autostart-victory"))
|
||||
{
|
||||
CStrW scriptName = args.Get("autostart-victory").FromUTF8();
|
||||
CStrW scriptPath = L"simulation/data/settings/victory_conditions/" + scriptName + L".json";
|
||||
JS::RootedValue scriptData(cx);
|
||||
JS::RootedValue data(cx);
|
||||
JS::RootedValue victoryScripts(cx);
|
||||
|
||||
scriptInterface.ReadJSONFile(scriptPath, &scriptData);
|
||||
|
||||
if (!scriptData.isUndefined() && scriptInterface.GetProperty(scriptData, "Data", &data) && !data.isUndefined()
|
||||
&& scriptInterface.GetProperty(data, "Scripts", &victoryScripts) && !victoryScripts.isUndefined())
|
||||
{
|
||||
std::vector<CStrW> victoryScriptsVector;
|
||||
FromJSVal_vector(cx, victoryScripts, victoryScriptsVector);
|
||||
triggerScriptsVector.insert(triggerScriptsVector.end(), victoryScriptsVector.begin(), victoryScriptsVector.end());
|
||||
}
|
||||
}
|
||||
|
||||
ToJSVal_vector(cx, &triggerScripts, triggerScriptsVector);
|
||||
scriptInterface.SetProperty(settings, "TriggerScripts", triggerScripts);
|
||||
|
||||
if (args.Has("autostart-victoryduration"))
|
||||
scriptInterface.SetProperty(settings, "VictoryDuration", args.Get("autostart-victoryduration").ToInt());
|
||||
|
||||
if (args.Has("autostart-host"))
|
||||
{
|
||||
InitPs(true, L"page_loading.xml", &scriptInterface, mpInitData);
|
||||
@ -1531,6 +1597,9 @@ bool Autostart(const CmdLineArgs& args)
|
||||
PSRETURN ret = g_Game->ReallyStartGame();
|
||||
ENSURE(ret == PSRETURN_OK);
|
||||
|
||||
if (nonVisual)
|
||||
return true;
|
||||
|
||||
InitPs(true, L"page_session.xml", NULL, JS::UndefinedHandleValue);
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2014 Wildfire Games.
|
||||
/* Copyright (C) 2017 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -86,6 +86,7 @@ extern void MountMods(const Paths& paths, const std::vector<CStr>& mods);
|
||||
*/
|
||||
extern bool Init(const CmdLineArgs& args, int flags);
|
||||
extern void InitGraphics(const CmdLineArgs& args, int flags);
|
||||
extern void InitNonVisual(const CmdLineArgs& args);
|
||||
extern void Shutdown(int flags);
|
||||
extern void CancelLoad(const CStrW& message);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
/* Copyright (C) 2017 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -57,6 +57,11 @@ public:
|
||||
{
|
||||
return m_Script.Call<bool>("HasStartingCamera");
|
||||
}
|
||||
|
||||
virtual std::string GetState()
|
||||
{
|
||||
return m_Script.Call<std::string>("GetState");
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT_SCRIPT_WRAPPER(PlayerScripted)
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* Copyright (C) 2011 Wildfire Games.
|
||||
/* Copyright (C) 2017 Wildfire Games.
|
||||
* This file is part of 0 A.D.
|
||||
*
|
||||
* 0 A.D. is free software: you can redistribute it and/or modify
|
||||
@ -25,8 +25,9 @@ class CFixedVector3D;
|
||||
|
||||
/**
|
||||
* Player data.
|
||||
* (This interface only includes the functions needed by native code for loading maps,
|
||||
* and for minimap rendering; most player interaction is handled by scripts instead.)
|
||||
* (This interface includes the functions needed by native code for loading maps,
|
||||
* and for minimap rendering; most player interaction is handled by scripts instead.
|
||||
* Also includes some functions needed for the non visual autostart.)
|
||||
*/
|
||||
class ICmpPlayer : public IComponent
|
||||
{
|
||||
@ -37,6 +38,7 @@ public:
|
||||
virtual CFixedVector3D GetStartingCameraRot() = 0;
|
||||
|
||||
virtual bool HasStartingCamera() = 0;
|
||||
virtual std::string GetState() = 0;
|
||||
|
||||
DECLARE_INTERFACE_TYPE(Player)
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user