Ticket #3433: 3433_replay_cache_v1.6_vector.patch
File 3433_replay_cache_v1.6_vector.patch, 10.2 KB (added by , 8 years ago) |
---|
-
binaries/data/mods/public/gui/session/session.js
537 537 538 538 Engine.EndGame(); 539 539 540 // After the replay file was closed in EndGame 541 // Done here to keep EndGame small 542 if (!g_IsReplay) 543 Engine.AddReplayToCache(replayDirectory); 544 540 545 if (g_IsController && Engine.HasXmppClient()) 541 546 Engine.SendUnregisterGame(); 542 547 -
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::vector<std::pair<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.push_back(std::make_pair(fileName, std::make_pair(j, fileSize))); 111 } 112 std::sort(fileList.begin(), fileList.end()); 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 JS::ObjectValue(*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 for (const OsPath& directory : directories) 127 { 128 if (SDL_QuitRequested()) 129 break; 130 131 bool isNew = true; 132 std::pair<CStr, std::pair<u32, u64>> searchVal = std::make_pair(directory.string8(), std::make_pair(0, 0)); 133 std::vector<std::pair<CStr, std::pair<u32, u64>>>::iterator it = lower_bound(fileList.begin(), fileList.end(), searchVal); 134 // directory is in fileList 135 if (it != fileList.end() && it->first == directory.string8()) 82 136 { 83 if (SDL_QuitRequested()) 84 return JSVAL_NULL; 137 CFileInfo fileInfo; 138 GetFileInfo(GetDirectoryName() / directory / L"commands.txt", &fileInfo); 139 if (fileInfo.Size() == it->second.second) 140 isNew = false; 141 } 85 142 143 if (isNew) 144 { 86 145 JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, directory)); 87 146 if (!replayData.isNull()) 147 { 88 148 JS_SetElement(cx, replays, i++, replayData); 149 newReplays = true; 150 } 89 151 } 152 else 153 copyFromOldCache.push_back(it->second.first); 154 } 155 156 if (!newReplays && fileList.empty()) 157 return JS::ObjectValue(*replays); 158 // No replay was changed, so just return the cache 159 if (!newReplays && fileList.size() == copyFromOldCache.size()) 160 return JS::ObjectValue(*cachedReplaysObject); 161 162 // Copy the replays from the old cache that are not deleted 163 if (!copyFromOldCache.empty()) 164 for (u32 j : copyFromOldCache) 165 { 166 JS::RootedValue replay(cx); 167 JS_GetElement(cx, cachedReplaysObject, j, &replay); 168 JS_SetElement(cx, replays, i++, replay); 169 } 170 171 JS::RootedValue replaysRooted(cx, JS::ObjectValue(*replays)); 172 std::ofstream stream(tempCacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc); 173 stream << scriptInterface.StringifyJSON(&replaysRooted); 174 stream.close(); 175 176 wunlink(cacheFileName); 177 if (wrename(tempCacheFileName, cacheFileName)) 178 LOGERROR("Could not store the replay cache"); 179 90 180 return JS::ObjectValue(*replays); 91 181 } 92 182 … … 173 263 return -1; 174 264 } 175 265 176 JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, OsPath& directory)266 JS::Value VisualReplay::LoadReplayData(ScriptInterface& scriptInterface, const OsPath& directory) 177 267 { 178 268 // The directory argument must not be constant, otherwise concatenating will fail 179 269 const OsPath replayFile = GetDirectoryName() / directory / L"commands.txt"; … … 251 341 scriptInterface.Eval("({})", &replayData); 252 342 scriptInterface.SetProperty(replayData, "file", replayFile); 253 343 scriptInterface.SetProperty(replayData, "directory", directory); 344 scriptInterface.SetProperty(replayData, "fileSize", (u32)fileSize); 254 345 scriptInterface.SetProperty(replayData, "filemod_timestamp", std::to_string(fileTime)); 255 346 scriptInterface.SetProperty(replayData, "attribs", attribs); 256 347 scriptInterface.SetProperty(replayData, "duration", duration); … … 266 357 return DirectoryExists(directory) && DeleteDirectory(directory) == INFO::OK; 267 358 } 268 359 269 270 360 JS::Value VisualReplay::GetReplayAttributes(ScriptInterface::CxPrivate* pCxPrivate, const CStrW& directoryName) 271 361 { 272 362 // Create empty JS object … … 292 382 return attribs; 293 383 } 294 384 385 void VisualReplay::AddReplayToCache(ScriptInterface& scriptInterface, const CStrW& directoryName) 386 { 387 TIMER(L"AddReplayToCache"); 388 JSContext* cx = scriptInterface.GetContext(); 389 JSAutoRequest rq(cx); 390 391 JS::RootedValue replayData(cx, LoadReplayData(scriptInterface, OsPath(directoryName))); 392 393 if (replayData.isNull()) 394 return; 395 396 const OsPath cacheFileName = GetDirectoryName() / L"replayCache.json"; 397 JS::RootedObject cachedReplaysObject(cx, JS_NewArrayObject(cx, 0)); 398 399 if (FileExists(cacheFileName)) 400 { 401 // Open cache file 402 std::istream* cacheStream = new std::ifstream(cacheFileName.string8().c_str()); 403 404 // Read file into chacheStr 405 CStr cacheStr((std::istreambuf_iterator<char>(*cacheStream)), std::istreambuf_iterator<char>()); 406 407 // Create empty JS object and parse the content of the cache into it 408 JS::RootedValue cachedReplays(cx); 409 scriptInterface.ParseJSON(cacheStr, &cachedReplays); 410 SAFE_DELETE(cacheStream); 411 412 cachedReplaysObject = &cachedReplays.toObject(); 413 } 414 415 u32 cacheLength = 0; 416 JS_GetArrayLength(cx, cachedReplaysObject, &cacheLength); 417 JS_SetElement(cx, cachedReplaysObject, cacheLength, replayData); 418 419 JS::RootedValue replaysRooted(cx, JS::ObjectValue(*cachedReplaysObject)); 420 std::ofstream cacheStream(cacheFileName.string8().c_str(), std::ofstream::out | std::ofstream::trunc); 421 cacheStream << scriptInterface.StringifyJSON(&replaysRooted); 422 cacheStream.close(); 423 } 424 295 425 void VisualReplay::SaveReplayMetadata(ScriptInterface* scriptInterface) 296 426 { 297 427 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