Ticket #3433: 3433_replay_cache_v1.6_wip.patch
File 3433_replay_cache_v1.6_wip.patch, 13.0 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 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 130 139 /** 131 140 * Callback. 132 141 */ -
binaries/data/mods/public/gui/replaymenu/replay_menu.xml
240 240 <action on="Press">deleteReplayButtonPressed();</action> 241 241 </object> 242 242 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 243 249 <!-- Summary Button --> 244 250 <object name="summaryButton" type="button" style="StoneButton" size="65%-50 0 82%-50 100%"> 245 251 <translatableAttribute id="caption">Summary</translatableAttribute> -
binaries/data/mods/public/gui/session/session.js
526 526 527 527 Engine.EndGame(); 528 528 529 // After game is ended 530 if (!g_IsReplay) 531 Engine.AddReplayToCache(replayDirectory); 532 529 533 if (g_IsController && Engine.HasXmppClient()) 530 534 Engine.SendUnregisterGame(); 531 535 -
source/ps/scripting/JSInterface_VisualReplay.cpp
52 52 { 53 53 return VisualReplay::GetReplayMetadata(pCxPrivate, directoryName); 54 54 } 55 56 void JSI_VisualReplay::AddReplayToCache(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName) 57 { 58 VisualReplay::AddReplayToCache(*(pCxPrivate->pScriptInterface), directoryName); 59 } 55 60 61 /*void JSI_VisualReplay::ReloadReplayCache(ScriptInterface::CxPrivate* pCxPrivate) 62 { 63 VisualReplay::ReloadReplayCache(*(pCxPrivate->pScriptInterface)); 64 }*/ 65 56 66 void JSI_VisualReplay::RegisterScriptFunctions(ScriptInterface& scriptInterface) 57 67 { 58 68 scriptInterface.RegisterFunction<JS::Value, &GetReplays>("GetReplays"); … … 61 71 scriptInterface.RegisterFunction<JS::Value, CStrW, &GetReplayAttributes>("GetReplayAttributes"); 62 72 scriptInterface.RegisterFunction<JS::Value, CStrW, &GetReplayMetadata>("GetReplayMetadata"); 63 73 scriptInterface.RegisterFunction<bool, CStrW, &HasReplayMetadata>("HasReplayMetadata"); 74 scriptInterface.RegisterFunction<void, CStrW, &AddReplayToCache>("AddReplayToCache"); 75 //scriptInterface.RegisterFunction<void, &ReloadReplayCache>("ReloadReplayCache"); 64 76 } -
source/ps/scripting/JSInterface_VisualReplay.h
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); 33 //void ReloadReplayCache(ScriptInterface::CxPrivate* pCxPrivate); 32 34 void RegisterScriptFunctions(ScriptInterface& scriptInterface); 33 35 } 34 36 -
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. 65 * Load the replay cache and check if there are new/deleted ones 66 * If so, update their data. 69 67 */ 70 68 JS::Value VisualReplay::GetReplays(ScriptInterface& scriptInterface) 71 69 { … … 73 71 JSContext* cx = scriptInterface.GetContext(); 74 72 JSAutoRequest rq(cx); 75 73 76 u32 i = 0; 74 // Maps the filename onto the replay data 75 std::map<CStr, JS::Heap<JS::Value>> cachedReplaysMap; 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 bool cacheFound = FileExists(cacheFileName); 82 // Load cache and create fileList 83 if (cacheFound) 84 { 85 // Open cache file 86 std::istream* cacheStream = new std::ifstream(cacheFileName.string8().c_str()); 87 88 // Read file into chacheStr 89 CStr cacheStr((std::istreambuf_iterator<char>(*cacheStream)), std::istreambuf_iterator<char>()); 90 SAFE_DELETE(cacheStream); 91 92 // Create empty JS object and parse the context of the cache into it 93 JS::RootedValue cachedReplays(cx); 94 scriptInterface.ParseJSON(cacheStr, &cachedReplays); 95 96 cachedReplaysObject = &cachedReplays.toObject(); 97 98 // Create list of files included in the cache 99 u32 cacheLength = 0; 100 JS_GetArrayLength(cx, cachedReplaysObject, &cacheLength); 101 for (u32 j = 0; j < cacheLength ; ++j) 102 { 103 JS::RootedValue replay(cx); 104 JS_GetElement(cx, cachedReplaysObject, j, &replay); 105 106 JS::RootedValue directoryJS(cx); 107 CStr directory; 108 scriptInterface.GetProperty(replay, "directory", &directoryJS); 109 scriptInterface.FromJSVal(cx, directoryJS, directory); 110 cachedReplaysMap.emplace(directory, JS::Heap<JS::Value>(replay.get())); 111 } 112 } 113 114 JS::RootedObject replays(cx, JS_NewArrayObject(cx, 0)); 115 bool newReplays = false; 116 std::vector<JS::Value> copyFromOldCache; 77 117 DirectoryNames directories; 78 JS::RootedObject replays(cx, JS_NewArrayObject(cx, 0));79 118 80 if (GetDirectoryEntries(GetDirectoryName(), NULL, &directories) == INFO::OK) 81 for (OsPath& directory : directories) 119 // Specifies where the next replay data should go in replays 120 u32 i = 0; 121 if (GetDirectoryEntries(GetDirectoryName(), NULL, &directories) != INFO::OK) 122 return JS::ObjectValue(*replays); 123 124 for (OsPath& directory : directories) 125 { 126 if (SDL_QuitRequested()) 127 break; 128 129 std::map<CStr, JS::Heap<JS::Value>>::iterator it = cachedReplaysMap.find(directory.string8()); 130 bool isNew = true; 131 132 // There is already a replay with this directory name, so check if the size of the file matches too 133 if (it != cachedReplaysMap.end()) 82 134 { 83 if (SDL_QuitRequested()) 84 return JSVAL_NULL; 135 JS::RootedValue fileSizeJS(cx); 136 long fileSize; 137 JS::RootedValue replay(cx, it->second); 138 scriptInterface.GetProperty(replay, "fileSize", &fileSizeJS); 139 scriptInterface.FromJSVal(cx, fileSizeJS, fileSize); 85 140 141 CFileInfo fileInfo; 142 GetFileInfo(GetDirectoryName() / directory / L"commands.txt", &fileInfo); 143 144 if (fileInfo.Size() == fileSize) 145 isNew = false; 146 } 147 148 if (isNew) 149 { 86 150 JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, directory)); 87 151 if (!replayData.isNull()) 152 { 88 153 JS_SetElement(cx, replays, i++, replayData); 154 newReplays = true; 155 } 89 156 } 157 else 158 copyFromOldCache.push_back(it->second); 159 } 160 161 // No replay was changed, so just return the cache 162 if (!newReplays && cachedReplaysMap.size() == cachedReplaysMap.size()) 163 return JS::ObjectValue(*cachedReplaysObject); 164 165 // Cache found, so copy the replays from the cache that are not deleted 166 if (!copyFromOldCache.empty()) 167 for (JS::Value replay : copyFromOldCache) 168 { 169 JS::RootedValue replayRooted(cx, replay); 170 JS_SetElement(cx, replays, i++, replayRooted); 171 } 172 173 JS::RootedValue replaysRooted(cx, JS::ObjectValue(*replays)); 174 std::ofstream stream(tempCacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc); 175 stream << scriptInterface.StringifyJSON(&replaysRooted); 176 stream.close(); 177 178 wunlink(cacheFileName); 179 if (wrename(tempCacheFileName, cacheFileName)) 180 LOGERROR("Could not store the replay cache"); 181 90 182 return JS::ObjectValue(*replays); 91 183 } 92 184 … … 173 265 return -1; 174 266 } 175 267 176 JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, OsPath& directory)268 JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, const OsPath& directory) 177 269 { 178 270 // The directory argument must not be constant, otherwise concatenating will fail 179 271 const OsPath replayFile = GetDirectoryName() / directory / L"commands.txt"; … … 251 343 scriptInterface.Eval("({})", &replayData); 252 344 scriptInterface.SetProperty(replayData, "file", replayFile); 253 345 scriptInterface.SetProperty(replayData, "directory", directory); 346 scriptInterface.SetProperty(replayData, "fileSize", (u32)fileSize); 254 347 scriptInterface.SetProperty(replayData, "filemod_timestamp", std::to_string(fileTime)); 255 348 scriptInterface.SetProperty(replayData, "attribs", attribs); 256 349 scriptInterface.SetProperty(replayData, "duration", duration); … … 266 359 return DirectoryExists(directory) && DeleteDirectory(directory) == INFO::OK; 267 360 } 268 361 269 270 362 JS::Value VisualReplay::GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName) 271 363 { 272 364 // Create empty JS object … … 292 384 return attribs; 293 385 } 294 386 387 void VisualReplay::AddReplayToCache(ScriptInterface& scriptInterface, const CStrW& directoryName) 388 { 389 TIMER(L"AddReplayToCache"); 390 JSContext* cx = scriptInterface.GetContext(); 391 JSAutoRequest rq(cx); 392 393 JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, OsPath(directoryName))); 394 395 if (replayData.isNull()) 396 return; 397 398 const OsPath cacheFileName = GetDirectoryName() / L"replayCache.json"; 399 JS::RootedObject cachedReplaysObject(cx, JS_NewArrayObject(cx, 0)); 400 401 if (FileExists(cacheFileName)) 402 { 403 // Open cache file 404 std::istream* cacheStream = new std::ifstream(cacheFileName.string8().c_str()); 405 406 // Read file into chacheStr 407 CStr cacheStr((std::istreambuf_iterator<char>(*cacheStream)), std::istreambuf_iterator<char>()); 408 409 // Create empty JS object and parse the context of the cache into it 410 JS::RootedValue cachedReplays(cx); 411 scriptInterface.ParseJSON(cacheStr, &cachedReplays); 412 SAFE_DELETE(cacheStream); 413 414 JS::RootedObject temp(cx, &cachedReplays.toObject()); 415 cachedReplaysObject = temp; 416 } 417 418 u32 cacheLength = 0; 419 JS_GetArrayLength(cx, cachedReplaysObject, &cacheLength); 420 JS_SetElement(cx, cachedReplaysObject, cacheLength, replayData); 421 422 JS::RootedValue replaysRooted(cx, JS::ObjectValue(*cachedReplaysObject)); 423 std::ofstream cacheStream(cacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc); 424 cacheStream << scriptInterface.StringifyJSON(&replaysRooted); 425 cacheStream.close(); 426 } 427 428 /*void VisualReplay::ReloadReplayCache(ScriptInterface& scriptInterface) 429 { 430 JSContext* cx = scriptInterface.GetContext(); 431 JSAutoRequest rq(cx); 432 433 JS::RootedObject replays(cx, JS_NewArrayObject(cx, 0)); 434 DirectoryNames directories; 435 u32 i = 0; 436 437 if (GetDirectoryEntries(GetDirectoryName(), NULL, &directories) != INFO::OK) 438 return; 439 440 for (OsPath& directory : directories) 441 { 442 if (SDL_QuitRequested()) 443 break; 444 445 JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, directory)); 446 if (!replayData.isNull()) 447 JS_SetElement(cx, replays, i++, replayData); 448 } 449 450 const OsPath cacheFileName = GetDirectoryName() / L"replayCache.json"; 451 JS::RootedValue replaysRooted(cx, JS::ObjectValue(*replays)); 452 std::ofstream stream(cacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc); 453 stream << scriptInterface.StringifyJSON(&replaysRooted); 454 stream.close(); 455 }*/ 456 295 457 void VisualReplay::SaveReplayMetadata(ScriptInterface* scriptInterface) 296 458 { 297 459 JSContext* cx = scriptInterface->GetContext(); -
source/ps/VisualReplay.h
53 53 * Parses a commands.txt file and extracts metadata. 54 54 * Works similarly to CGame::LoadReplayData(). 55 55 */ 56 JS::Value LoadReplayData(ScriptInterface& scriptInterface, OsPath& directory);56 JS::Value LoadReplayData(ScriptInterface& scriptInterface, const OsPath& directory); 57 57 58 58 /** 59 59 * Permanently deletes the visual replay (including the parent directory) … … 83 83 */ 84 84 void SaveReplayMetadata(ScriptInterface* scriptInterface); 85 85 86 /** 87 * Saves the metadata from the session to metadata.json 88 */ 89 void AddReplayToCache(ScriptInterface& scriptInterface, const CStrW& directoryName); 90 91 /** 92 * Reloads the replay cache 93 * 94 void ReloadReplayCache(ScriptInterface& scriptInterface);*/ 95 86 96 } 87 97 88 98 #endif