Ticket #3433: 3433_replay_cache_v1.7.patch
File 3433_replay_cache_v1.7.patch, 15.9 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/gui/replaymenu/replay_actions.js
127 127 }); 128 128 } 129 129 130 function reloadCache() 131 { 132 let selected = Engine.GetGUIObjectByName("replaySelection").selected; 133 loadReplays(selected > 0 ? createReplaySelectionData(g_ReplaysFiltered[selected].directory) : "", true); 134 } 135 130 136 /** 131 137 * Callback. 132 138 */ -
binaries/data/mods/public/gui/replaymenu/replay_menu.js
59 59 return; 60 60 } 61 61 62 loadReplays(data && data.replaySelectionData );62 loadReplays(data && data.replaySelectionData, false); 63 63 64 64 if (!g_Replays) 65 65 { … … 76 76 * Check timestamp and compatibility and extract g_Playernames, g_MapNames, g_VictoryConditions. 77 77 * Restore selected filters and item. 78 78 */ 79 function loadReplays(replaySelectionData )79 function loadReplays(replaySelectionData, reload) 80 80 { 81 g_Replays = Engine.GetReplays( );81 g_Replays = Engine.GetReplays(reload); 82 82 83 83 if (!g_Replays) 84 84 return; -
binaries/data/mods/public/gui/replaymenu/replay_menu.xml
250 250 <translatableAttribute id="caption">Delete</translatableAttribute> 251 251 <action on="Press">deleteReplayButtonPressed();</action> 252 252 </object> 253 254 <!-- Reload Cache Button --> 255 <object type="button" style="StoneButton" size="40%+25 0 57%+25 100%"> 256 <translatableAttribute id="caption">Reload Cache</translatableAttribute> 257 <action on="Press">reloadCache();</action> 258 </object> 253 259 254 260 <!-- Summary Button --> 255 261 <object name="summaryButton" type="button" style="StoneButton" size="65%-50 0 82%-50 100%"> -
binaries/data/mods/public/gui/session/session.js
559 559 560 560 Engine.EndGame(); 561 561 562 // After the replay file was closed in EndGame 563 // Done here to keep EndGame small 564 if (!g_IsReplay) 565 Engine.AddReplayToCache(replayDirectory); 566 562 567 if (g_IsController && Engine.HasXmppClient()) 563 568 Engine.SendUnregisterGame(); 564 569 -
source/ps/scripting/JSInterface_VisualReplay.cpp
33 33 return VisualReplay::DeleteReplay(replayFile); 34 34 } 35 35 36 JS::Value JSI_VisualReplay::GetReplays(ScriptInterface::CxPrivate* pCxPrivate )36 JS::Value JSI_VisualReplay::GetReplays(ScriptInterface::CxPrivate* pCxPrivate, bool reload) 37 37 { 38 return VisualReplay::GetReplays(*(pCxPrivate->pScriptInterface) );38 return VisualReplay::GetReplays(*(pCxPrivate->pScriptInterface), reload); 39 39 } 40 40 41 41 JS::Value JSI_VisualReplay::GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName) … … 53 53 return VisualReplay::GetReplayMetadata(pCxPrivate, directoryName); 54 54 } 55 55 56 void JSI_VisualReplay::AddReplayToCache(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName) 57 { 58 VisualReplay::AddReplayToCache(*(pCxPrivate->pScriptInterface), directoryName); 59 } 60 56 61 void JSI_VisualReplay::RegisterScriptFunctions(ScriptInterface& scriptInterface) 57 62 { 58 scriptInterface.RegisterFunction<JS::Value, &GetReplays>("GetReplays");63 scriptInterface.RegisterFunction<JS::Value, bool, &GetReplays>("GetReplays"); 59 64 scriptInterface.RegisterFunction<bool, CStrW, &DeleteReplay>("DeleteReplay"); 60 65 scriptInterface.RegisterFunction<void, CStrW, &StartVisualReplay>("StartVisualReplay"); 61 66 scriptInterface.RegisterFunction<JS::Value, CStrW, &GetReplayAttributes>("GetReplayAttributes"); 62 67 scriptInterface.RegisterFunction<JS::Value, CStrW, &GetReplayMetadata>("GetReplayMetadata"); 63 68 scriptInterface.RegisterFunction<bool, CStrW, &HasReplayMetadata>("HasReplayMetadata"); 69 scriptInterface.RegisterFunction<void, CStrW, &AddReplayToCache>("AddReplayToCache"); 64 70 } -
source/ps/scripting/JSInterface_VisualReplay.h
25 25 { 26 26 void StartVisualReplay(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directory); 27 27 bool DeleteReplay(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& replayFile); 28 JS::Value GetReplays(ScriptInterface::CxPrivate* pCxPrivate );28 JS::Value GetReplays(ScriptInterface::CxPrivate* pCxPrivate, bool reload); 29 29 JS::Value GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName); 30 30 bool HasReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName); 31 31 JS::Value GetReplayMetadata(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName); 32 void AddReplayToCache(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName); 32 33 void RegisterScriptFunctions(ScriptInterface& scriptInterface); 33 34 } 34 35 -
source/ps/VisualReplay.cpp
62 62 } 63 63 64 64 /** 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. 69 */ 70 JS::Value VisualReplay::GetReplays(ScriptInterface& scriptInterface) 65 * Load the replay cache and check if there are new/deleted ones 66 * If so, update their data. 67 */ 68 JS::HandleObject VisualReplay::ReloadReplayCache(ScriptInterface& scriptInterface, bool compareFileSize) 71 69 { 72 TIMER(L" GetReplays");70 TIMER(L"ReloadReplayCache"); 73 71 JSContext* cx = scriptInterface.GetContext(); 74 72 JSAutoRequest rq(cx); 75 73 76 u32 i = 0; 74 // Maps the filename onto the index and size 75 std::map<CStr, std::pair<u32, u32>> fileList; 76 77 const OsPath cacheFileName = GetDirectoryName() / L"replayCache.json"; 78 const OsPath tempCacheFileName = GetDirectoryName() / L"replayCache_temp.json"; 79 JS::RootedObject cachedReplaysObject(cx); 80 81 if (FileExists(cacheFileName)) 82 { 83 TIMER(L"ReloadReplayCache: openCacheFile"); 84 // Open cache file 85 std::istream* cacheStream = new std::ifstream(cacheFileName.string8().c_str()); 86 87 // Read file into chacheStr 88 CStr cacheStr((std::istreambuf_iterator<char>(*cacheStream)), std::istreambuf_iterator<char>()); 89 90 // Create empty JS object and parse the context of the cache into it 91 JS::RootedValue cachedReplays(cx); 92 scriptInterface.ParseJSON(cacheStr, &cachedReplays); 93 SAFE_DELETE(cacheStream); 94 95 cachedReplaysObject = &cachedReplays.toObject(); 96 97 // Create list of files included in the cache 98 u32 cacheLength = 0; 99 JS_GetArrayLength(cx, cachedReplaysObject, &cacheLength); 100 for (u32 j = 0; j < cacheLength; ++j) 101 { 102 JS::RootedValue replay(cx); 103 JS_GetElement(cx, cachedReplaysObject, j, &replay); 104 105 JS::RootedValue file(cx); 106 CStr fileName; 107 u32 fileSize; 108 scriptInterface.GetProperty(replay, "directory", fileName); 109 scriptInterface.GetProperty(replay, "fileSize", fileSize); 110 111 fileList.emplace(fileName, std::make_pair(j, fileSize)); 112 } 113 } 114 115 JS::RootedObject replays(cx, JS_NewArrayObject(cx, 0)); 77 116 DirectoryNames directories; 78 JS::RootedObject replays(cx, JS_NewArrayObject(cx, 0));79 117 80 if (GetDirectoryEntries(GetDirectoryName(), NULL, &directories) == INFO::OK) 81 for (OsPath& directory : directories) 118 if (GetDirectoryEntries(GetDirectoryName(), NULL, &directories) != INFO::OK) 119 return replays; 120 121 bool newReplays = false; 122 std::vector<u32> copyFromOldCache; 123 // Specifies where the next replay data should go in replays 124 u32 i = 0; 125 { 126 TIMER(L"ReloadReplayCache: Load data"); 127 for (const OsPath& directory : directories) 82 128 { 83 129 if (SDL_QuitRequested()) 84 return JSVAL_NULL;130 break; 85 131 86 JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, directory)); 87 if (!replayData.isNull()) 132 bool isNew = true; 133 std::map<CStr, std::pair<u32, u32>>::iterator it = fileList.find(directory.string8()); 134 // directory is in fileList 135 if (it != fileList.end()) 136 { 137 if (compareFileSize) 138 { 139 CFileInfo fileInfo; 140 GetFileInfo(GetDirectoryName() / directory / L"commands.txt", &fileInfo); 141 if ((u32)fileInfo.Size() == it->second.second) 142 isNew = false; 143 } 144 else 145 isNew = false; 146 } 147 148 if (isNew) 149 { 150 JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, directory)); 151 if (replayData.isNull()) 152 { 153 CFileInfo fileInfo; 154 GetFileInfo(GetDirectoryName() / directory / L"commands.txt", &fileInfo); 155 scriptInterface.Eval("({})", &replayData); 156 scriptInterface.SetProperty(replayData, "directory", directory); 157 scriptInterface.SetProperty(replayData, "fileSize", (u32)fileInfo.Size()); 158 } 88 159 JS_SetElement(cx, replays, i++, replayData); 160 newReplays = true; 161 } 162 else 163 copyFromOldCache.push_back(it->second.first); 89 164 } 90 return JS::ObjectValue(*replays); 165 } 166 167 debug_printf("old not deleted replays: %d \n", copyFromOldCache.size()); 168 debug_printf("old deleted replays:: %d \n", fileList.size() - copyFromOldCache.size()); 169 debug_printf("new replays: %d \n", i); 170 171 if (!newReplays && fileList.empty()) 172 return replays; 173 // No replay was changed, so just return the cache 174 if (!newReplays && fileList.size() == copyFromOldCache.size()) 175 return cachedReplaysObject; 176 177 { 178 TIMER(L"ReloadReplayCache: copy from old cache"); 179 // Copy the replays from the old cache that are not deleted 180 if (!copyFromOldCache.empty()) 181 for (u32 j : copyFromOldCache) 182 { 183 JS::RootedValue replay(cx); 184 JS_GetElement(cx, cachedReplaysObject, j, &replay); 185 JS_SetElement(cx, replays, i++, replay); 186 } 187 } 188 189 { 190 TIMER(L"ReloadReplayCache: save new cache"); 191 JS::RootedValue replaysRooted(cx, JS::ObjectValue(*replays)); 192 std::ofstream stream(tempCacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc); 193 stream << scriptInterface.StringifyJSON(&replaysRooted); 194 stream.close(); 195 196 wunlink(cacheFileName); 197 if (wrename(tempCacheFileName, cacheFileName)) 198 LOGERROR("Could not store the replay cache"); 199 } 200 return replays; 91 201 } 92 202 203 JS::Value VisualReplay::GetReplays(ScriptInterface& scriptInterface, bool reload) 204 { 205 TIMER(L"GetReplays"); 206 JSContext* cx = scriptInterface.GetContext(); 207 JSAutoRequest rq(cx); 208 // Acquire replays 209 JS::RootedObject replays(cx, ReloadReplayCache(scriptInterface, reload)); 210 // Remove entries without data 211 JS::RootedObject replaysWithoutNullEntries(cx, JS_NewArrayObject(cx, 0)); 212 u32 replaysLength = 0; 213 u32 i = 0; 214 JS_GetArrayLength(cx, replays, &replaysLength); 215 for (u32 j = 0; j < replaysLength; ++j) 216 { 217 JS::RootedValue replay(cx); 218 JS_GetElement(cx, replays, j, &replay); 219 if (scriptInterface.HasProperty(replay, "attribs")) 220 JS_SetElement(cx, replaysWithoutNullEntries, i++, replay); 221 } 222 return JS::ObjectValue(*replaysWithoutNullEntries); 223 } 224 93 225 /** 94 226 * Move the cursor backwards until a newline was read or the beginning of the file was found. 95 227 * Either way the cursor points to the beginning of a newline. … … 173 305 return -1; 174 306 } 175 307 176 JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, OsPath& directory)308 JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, const OsPath& directory) 177 309 { 310 TIMER(L"LoadReplayData"); 178 311 // The directory argument must not be constant, otherwise concatenating will fail 179 312 const OsPath replayFile = GetDirectoryName() / directory / L"commands.txt"; 180 313 … … 251 384 scriptInterface.Eval("({})", &replayData); 252 385 scriptInterface.SetProperty(replayData, "file", replayFile); 253 386 scriptInterface.SetProperty(replayData, "directory", directory); 387 scriptInterface.SetProperty(replayData, "fileSize", (u32)fileSize); 254 388 scriptInterface.SetProperty(replayData, "filemod_timestamp", std::to_string(fileTime)); 255 389 scriptInterface.SetProperty(replayData, "attribs", attribs); 256 390 scriptInterface.SetProperty(replayData, "duration", duration); … … 266 400 return DirectoryExists(directory) && DeleteDirectory(directory) == INFO::OK; 267 401 } 268 402 269 270 403 JS::Value VisualReplay::GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName) 271 404 { 272 405 // Create empty JS object … … 292 425 return attribs; 293 426 } 294 427 428 void VisualReplay::AddReplayToCache(ScriptInterface& scriptInterface, const CStrW& directoryName) 429 { 430 TIMER(L"AddReplayToCache"); 431 JSContext* cx = scriptInterface.GetContext(); 432 JSAutoRequest rq(cx); 433 434 JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, OsPath(directoryName))); 435 436 if (replayData.isNull()) 437 return; 438 439 const OsPath cacheFileName = GetDirectoryName() / L"replayCache.json"; 440 JS::RootedObject cachedReplaysObject(cx, JS_NewArrayObject(cx, 0)); 441 442 if (FileExists(cacheFileName)) 443 { 444 // Open cache file 445 std::istream* cacheStream = new std::ifstream(cacheFileName.string8().c_str()); 446 447 // Read file into chacheStr 448 CStr cacheStr((std::istreambuf_iterator<char>(*cacheStream)), std::istreambuf_iterator<char>()); 449 450 // Create empty JS object and parse the content of the cache into it 451 JS::RootedValue cachedReplays(cx); 452 scriptInterface.ParseJSON(cacheStr, &cachedReplays); 453 SAFE_DELETE(cacheStream); 454 455 cachedReplaysObject = &cachedReplays.toObject(); 456 } 457 458 u32 cacheLength = 0; 459 JS_GetArrayLength(cx, cachedReplaysObject, &cacheLength); 460 JS_SetElement(cx, cachedReplaysObject, cacheLength, replayData); 461 462 JS::RootedValue replaysRooted(cx, JS::ObjectValue(*cachedReplaysObject)); 463 std::ofstream cacheStream(cacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc); 464 cacheStream << scriptInterface.StringifyJSON(&replaysRooted); 465 cacheStream.close(); 466 } 467 295 468 void VisualReplay::SaveReplayMetadata(ScriptInterface* scriptInterface) 296 469 { 297 470 JSContext* cx = scriptInterface->GetContext(); -
source/ps/VisualReplay.h
42 42 void StartVisualReplay(const CStrW& directory); 43 43 44 44 /** 45 * Reloads the replay cache 46 * 47 * @param scriptInterface the ScriptInterface in which to create the return data. 48 * @param compareFileSize compare the directory name and the FileSize of the replays and the cache 49 * @return cache entries 50 */ 51 JS::HandleObject ReloadReplayCache(ScriptInterface& scriptInterface, bool compareFileSize = false); 52 53 /** 45 54 * Get a list of replays to display in the GUI. 46 55 * 47 56 * @param scriptInterface the ScriptInterface in which to create the return data. 48 57 * @return array of objects containing replay data 49 58 */ 50 JS::Value GetReplays(ScriptInterface& scriptInterface );59 JS::Value GetReplays(ScriptInterface& scriptInterface, bool reload); 51 60 52 61 /** 53 62 * Parses a commands.txt file and extracts metadata. 54 63 * Works similarly to CGame::LoadReplayData(). 55 64 */ 56 JS::Value LoadReplayData(ScriptInterface& scriptInterface, OsPath& directory);65 JS::Value LoadReplayData(ScriptInterface& scriptInterface, const OsPath& directory); 57 66 58 67 /** 59 68 * Permanently deletes the visual replay (including the parent directory) … … 83 92 */ 84 93 void SaveReplayMetadata(ScriptInterface* scriptInterface); 85 94 95 /** 96 * Adds a replay to the replayCache 97 */ 98 void AddReplayToCache(ScriptInterface& scriptInterface, const CStrW& directoryName); 86 99 } 87 100 88 101 #endif