Ticket #3433: 3433_replay_cache_v1.5.patch

File 3433_replay_cache_v1.5.patch, 11.8 KB (added by Imarok, 8 years ago)

rebased after r18561

  • binaries/data/mods/public/gui/replaymenu/replay_actions.js

     
    127127    });
    128128}
    129129
     130function reloadCache()
     131{
     132    Engine.ReloadReplayCache();
     133
     134    let selected = Engine.GetGUIObjectByName("replaySelection").selected;
     135    Engine.SwitchGuiPage("page_replaymenu.xml", selected > 0 ?
     136    { "replaySelectionData": createReplaySelectionData(g_ReplaysFiltered[selected].directory) } : "");
     137}
     138
    130139/**
    131140 * Callback.
    132141 */
  • binaries/data/mods/public/gui/replaymenu/replay_menu.xml

     
    240240                <action on="Press">deleteReplayButtonPressed();</action>
    241241            </object>
    242242
     243            <!-- Reload Cache Button -->
     244            <object type="button" style="StoneButton" size="40%+25 0 57%+25 100%">
     245                <translatableAttribute id="caption">Reload Cache</translatableAttribute>
     246                <action on="Press">reloadCache();</action>
     247            </object>
     248
    243249            <!-- Summary Button -->
    244250            <object name="summaryButton" type="button" style="StoneButton" size="65%-50 0 82%-50 100%">
    245251                <translatableAttribute id="caption">Summary</translatableAttribute>
  • binaries/data/mods/public/gui/session/session.js

     
    537537
    538538    Engine.EndGame();
    539539
     540    // After game is ended
     541    if (!g_IsReplay)
     542        Engine.AddReplayToCache(replayDirectory);
     543
    540544    if (g_IsController && Engine.HasXmppClient())
    541545        Engine.SendUnregisterGame();
    542546
  • source/ps/scripting/JSInterface_VisualReplay.cpp

     
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 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
     
    5151    return VisualReplay::GetReplayMetadata(pCxPrivate, directoryName);
    5252}
    5353
    54 void JSI_VisualReplay::SaveReplayMetadata(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStrW& data)
     54void JSI_VisualReplay::SaveReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& data)
    5555{
    5656    VisualReplay::SaveReplayMetadata(data);
    5757}
    5858
     59void JSI_VisualReplay::AddReplayToCache(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName)
     60{
     61    VisualReplay::AddReplayToCache(*(pCxPrivate->pScriptInterface), directoryName);
     62}
     63
     64void JSI_VisualReplay::ReloadReplayCache(ScriptInterface::CxPrivate* pCxPrivate)
     65{
     66    VisualReplay::ReloadReplayCache(*(pCxPrivate->pScriptInterface));
     67}
     68
    5969void JSI_VisualReplay::RegisterScriptFunctions(ScriptInterface& scriptInterface)
    6070{
    6171    scriptInterface.RegisterFunction<JS::Value, &GetReplays>("GetReplays");
     
    6575    scriptInterface.RegisterFunction<JS::Value, CStrW, &GetReplayMetadata>("GetReplayMetadata");
    6676    scriptInterface.RegisterFunction<bool, CStrW, &HasReplayMetadata>("HasReplayMetadata");
    6777    scriptInterface.RegisterFunction<void, CStrW, &SaveReplayMetadata>("SaveReplayMetadata");
     78    scriptInterface.RegisterFunction<void, CStrW, &AddReplayToCache>("AddReplayToCache");
     79    scriptInterface.RegisterFunction<void, &ReloadReplayCache>("ReloadReplayCache");
    6880}
  • source/ps/scripting/JSInterface_VisualReplay.h

     
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 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
     
    3030    bool HasReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName);
    3131    JS::Value GetReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName);
    3232    void SaveReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& data);
     33    void AddReplayToCache(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName);
     34    void ReloadReplayCache(ScriptInterface::CxPrivate* pCxPrivate);
    3335    void RegisterScriptFunctions(ScriptInterface& scriptInterface);
    3436}
    3537
  • source/ps/VisualReplay.cpp

     
    6262}
    6363
    6464/**
    65  * Load all replays found in the directory.
    66  *
    67  * Since files are spread across the harddisk,
    68  * loading hundreds of them can consume a lot of time.
     65 * Load the replay cache and check if there are new/deleted ones
     66 * If so, update their data.
    6967 */
    7068JS::Value VisualReplay::GetReplays(ScriptInterface& scriptInterface)
    7169{
     
    7270    TIMER(L"GetReplays");
    7371    JSContext* cx = scriptInterface.GetContext();
    7472    JSAutoRequest rq(cx);
     73   
     74    std::map<CStr, u32> fileList;
    7575
     76    const OsPath cacheFileName = GetDirectoryName() / L"replayCache.json";
     77    JS::RootedObject cachedReplaysObject(cx);
     78
     79    bool cacheFound = FileExists(cacheFileName);
     80    if (cacheFound)
     81    {
     82        // Open cache file
     83        std::istream* cacheStream = new std::ifstream(cacheFileName.string8().c_str());
     84
     85        // Read file into chacheStr
     86        CStr cacheStr((std::istreambuf_iterator<char>(*cacheStream)), std::istreambuf_iterator<char>());
     87
     88        // Create empty JS object and parse the context of the cache into it
     89        JS::RootedValue cachedReplays(cx);
     90        scriptInterface.ParseJSON(cacheStr, &cachedReplays);
     91        SAFE_DELETE(cacheStream);
     92
     93        JS::RootedObject temp(cx, &cachedReplays.toObject());
     94        cachedReplaysObject = temp;
     95
     96        // Create list of files included in the cache
     97        u32 cacheLength = 0;
     98        JS_GetArrayLength(cx, cachedReplaysObject, &cacheLength);
     99        for (u32 j = 0; j < cacheLength ; ++j)
     100        {
     101            JS::RootedValue replay(cx);
     102            JS_GetElement(cx, cachedReplaysObject, j, &replay);
     103
     104            JS::RootedObject replayObject(cx, &replay.toObject());
     105            JS::RootedValue file(cx);
     106            JS_GetProperty(cx, replayObject, "directory", &file);
     107
     108            CStr fileName;
     109            scriptInterface.FromJSVal(cx, file, fileName);
     110            fileList.emplace(fileName, j);
     111        }
     112    }
     113
     114    JS::RootedObject replays(cx, JS_NewArrayObject(cx, 0));
     115    bool newReplays = false;
     116    DirectoryNames directories;
    76117    u32 i = 0;
    77     DirectoryNames directories;
    78     JS::RootedObject replays(cx, JS_NewArrayObject(cx, 0));
    79 
    80118    if (GetDirectoryEntries(GetDirectoryName(), NULL, &directories) == INFO::OK)
     119    {
    81120        for (OsPath& directory : directories)
    82121        {
    83122            if (SDL_QuitRequested())
    84                 return JSVAL_NULL;
     123                break;
    85124
    86             JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, directory));
    87             if (!replayData.isNull())
    88                 JS_SetElement(cx, replays, i++, replayData);
     125            std::map<CStr, u32>::iterator it = fileList.find(directory.string8());
     126           
     127            // directory is not element of fileList
     128            if (it == fileList.end())
     129            {
     130                JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, directory));
     131                if (!replayData.isNull())
     132                {
     133                    JS_SetElement(cx, replays, i++, replayData);
     134                    newReplays = true;
     135                }
     136            }
     137            else
     138                fileList.erase(it);
    89139        }
     140
     141        // No replay was changed, so just return the cache
     142        if (!newReplays && fileList.empty())
     143            return JS::ObjectValue(*cachedReplaysObject);
     144
     145        // Cache found, so copy the replays from the cache that are not deleted
     146        if (cacheFound)
     147        {
     148            std::vector<u32> deleteIndices;
     149            for (const std::pair<CStr, u32>& file : fileList)
     150                deleteIndices.push_back(file.second);
     151
     152            u32 cacheLength = 0;
     153            JS_GetArrayLength(cx, cachedReplaysObject, &cacheLength);
     154
     155            for (u32 j = 0; j < cacheLength; ++j)
     156                // a not in deleteIndices, so copy it into replays
     157                if (std::find(deleteIndices.begin(), deleteIndices.end(), j) == deleteIndices.end())
     158                {
     159                    JS::RootedValue replay(cx);
     160                    JS_GetElement(cx, cachedReplaysObject, j, &replay);
     161                    JS_SetElement(cx, replays, i++, replay);
     162                }
     163        }
     164
     165        JS::RootedValue replaysRooted(cx, JS::ObjectValue(*replays));
     166        std::ofstream stream(cacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc);
     167        stream << scriptInterface.StringifyJSON(&replaysRooted);
     168        stream.close();
     169    }
     170
    90171    return JS::ObjectValue(*replays);
    91172}
    92173
     
    292373    return attribs;
    293374}
    294375
     376
     377void VisualReplay::AddReplayToCache(ScriptInterface& scriptInterface, const CStrW& directoryName)
     378{
     379    JSContext* cx = scriptInterface.GetContext();
     380    JSAutoRequest rq(cx);
     381
     382    JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, OsPath(directoryName)));
     383
     384    if (replayData.isNull())
     385        return;
     386
     387    const OsPath cacheFileName = GetDirectoryName() / L"replayCache.json";
     388    JS::RootedObject cachedReplaysObject(cx, JS_NewArrayObject(cx, 0));
     389
     390    if (FileExists(cacheFileName))
     391    {
     392        // Open cache file
     393        std::istream* cacheStream = new std::ifstream(cacheFileName.string8().c_str());
     394
     395        // Read file into chacheStr
     396        CStr cacheStr((std::istreambuf_iterator<char>(*cacheStream)), std::istreambuf_iterator<char>());
     397
     398        // Create empty JS object and parse the context of the cache into it
     399        JS::RootedValue cachedReplays(cx);
     400        scriptInterface.ParseJSON(cacheStr, &cachedReplays);
     401        SAFE_DELETE(cacheStream);
     402
     403        JS::RootedObject temp(cx, &cachedReplays.toObject());
     404        cachedReplaysObject = temp;
     405    }
     406   
     407    u32 cacheLength = 0;
     408    JS_GetArrayLength(cx, cachedReplaysObject, &cacheLength);
     409    JS_SetElement(cx, cachedReplaysObject, cacheLength, replayData);
     410
     411    JS::RootedValue replaysRooted(cx, JS::ObjectValue(*cachedReplaysObject));
     412    std::ofstream cacheStream(cacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc);
     413    cacheStream << scriptInterface.StringifyJSON(&replaysRooted);
     414    cacheStream.close();
     415}
     416
     417void VisualReplay::ReloadReplayCache(ScriptInterface& scriptInterface)
     418{
     419    JSContext* cx = scriptInterface.GetContext();
     420    JSAutoRequest rq(cx);
     421
     422    JS::RootedObject replays(cx, JS_NewArrayObject(cx, 0));
     423    DirectoryNames directories;
     424    u32 i = 0;
     425
     426    if (GetDirectoryEntries(GetDirectoryName(), NULL, &directories) != INFO::OK)
     427        return;
     428
     429    for (OsPath& directory : directories)
     430    {
     431        if (SDL_QuitRequested())
     432            break;
     433
     434        JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, directory));
     435        if (!replayData.isNull())
     436            JS_SetElement(cx, replays, i++, replayData);
     437    }
     438
     439    const OsPath cacheFileName = GetDirectoryName() / L"replayCache.json";
     440    JS::RootedValue replaysRooted(cx, JS::ObjectValue(*replays));
     441    std::ofstream stream(cacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc);
     442    stream << scriptInterface.StringifyJSON(&replaysRooted);
     443    stream.close();
     444}
     445
    295446// TODO: enhancement: how to save the data if the process is killed? (case SDL_QUIT in main.cpp)
    296447void VisualReplay::SaveReplayMetadata(const CStrW& data)
    297448{
  • source/ps/VisualReplay.h

     
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 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
     
    8383 */
    8484void SaveReplayMetadata(const CStrW& data);
    8585
     86/**
     87* Saves the metadata from the session to metadata.json
     88*/
     89void AddReplayToCache(ScriptInterface& scriptInterface, const CStrW& directoryName);
     90
     91/**
     92 * Reloads the replay cache
     93 */
     94void ReloadReplayCache(ScriptInterface& scriptInterface);
     95
    8696}
    8797
    8898#endif