void loom_asset_pump() { // Currently we only want to do this on the main thread so piggy back on the // native delegate sanity check to bail if on secondary thread. if(platform_getCurrentThreadId() != LS::NativeDelegate::smMainThreadID && LS::NativeDelegate::smMainThreadID != 0xBAADF00D) return; loom_mutex_lock(gAssetLock); // Talk to the asset server. loom_asset_serviceServer(); // For now just blast all the data from each file into the asset. while(gAssetLoadQueue.size()) { loom_asset_t *asset = gAssetLoadQueue.front(); // Figure out the type from the path. utString path = asset->name; int type = loom_asset_recognizeAssetTypeFromPath(path); if(type == 0) { lmLog(gAssetLogGroup, "Could not infer type of resource '%s', skipping it...", path.c_str()); asset->state = loom_asset_t::Unloaded; gAssetLoadQueue.erase((UTsize)0, true); continue; } // Open the file. void *ptr; long size; if(!platform_mapFile(asset->name.c_str(), &ptr, &size)) { lmAssert(false, "Could not open file '%s'.", asset->name.c_str()); } // Deserialize it. LoomAssetCleanupCallback dtor = NULL; void *assetBits = loom_asset_deserializeAsset(path, type, size, ptr, &dtor); // Close the file. platform_unmapFile(ptr); // Instate the asset. asset->instate(type, assetBits, dtor); // Done! Update queue. gAssetLoadQueue.erase((UTsize)0, true); } loom_mutex_unlock(gAssetLock); }
static bool readFontFile(const char *path, void** mem, size_t* size) { void* mapped; long mappedSize; bool success = platform_mapFile(path, &mapped, &mappedSize) != 0; if (success) { *mem = customAlloc(mappedSize); *size = mappedSize; memcpy(*mem, mapped, mappedSize); platform_unmapFile(mapped); } return success; }
// Take a difference report from compareFileEntries and issue appropriate // file modification notes, and check whether they have settled. If so, // transmit updates to clients. static void processFileEntryDeltas(utArray<FileEntryDelta> *deltas) { int curTime = platform_getMilliseconds(); loom_mutex_lock(gFileScannerLock); // Update the pending list with all the stuff we've seen. for (UTsize i = 0; i < deltas->size(); i++) { // Get the delta. const FileEntryDelta& fed = deltas->at(i); // If it's removal, we don't currently send a notification. if (fed.action == FileEntryDelta::Removed) { continue; } // If it's not whitelisted, ignore it. if (!checkInWhitelist(fed.path)) { continue; } // Note it in the pending modification list. bool sawInList = false; for (UTsize i = 0; i < gPendingModifications.size(); i++) { FileModificationNote& fmn = gPendingModifications.at(i); if (strcmp(fmn.path, fed.path.c_str())) { continue; } // Match - update time. lmLogDebug(gAssetAgentLogGroup, "FILE CHANGING - '%s'", fed.path.c_str()); fmn.lastSeenTime = curTime; sawInList = true; } if (!sawInList) { FileModificationNote fmn; fmn.path = stringtable_insert(fed.path.c_str()); fmn.lastSeenTime = curTime; gPendingModifications.push_back(fmn); lmLogDebug(gAssetAgentLogGroup, "FILE CHANGED - '%s'", fed.path.c_str()); } } // Now, walk the pending list and send everyone who hasn't been touched for the settling period. // See how many files we're sending and note that state. const int settleTimeMs = 750; int transferStartTime = platform_getMilliseconds(); int totalPendingTransfers = 0; for (UTsize i = 0; i < gPendingModifications.size(); i++) { // Only consider pending items that have aged out. FileModificationNote& fmn = gPendingModifications.at(i); if (curTime - fmn.lastSeenTime < settleTimeMs) { continue; } totalPendingTransfers++; } bool didWeNotifyUserAboutPending = false; for (UTsize i = 0; i < gPendingModifications.size(); i++) { // Only consider pending items that have aged out. FileModificationNote& fmn = gPendingModifications.at(i); if (curTime - fmn.lastSeenTime < settleTimeMs) { continue; } // Make the path canonical. utString filename = fmn.path; char canonicalFile[MAXPATHLEN]; makeAssetPathCanonical(filename.c_str(), canonicalFile); // Note: we don't deal with deleted files properly (by uploading new state) because realpath // only works right when the file exists. So we just skip doing anything about it. // Note we are using gActiveHandlers.size() outside of a lock, but this is ok as it's a word. if ((strstr(canonicalFile, ".loom") || strstr(canonicalFile, ".ls")) && (gActiveHandlers.size() > 0)) { lmLog(gAssetAgentLogGroup, "Changed '%s'", canonicalFile); } if (canonicalFile[0] == 0) { lmLog(gAssetAgentLogGroup, " o Ignoring file missing from the asset folder!"); // Remove from the pending list. gPendingModifications.erase(i); i--; continue; } // Queue the callback. enqueueFileChangeCallback(canonicalFile); // Map the file. void *fileBits = NULL; long fileBitsLength = 0; if (!platform_mapFile(canonicalFile, &fileBits, &fileBitsLength)) { lmLog(gAssetAgentLogGroup, " o Skipping due to file failing to map."); continue; } // Loop over the active sockets. loom_mutex_lock(gActiveSocketsMutex); // Blast it out to all clients. for (UTsize j = 0; j < gActiveHandlers.size(); j++) { // If it's for a specific client then only send to that client. if ((fmn.onlyForClient != -1) && (fmn.onlyForClient != gActiveHandlers[j]->getId())) { continue; } gActiveHandlers[j]->sendFile(canonicalFile, fileBits, fileBitsLength, totalPendingTransfers); // If it has been more than a second, note that we are still working. const int remainingTransferCount = (totalPendingTransfers * gActiveHandlers.size()) - j; if (((platform_getMilliseconds() - transferStartTime) > 2000) && (remainingTransferCount > 1)) { transferStartTime = platform_getMilliseconds(); lmLog(gAssetAgentLogGroup, "Still transferring files. %d to go!", remainingTransferCount - 1); didWeNotifyUserAboutPending = true; } } loom_mutex_unlock(gActiveSocketsMutex); totalPendingTransfers--; // Unmap the file. platform_unmapFile(fileBits); // Remove from the pending list. gPendingModifications.erase(i); i--; } loom_mutex_unlock(gFileScannerLock); if (didWeNotifyUserAboutPending) { lmLog(gAssetAgentLogGroup, "Done transferring files!"); } }