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); }
// Entry point for the socket thread. Listen for connections and incoming data, // and route it to the protocol handlers. static int socketListeningThread(void *payload) { // Listen for incoming connections. int listenPort = 12340; gListenSocket = (loom_socketId_t)-1; for ( ; ; ) { gListenSocket = loom_net_listenTCPSocket(listenPort); if (gListenSocket != (loom_socketId_t)-1) { break; } lmLogWarn(gAssetAgentLogGroup, " - Failed to acquire port %d, trying port %d", listenPort, listenPort + 1); listenPort++; } lmLog(gAssetAgentLogGroup, "Listening on port %d", listenPort); while (loom_socketId_t acceptedSocket = loom_net_acceptTCPSocket(gListenSocket)) { // Check to see if we got anybody... if (!acceptedSocket || ((int)(long)acceptedSocket == -1)) { // Process the connections. loom_mutex_lock(gActiveSocketsMutex); for (UTsize i = 0; i < gActiveHandlers.size(); i++) { AssetProtocolHandler* aph = gActiveHandlers[i]; aph->process(); // Check for ping timeout int msSincePing = loom_readTimer(aph->lastActiveTime); if (msSincePing > socketPingTimeoutMs) { gActiveHandlers.erase(i); i--; lmLog(gAssetAgentLogGroup, "Client timed out (%x)", aph->socket); loom_net_closeTCPSocket(aph->socket); lmDelete(NULL, aph); } } loom_mutex_unlock(gActiveSocketsMutex); loom_thread_sleep(10); continue; } lmLog(gAssetAgentLogGroup, "Client connected (%x)", acceptedSocket); loom_mutex_lock(gActiveSocketsMutex); gActiveHandlers.push_back(lmNew(NULL) AssetProtocolHandler(acceptedSocket)); AssetProtocolHandler *handler = gActiveHandlers.back(); handler->registerListener(lmNew(NULL) TelemetryListener()); if (TelemetryServer::isRunning()) handler->sendCommand("telemetryEnable"); // Send it all of our files. // postAllFiles(gActiveHandlers[gActiveHandlers.size()-1]->getId()); loom_mutex_unlock(gActiveSocketsMutex); } return 0; }
// 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!"); } }