Ticket #3433: 3433_replay_cache_v1.6.patch
File 3433_replay_cache_v1.6.patch, 10.0 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/gui/session/session.js
529 529 530 530 Engine.EndGame(); 531 531 532 // After the replay file was closed in EndGame 533 // Done here to keep EndGame small 534 if (!g_IsReplay) 535 Engine.AddReplayToCache(replayDirectory); 536 532 537 if (g_IsController && Engine.HasXmppClient()) 533 538 Engine.SendUnregisterGame(); 534 539 -
source/ps/scripting/JSInterface_VisualReplay.cpp
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 63 scriptInterface.RegisterFunction<JS::Value, &GetReplays>("GetReplays"); … … 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
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. 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 index and size 75 std::map<CStr, std::pair<u32, u64>> 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 // Open cache file 84 std::istream* cacheStream = new std::ifstream(cacheFileName.string8().c_str()); 85 86 // Read file into chacheStr 87 CStr cacheStr((std::istreambuf_iterator<char>(*cacheStream)), std::istreambuf_iterator<char>()); 88 89 // Create empty JS object and parse the context of the cache into it 90 JS::RootedValue cachedReplays(cx); 91 scriptInterface.ParseJSON(cacheStr, &cachedReplays); 92 SAFE_DELETE(cacheStream); 93 94 cachedReplaysObject = &cachedReplays.toObject(); 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::RootedValue file(cx); 105 CStr fileName; 106 u32 fileSize; 107 scriptInterface.GetProperty(replay, "directory", fileName); 108 scriptInterface.GetProperty(replay, "fileSize", fileSize); 109 110 fileList.emplace(fileName, std::make_pair(j, fileSize)); 111 } 112 } 113 114 JS::RootedObject replays(cx, JS_NewArrayObject(cx, 0)); 77 115 DirectoryNames directories; 78 JS::RootedObject replays(cx, JS_NewArrayObject(cx, 0));79 116 80 if (GetDirectoryEntries(GetDirectoryName(), NULL, &directories) == INFO::OK) 81 for (OsPath& directory : directories) 117 if (GetDirectoryEntries(GetDirectoryName(), NULL, &directories) != INFO::OK) 118 return JS::ObjectValue(*replays); 119 120 bool newReplays = false; 121 std::vector<u32> copyFromOldCache; 122 // Specifies where the next replay data should go in replays 123 u32 i = 0; 124 125 for (const OsPath& directory : directories) 126 { 127 if (SDL_QuitRequested()) 128 break; 129 130 bool isNew = true; 131 std::map<CStr, std::pair<u32, u64>>::iterator it = fileList.find(directory.string8()); 132 // directory is in fileList 133 if (it != fileList.end()) 82 134 { 83 if (SDL_QuitRequested()) 84 return JSVAL_NULL; 135 CFileInfo fileInfo; 136 GetFileInfo(GetDirectoryName() / directory / L"commands.txt", &fileInfo); 137 if (fileInfo.Size() == it->second.second) 138 isNew = false; 139 } 85 140 141 if (isNew) 142 { 86 143 JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, directory)); 87 144 if (!replayData.isNull()) 145 { 88 146 JS_SetElement(cx, replays, i++, replayData); 147 newReplays = true; 148 } 89 149 } 150 else 151 copyFromOldCache.push_back(it->second.first); 152 } 153 154 if (!newReplays && fileList.empty()) 155 return JS::ObjectValue(*replays); 156 // No replay was changed, so just return the cache 157 if (!newReplays && fileList.size() == copyFromOldCache.size()) 158 return JS::ObjectValue(*cachedReplaysObject); 159 160 // Copy the replays from the old cache that are not deleted 161 if (!copyFromOldCache.empty()) 162 for (u32 j : copyFromOldCache) 163 { 164 JS::RootedValue replay(cx); 165 JS_GetElement(cx, cachedReplaysObject, j, &replay); 166 JS_SetElement(cx, replays, i++, replay); 167 } 168 169 JS::RootedValue replaysRooted(cx, JS::ObjectValue(*replays)); 170 std::ofstream stream(tempCacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc); 171 stream << scriptInterface.StringifyJSON(&replaysRooted); 172 stream.close(); 173 174 wunlink(cacheFileName); 175 if (wrename(tempCacheFileName, cacheFileName)) 176 LOGERROR("Could not store the replay cache"); 177 90 178 return JS::ObjectValue(*replays); 91 179 } 92 180 … … 173 261 return -1; 174 262 } 175 263 176 JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, OsPath& directory)264 JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, const OsPath& directory) 177 265 { 178 266 // The directory argument must not be constant, otherwise concatenating will fail 179 267 const OsPath replayFile = GetDirectoryName() / directory / L"commands.txt"; … … 251 339 scriptInterface.Eval("({})", &replayData); 252 340 scriptInterface.SetProperty(replayData, "file", replayFile); 253 341 scriptInterface.SetProperty(replayData, "directory", directory); 342 scriptInterface.SetProperty(replayData, "fileSize", (u32)fileSize); 254 343 scriptInterface.SetProperty(replayData, "filemod_timestamp", std::to_string(fileTime)); 255 344 scriptInterface.SetProperty(replayData, "attribs", attribs); 256 345 scriptInterface.SetProperty(replayData, "duration", duration); … … 266 355 return DirectoryExists(directory) && DeleteDirectory(directory) == INFO::OK; 267 356 } 268 357 269 270 358 JS::Value VisualReplay::GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName) 271 359 { 272 360 // Create empty JS object … … 292 380 return attribs; 293 381 } 294 382 383 void VisualReplay::AddReplayToCache(ScriptInterface& scriptInterface, const CStrW& directoryName) 384 { 385 TIMER(L"AddReplayToCache"); 386 JSContext* cx = scriptInterface.GetContext(); 387 JSAutoRequest rq(cx); 388 389 JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, OsPath(directoryName))); 390 391 if (replayData.isNull()) 392 return; 393 394 const OsPath cacheFileName = GetDirectoryName() / L"replayCache.json"; 395 JS::RootedObject cachedReplaysObject(cx, JS_NewArrayObject(cx, 0)); 396 397 if (FileExists(cacheFileName)) 398 { 399 // Open cache file 400 std::istream* cacheStream = new std::ifstream(cacheFileName.string8().c_str()); 401 402 // Read file into chacheStr 403 CStr cacheStr((std::istreambuf_iterator<char>(*cacheStream)), std::istreambuf_iterator<char>()); 404 405 // Create empty JS object and parse the content of the cache into it 406 JS::RootedValue cachedReplays(cx); 407 scriptInterface.ParseJSON(cacheStr, &cachedReplays); 408 SAFE_DELETE(cacheStream); 409 410 cachedReplaysObject = &cachedReplays.toObject(); 411 } 412 413 u32 cacheLength = 0; 414 JS_GetArrayLength(cx, cachedReplaysObject, &cacheLength); 415 JS_SetElement(cx, cachedReplaysObject, cacheLength, replayData); 416 417 JS::RootedValue replaysRooted(cx, JS::ObjectValue(*cachedReplaysObject)); 418 std::ofstream cacheStream(cacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc); 419 cacheStream << scriptInterface.StringifyJSON(&replaysRooted); 420 cacheStream.close(); 421 } 422 295 423 void VisualReplay::SaveReplayMetadata(ScriptInterface* scriptInterface) 296 424 { 297 425 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 * Adds a replay to the replayCache 88 */ 89 void AddReplayToCache(ScriptInterface& scriptInterface, const CStrW& directoryName); 86 90 } 87 91 88 92 #endif