Ticket #9: t0009_visualreplay_v3_16726.patch

File t0009_visualreplay_v3_16726.patch, 23.4 KB (added by elexis, 9 years ago)
  • binaries/data/mods/public/gui/session/session.js

     
    555555        if (battleState)
    556556            global.music.setState(global.music.states[battleState]);
    557557    }
    558558}
    559559
     560function onReplayFinished()
     561{
     562    closeMenu();
     563    closeOpenDialogs();
     564    pauseGame();
     565    var btCaptions = [translateWithContext("replayFinished", "Yes"), translateWithContext("replayFinished", "No")];
     566    var btCode = [leaveGame, resumeGame];
     567    messageBox(400, 200, translateWithContext("replayFinished", "The replay has finished. Do you want to quit?"), translateWithContext("replayFinished","Confirmation"), 0, btCaptions, btCode);
     568}
     569
    560570/**
    561571* updates a status bar on the GUI
    562572* nameOfBar: name of the bar
    563573* points: points to show
    564574* maxPoints: max points
  • binaries/data/mods/public/gui/session/session.xml

     
    2020
    2121    <action on="SimulationUpdate">
    2222        onSimulationUpdate();
    2323    </action>
    2424
     25    <action on="ReplayFinished">
     26        onReplayFinished();
     27    </action>
     28
    2529    <action on="Press">
    2630        this.hidden = !this.hidden;
    2731    </action>
    2832
    2933    <!-- ================================  ================================ -->
  • build/premake/premake4.lua

     
    603603        "third_party/tinygettext/src",
    604604    }
    605605    extern_libs = {
    606606        "iconv",
    607607        "boost",
     608        "tinygettext"
    608609    }
    609610    setup_third_party_static_lib_project("tinygettext", source_dirs, extern_libs, { } )
    610611   
    611612    -- it's an external library and we don't want to modify its source to fix warnings, so we just disable them to avoid noise in the compile output
    612613    if _ACTION == "vs2013" then
  • source/main.cpp

     
    440440        return;
    441441
    442442    // run non-visual simulation replay if requested
    443443    if (args.Has("replay"))
    444444    {
     445        std::string replayFile = args.Get("replay");
     446        if (!FileExists(OsPath(replayFile)))
     447        {
     448            debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.c_str());
     449            return;
     450        }
    445451        Paths paths(args);
    446452        g_VFS = CreateVfs(20 * MiB);
    447453        g_VFS->Mount(L"cache/", paths.Cache(), VFS_MOUNT_ARCHIVABLE);
    448454        MountMods(paths, GetMods(args, INIT_MODS));
    449455
    450456        {
    451457            CReplayPlayer replay;
    452             replay.Load(args.Get("replay"));
     458            replay.Load(replayFile);
    453459            replay.Replay(args.Has("serializationtest"), args.Has("ooslog"));
    454460        }
    455461
    456462        g_VFS.reset();
    457463
    458464        CXeromyces::Terminate();
    459465        return;
    460466    }
    461467
     468    // If visual replay file does not exist, quit before starting the renderer
     469    if (args.Has("replay-visual"))
     470    {
     471        std::string replayFile = args.Get("replay-visual");
     472        if (!FileExists(OsPath(replayFile)))
     473        {
     474            debug_printf("ERROR: The requested replay file '%s' does not exist!\n", replayFile.c_str());
     475            return;
     476        }
     477    }
     478
     479
    462480    // run in archive-building mode if requested
    463481    if (args.Has("archivebuild"))
    464482    {
    465483        Paths paths(args);
    466484
  • source/network/NetTurnManager.cpp

     
    1 /* Copyright (C) 2012 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
     
    223223    NETTURN_LOG((L"OnSyncError(%d, %hs)\n", turn, Hexify(expectedHash).c_str()));
    224224
    225225    // Only complain the first time
    226226    if (m_HasSyncError)
    227227        return;
    228     m_HasSyncError = true;
    229228
    230229    bool quick = !TurnNeedsFullHash(turn);
    231230    std::string hash;
    232     bool ok = m_Simulation2.ComputeStateHash(hash, quick);
    233     ENSURE(ok);
     231    ENSURE(m_Simulation2.ComputeStateHash(hash, quick));
    234232
    235233    OsPath path = psLogDir()/"oos_dump.txt";
    236234    std::ofstream file (OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc);
    237235    m_Simulation2.DumpDebugState(file);
    238236    file.close();
    239237
     238    hash = Hexify(hash);
     239    const std::string& expectedHashHex = Hexify(expectedHash);
     240
     241    DisplayOOSError(turn, hash, expectedHashHex, false, &path);
     242}
     243
     244void CNetTurnManager::DisplayOOSError(u32 turn, std::string& hash, const std::string& expectedHash, const bool isReplay, OsPath* path = NULL)
     245{
     246    m_HasSyncError = true;
     247
    240248    std::stringstream msg;
    241     msg << "Out of sync on turn " << turn << ": expected hash " << Hexify(expectedHash) << "\n\n";
    242     msg << "Current state: turn " << m_CurrentTurn << ", hash " << Hexify(hash) << "\n\n";
    243     msg << "Dumping current state to " << utf8_from_wstring(path.string());
     249    msg << "Out of sync on turn " << turn << ": expected hash " << expectedHash << "\n";
     250
     251    if (expectedHash != hash || m_CurrentTurn != turn)
     252        msg << "\nCurrent state: turn " << m_CurrentTurn << ", hash " << hash << "\n\n";
     253
     254    if (isReplay)
     255        msg << "\nThe current game state is different from the original game state.\n\n";
     256    else
     257    {
     258        if (expectedHash == hash)
     259            msg << "Your game state is identical to the hosts game state.\n\n";
     260        else
     261            msg << "Your game state is different from the hosts game state.\n\n";
     262    }
     263
     264    if (path)
     265        msg << "Dumping current state to " << utf8_from_wstring(OsPath(*path).string());
     266
     267    LOGERROR("%s", msg.str());
     268
    244269    if (g_GUI)
    245270        g_GUI->DisplayMessageBox(600, 350, L"Sync error", wstring_from_utf8(msg.str()));
    246     else
    247         LOGERROR("%s", msg.str());
    248271}
    249272
    250273void CNetTurnManager::Interpolate(float simFrameLength, float realFrameLength)
    251274{
    252275    // TODO: using m_TurnLength might be a bit dodgy when length changes - maybe
     
    320343void CNetTurnManager::QuickSave()
    321344{
    322345    TIMER(L"QuickSave");
    323346   
    324347    std::stringstream stream;
    325     bool ok = m_Simulation2.SerializeState(stream);
    326     if (!ok)
     348    if (!m_Simulation2.SerializeState(stream))
    327349    {
    328350        LOGERROR("Failed to quicksave game");
    329351        return;
    330352    }
    331353
     
    348370        LOGERROR("Cannot quickload game - no game was quicksaved");
    349371        return;
    350372    }
    351373
    352374    std::stringstream stream(m_QuickSaveState);
    353     bool ok = m_Simulation2.DeserializeState(stream);
    354     if (!ok)
     375    if (!m_Simulation2.DeserializeState(stream))
    355376    {
    356377        LOGERROR("Failed to quickload game");
    357378        return;
    358379    }
    359380
     
    400421{
    401422    bool quick = !TurnNeedsFullHash(turn);
    402423    std::string hash;
    403424    {
    404425        PROFILE3("state hash check");
    405         bool ok = m_Simulation2.ComputeStateHash(hash, quick);
    406         ENSURE(ok);
     426        ENSURE(m_Simulation2.ComputeStateHash(hash, quick));
    407427    }
    408428
    409429    NETTURN_LOG((L"NotifyFinishedUpdate(%d, %hs)\n", turn, Hexify(hash).c_str()));
    410430
    411431    m_Replay.Hash(hash, quick);
     
    450470{
    451471#if 0 // this hurts performance and is only useful for verifying log replays
    452472    std::string hash;
    453473    {
    454474        PROFILE3("state hash check");
    455         bool ok = m_Simulation2.ComputeStateHash(hash);
    456         ENSURE(ok);
     475        ENSURE(m_Simulation2.ComputeStateHash(hash));
    457476    }
    458477    m_Replay.Hash(hash);
    459478#endif
    460479}
    461480
    462481void CNetLocalTurnManager::OnSimulationMessage(CSimulationMessage* UNUSED(msg))
    463482{
    464483    debug_warn(L"This should never be called");
    465484}
    466485
     486CNetReplayTurnManager::CNetReplayTurnManager(CSimulation2& simulation, IReplayLogger& replay) :
     487    CNetLocalTurnManager(simulation, replay)
     488{
     489}
    467490
     491void CNetReplayTurnManager::StoreReplayCommand(u32 turn, int player, const std::string& command)
     492{
     493    m_ReplayCommands[turn][player].push_back(command);
     494}
     495
     496void CNetReplayTurnManager::StoreReplayHash(u32 turn, const std::string& hash, bool quick)
     497{
     498    m_ReplayHash[turn] = std::make_pair(hash, quick);
     499}
    468500
     501void CNetReplayTurnManager::StoreReplayTurnLength(u32 turn, u32 turnLength)
     502{
     503    m_ReplayTurnLengths[turn] = turnLength;
     504
     505    // Initialize turn length
     506    if (turn == 0)
     507        m_TurnLength = m_ReplayTurnLengths[0];
     508}
     509
     510void CNetReplayTurnManager::StoreFinalReplayTurn(u32 turn)
     511{
     512    m_FinalReplayTurn = turn;
     513}
     514
     515void CNetReplayTurnManager::NotifyFinishedUpdate(u32 turn)
     516{
     517    if (turn > m_FinalReplayTurn)
     518        return;
     519
     520    debug_printf("Executing turn %d of %d\n", turn, m_FinalReplayTurn);
     521    DoTurn(turn);
     522
     523    // Compare hash if it exists in the replay and if we didn't have an oos already
     524    if (m_HasSyncError || m_ReplayHash.find(turn) == m_ReplayHash.end())
     525        return;
     526
     527    std::string expectedHash = m_ReplayHash[turn].first;
     528    bool quickHash = m_ReplayHash[turn].second;
     529
     530    // Compute hash
     531    std::string hash;
     532    ENSURE(m_Simulation2.ComputeStateHash(hash, quickHash));
     533    hash = Hexify(hash);
     534
     535    if (hash != expectedHash)
     536        DisplayOOSError(turn, hash, expectedHash, true);
     537}
     538
     539void CNetReplayTurnManager::DoTurn(u32 turn)
     540{
     541    // Save turn length
     542    m_TurnLength = m_ReplayTurnLengths[turn];
     543
     544    // Simulate commands for that turn
     545    for (auto& command : m_ReplayCommands[turn])
     546    {
     547        for (size_t i = 0; i < command.second.size(); ++i)
     548        {
     549            JS::RootedValue data(m_Simulation2.GetScriptInterface().GetContext());
     550            m_Simulation2.GetScriptInterface().ParseJSON(command.second[i], &data);
     551            AddCommand(m_ClientId, command.first, data, m_CurrentTurn + 1);
     552        }
     553    }
     554}
    469555
    470556CNetServerTurnManager::CNetServerTurnManager(CNetServerWorker& server) :
    471557    m_NetServer(server), m_ReadyTurn(1), m_TurnLength(DEFAULT_TURN_LENGTH_MP)
    472558{
    473559    // The first turn we will actually execute is number 2,
  • source/network/NetTurnManager.h

     
    1 /* Copyright (C) 2012 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
     
    1717
    1818#ifndef INCLUDED_NETTURNMANAGER
    1919#define INCLUDED_NETTURNMANAGER
    2020
    2121#include "simulation2/helpers/SimulationCommand.h"
     22#include "lib/os_path.h"
    2223
    2324#include <list>
    2425#include <map>
     26#include <vector>
    2527
    2628class CNetServerWorker;
    2729class CNetClient;
    2830class CSimulationMessage;
    2931class CSimulation2;
     
    106108     * Called when there has been an out-of-sync error.
    107109     */
    108110    virtual void OnSyncError(u32 turn, const std::string& expectedHash);
    109111
    110112    /**
     113     * Shows a message box when an out of sync error has been detected in the session or visual replay.
     114     */
     115    virtual void DisplayOOSError(u32 turn, std::string& hash, const std::string& expectedHash, const bool isReplay, OsPath* path);
     116
     117    /**
    111118     * Called by simulation code, to add a new command to be distributed to all clients and executed soon.
    112119     */
    113120    virtual void PostCommand(JS::HandleValue data) = 0;
    114121
    115122    /**
     
    187194    std::list<std::string> m_TimeWarpStates;
    188195    std::string m_QuickSaveState; // TODO: should implement a proper disk-based quicksave system
    189196    std::string m_QuickSaveMetadata;
    190197};
    191198
     199
    192200/**
    193201 * Implementation of CNetTurnManager for network clients.
    194202 */
    195203class CNetClientTurnManager : public CNetTurnManager
    196204{
     
    231239
    232240    virtual void NotifyFinishedUpdate(u32 turn);
    233241};
    234242
    235243
     244
     245/**
     246 * Implementation of CNetTurnManager for replay games.
     247 */
     248class CNetReplayTurnManager : public CNetLocalTurnManager
     249{
     250public:
     251    CNetReplayTurnManager(CSimulation2& simulation, IReplayLogger& replay);
     252
     253    void StoreReplayCommand(u32 turn, int player, const std::string& command);
     254
     255    void StoreReplayTurnLength(u32 turn, u32 turnLength);
     256
     257    void StoreReplayHash(u32 turn, const std::string& hash, bool quick);
     258
     259    void StoreFinalReplayTurn(u32 turn);
     260
     261
     262protected:
     263    virtual void NotifyFinishedUpdate(u32 turn);
     264
     265    void DoTurn(u32 turn);
     266
     267    // Contains the commands of every player on each turn
     268    std::map<u32, std::map<int, std::vector<std::string> > > m_ReplayCommands;
     269
     270    // Contains the length of every turn
     271    std::map<u32, u32> m_ReplayTurnLengths;
     272
     273    // Contains all replay hash values and weather or not the quick hash method was used
     274    std::map<u32, std::pair<std::string, bool> > m_ReplayHash;
     275
     276    // The number of the last turn in the replay
     277    u32 m_FinalReplayTurn;
     278
     279};
    236280/**
    237281 * The server-side counterpart to CNetClientTurnManager.
    238282 * Records the turn state of each client, and sends turn advancement messages
    239283 * when all clients are ready.
    240284 *
  • source/ps/Game.cpp

     
    6161
    6262/**
    6363 * Constructor
    6464 *
    6565 **/
    66 CGame::CGame(bool disableGraphics):
     66CGame::CGame(bool disableGraphics, bool replayLog):
    6767    m_World(new CWorld(this)),
    6868    m_Simulation2(new CSimulation2(&m_World->GetUnitManager(), g_ScriptRuntime, m_World->GetTerrain())),
    6969    m_GameView(disableGraphics ? NULL : new CGameView(this)),
    7070    m_GameStarted(false),
    7171    m_Paused(false),
    7272    m_SimRate(1.0f),
    7373    m_PlayerID(-1),
    74     m_IsSavedGame(false)
     74    m_IsSavedGame(false),
     75    m_IsReplay(false),
     76    m_ReplayStream(NULL)
    7577{
    76     m_ReplayLogger = new CReplayLogger(m_Simulation2->GetScriptInterface());
    7778    // TODO: should use CDummyReplayLogger unless activated by cmd-line arg, perhaps?
     79    if (replayLog)
     80        m_ReplayLogger = new CReplayLogger(m_Simulation2->GetScriptInterface());
     81    else
     82        m_ReplayLogger = new CDummyReplayLogger();
    7883
    7984    // Need to set the CObjectManager references after various objects have
    8085    // been initialised, so do it here rather than via the initialisers above.
    8186    if (m_GameView)
    8287        m_World->GetUnitManager().SetObjectManager(m_GameView->GetObjectManager());
    8388
    8489    m_TurnManager = new CNetLocalTurnManager(*m_Simulation2, GetReplayLogger()); // this will get replaced if we're a net server/client
    8590
    8691    m_Simulation2->LoadDefaultScripts();
    8792}
    88 
    8993/**
    9094 * Destructor
    9195 *
    9296 **/
    9397CGame::~CGame()
     
    99103    delete m_TurnManager;
    100104    delete m_GameView;
    101105    delete m_Simulation2;
    102106    delete m_World;
    103107    delete m_ReplayLogger;
     108    delete m_ReplayStream;
    104109}
    105110
    106111void CGame::SetTurnManager(CNetTurnManager* turnManager)
    107112{
    108113    if (m_TurnManager)
     
    112117
    113118    if (m_TurnManager)
    114119        m_TurnManager->SetPlayerID(m_PlayerID);
    115120}
    116121
     122int CGame::LoadReplayData()
     123{
     124    ENSURE(m_IsReplay);
     125    ENSURE(!m_ReplayPath.empty());
     126
     127    CNetReplayTurnManager* replayTurnMgr = static_cast<CNetReplayTurnManager*>(GetTurnManager());
     128
     129    u32 currentTurn = 0;
     130    std::string type;
     131    while ((*m_ReplayStream >> type).good())
     132    {
     133        if (type == "turn")
     134        {
     135            u32 turn = 0;
     136            u32 turnLength = 0;
     137            *m_ReplayStream >> turn >> turnLength;
     138            ENSURE(turn == currentTurn);
     139            replayTurnMgr->StoreReplayTurnLength(currentTurn, turnLength);
     140        }
     141        else if (type == "cmd")
     142        {
     143            player_id_t player;
     144            *m_ReplayStream >> player;
     145
     146            std::string line;
     147            std::getline(*m_ReplayStream, line);
     148            replayTurnMgr->StoreReplayCommand(currentTurn, player, line);
     149        }
     150        else if (type == "hash" || type == "hash-quick")
     151        {
     152            bool quick = (type == "hash-quick");
     153            std::string replayHash;
     154            *m_ReplayStream >> replayHash;
     155            replayTurnMgr->StoreReplayHash(currentTurn, replayHash, quick);
     156        }
     157        else if (type == "end")
     158        {
     159            currentTurn++;
     160        }
     161        else
     162        {
     163            CancelLoad(L"Failed to load replay data (unrecognized content)");
     164        }
     165    }
     166    m_FinalReplayTurn = currentTurn;
     167    replayTurnMgr->StoreFinalReplayTurn(currentTurn);
     168    return 0;
     169}
     170
     171bool CGame::StartReplay(const std::string& replayPath)
     172{
     173    m_IsReplay = true;
     174    ScriptInterface& scriptInterface = m_Simulation2->GetScriptInterface();
     175
     176    SetTurnManager(new CNetReplayTurnManager(*m_Simulation2, GetReplayLogger()));
     177
     178    m_ReplayPath = replayPath;
     179    m_ReplayStream = new std::ifstream(m_ReplayPath.c_str());
     180
     181    std::string type;
     182    ENSURE((*m_ReplayStream >> type).good() && type == "start");
     183
     184    std::string line;
     185    std::getline(*m_ReplayStream, line);
     186    JS::RootedValue attribs(scriptInterface.GetContext());
     187    scriptInterface.ParseJSON(line, &attribs);
     188    StartGame(&attribs, "");
     189
     190    return true;
     191}
    117192
    118193/**
    119194 * Initializes the game with the set of attributes provided.
    120195 * Makes calls to initialize the game view, world, and simulation objects.
    121196 * Calls are made to facilitate progress reporting of the initialization.
     
    174249        RegMemFun(g_Renderer.GetSingletonPtr()->GetWaterManager(), &WaterManager::LoadWaterTextures, L"LoadWaterTextures", 80);
    175250
    176251    if (m_IsSavedGame)
    177252        RegMemFun(this, &CGame::LoadInitialState, L"Loading game", 1000);
    178253
     254    if (m_IsReplay)
     255        RegMemFun(this, &CGame::LoadReplayData, L"Loading replay data", 1000);
     256
    179257    LDR_EndRegistering();
    180258}
    181259
    182260int CGame::LoadInitialState()
    183261{
     
    261339int CGame::GetPlayerID()
    262340{
    263341    return m_PlayerID;
    264342}
    265343
    266 void CGame::SetPlayerID(int playerID)
     344void CGame::SetPlayerID(player_id_t playerID)
    267345{
    268346    m_PlayerID = playerID;
    269347    if (m_TurnManager)
    270348        m_TurnManager->SetPlayerID(m_PlayerID);
    271349}
    272350
    273351void CGame::StartGame(JS::MutableHandleValue attribs, const std::string& savedState)
    274352{
    275     m_ReplayLogger->StartGame(attribs);
     353    if (m_ReplayLogger != false)
     354        m_ReplayLogger->StartGame(attribs);
     355
    276356    RegisterInit(attribs, savedState);
    277357}
    278358
    279359// TODO: doInterpolate is optional because Atlas interpolates explicitly,
    280360// so that it has more control over the update rate. The game might want to
     
    308388        {
    309389            {
    310390                PROFILE3("gui sim update");
    311391                g_GUI->SendEventToAll("SimulationUpdate");
    312392            }
     393             if (m_IsReplay && m_TurnManager->GetCurrentTurn() == m_FinalReplayTurn - 1)
     394                 g_GUI->SendEventToAll("ReplayFinished");
    313395
    314396            GetView()->GetLOSTexture().MakeDirty();
    315397        }
    316398       
    317399        if (CRenderer::IsInitialised())
     
    360442            m_PlayerColors[i] = cmpPlayer->GetColor();
    361443    }
    362444}
    363445
    364446
    365 CColor CGame::GetPlayerColor(int player) const
     447CColor CGame::GetPlayerColor(player_id_t player) const
    366448{
    367449    if (player < 0 || player >= (int)m_PlayerColors.size())
    368450        return BrokenColor;
    369451
    370452    return m_PlayerColors[player];
  • source/ps/Game.h

     
    1 /* Copyright (C) 2013 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
     
    2020
    2121#include "ps/Errors.h"
    2222#include <vector>
    2323
    2424#include "scriptinterface/ScriptVal.h"
     25#include "simulation2/helpers/Player.h"
    2526
    2627class CWorld;
    2728class CSimulation2;
    2829class CGameView;
    2930class CNetTurnManager;
     
    5859    /**
    5960     * Timescale multiplier for simulation rate.
    6061     **/
    6162    float m_SimRate;
    6263
    63     int m_PlayerID;
     64    player_id_t m_PlayerID;
    6465
    6566    CNetTurnManager* m_TurnManager;
    6667
    6768public:
    68     CGame(bool disableGraphics = false);
     69    CGame(bool disableGraphics = false, bool replayLog = true);
    6970    ~CGame();
    7071
    7172    /**
    7273     * the game is paused and no updates will be performed if true.
    7374     **/
    7475    bool m_Paused;
    7576
    7677    void StartGame(JS::MutableHandleValue attribs, const std::string& savedState);
    7778    PSRETURN ReallyStartGame();
    7879
     80    bool StartReplay(const std::string& replayPath);
     81
    7982    /**
    8083     * Periodic heartbeat that controls the process. performs all per-frame updates.
    8184     * Simulation update is called and game status update is called.
    8285     *
    8386     * @param deltaRealTime Elapsed real time since last beat/frame, in seconds.
     
    8891    bool Update(const double deltaRealTime, bool doInterpolate = true);
    8992
    9093    void Interpolate(float simFrameLength, float realFrameLength);
    9194
    9295    int GetPlayerID();
    93     void SetPlayerID(int playerID);
     96    void SetPlayerID(player_id_t playerID);
    9497
    9598    /**
    9699     * Retrieving player colors from scripts is slow, so this updates an
    97100     * internal cache of all players' colors.
    98101     * Call this just before rendering, so it will always have the latest
    99102     * colors.
    100103     */
    101104    void CachePlayerColors();
    102105
    103     CColor GetPlayerColor(int player) const;
     106    CColor GetPlayerColor(player_id_t player) const;
    104107
    105108    /**
    106109     * Get m_GameStarted.
    107110     *
    108111     * @return bool the value of m_GameStarted.
     
    169172    std::vector<CColor> m_PlayerColors;
    170173
    171174    int LoadInitialState();
    172175    std::string m_InitialSavedState; // valid between RegisterInit and LoadInitialState
    173176    bool m_IsSavedGame; // true if loading a saved game; false for a new game
     177
     178    int LoadReplayData();
     179    std::string m_ReplayPath;
     180    bool m_IsReplay;
     181    std::istream* m_ReplayStream;
     182    u32 m_FinalReplayTurn;
    174183};
    175184
    176185extern CGame *g_Game;
    177186
    178187#endif
  • source/ps/GameSetup/GameSetup.cpp

     
    879879    srand(time(NULL));  // NOTE: this rand should *not* be used for simulation!
    880880}
    881881
    882882bool Autostart(const CmdLineArgs& args);
    883883
     884// Returns true if and only if the user has intended to replay a file
     885bool VisualReplay(const std::string replayFile);
     886
    884887bool Init(const CmdLineArgs& args, int flags)
    885888{
    886889    h_mgr_init();
    887890
    888891    // Do this as soon as possible, because it chdirs
     
    10711074
    10721075    ogl_WarnIfError();
    10731076
    10741077    try
    10751078    {
    1076         if (!Autostart(args))
     1079        if (!VisualReplay(args.Get("replay-visual")) && !Autostart(args))
    10771080        {
    10781081            const bool setup_gui = ((flags & INIT_NO_GUI) == 0);
    10791082            // We only want to display the splash screen at startup
    10801083            shared_ptr<ScriptInterface> scriptInterface = g_GUI->GetScriptInterface();
    10811084            JSContext* cx = scriptInterface->GetContext();
     
    14681471    }
    14691472
    14701473    return true;
    14711474}
    14721475
     1476bool VisualReplay(const std::string replayFile)
     1477{
     1478    if (!FileExists(OsPath(replayFile)))
     1479         return false;
     1480
     1481    g_Game = new CGame(false, false);
     1482    g_Game->SetPlayerID(-1);
     1483    g_Game->StartReplay(replayFile);
     1484
     1485    // TODO: Non progressive load can fail - need a decent way to handle this
     1486    LDR_NonprogressiveLoad();
     1487
     1488    PSRETURN ret = g_Game->ReallyStartGame();
     1489    ENSURE(ret == PSRETURN_OK);
     1490
     1491    ScriptInterface& scriptInterface = g_Game->GetSimulation2()->GetScriptInterface();
     1492
     1493    InitPs(true, L"page_session.xml", &scriptInterface, JS::UndefinedHandleValue);
     1494    return true;
     1495}
     1496
    14731497void CancelLoad(const CStrW& message)
    14741498{
    14751499    shared_ptr<ScriptInterface> pScriptInterface = g_GUI->GetActiveGUI()->GetScriptInterface();
    14761500    JSContext* cx = pScriptInterface->GetContext();
    14771501    JSAutoRequest rq(cx);
  • source/ps/Replay.h

     
    5454 */
    5555class CDummyReplayLogger : public IReplayLogger
    5656{
    5757public:
    5858    virtual void StartGame(JS::MutableHandleValue UNUSED(attribs)) { }
    59     virtual void Turn(u32 UNUSED(n), u32 UNUSED(turnLength), const std::vector<SimulationCommand>& UNUSED(commands)) { }
     59    virtual void Turn(u32 UNUSED(n), u32 UNUSED(turnLength), std::vector<SimulationCommand>& UNUSED(commands)) { }
    6060    virtual void Hash(const std::string& UNUSED(hash), bool UNUSED(quick)) { }
    6161};
    6262
    6363/**
    6464 * Implementation of IReplayLogger that saves data to a file in the logs directory.