void DLLEXPORT assetAgent_command(const char *cmd) { if (strstr(cmd, ".sendall") != 0) { postAllFiles(); } else if (strstr(cmd, ".clients") != 0) { listClients(); } else if (strstr(cmd, ".telemetry") != 0) { TelemetryServer::isRunning() ? TelemetryServer::stop() : TelemetryServer::start(); assetAgent_command(TelemetryServer::isRunning() ? "telemetryEnable" : "telemetryDisable"); } else if (cmd[0] != 0) { loom_mutex_lock(gActiveSocketsMutex); if (gActiveHandlers.size() == 0) { if (strcmp(cmd, "terminate") != 0) sendIgnoredError(); } for (UTsize i = 0; i < gActiveHandlers.size(); i++) { gActiveHandlers[i]->sendCommand(cmd); } loom_mutex_unlock(gActiveSocketsMutex); } }
void LSLuaState::finalizeAssemblyLoad(Assembly *assembly, utArray<Type *>& types) { for (UTsize j = 0; j < types.size(); j++) { Type *type = types.at(j); if (type->isNative() || type->hasStaticNativeMember()) { // we're native NativeInterface::resolveScriptType(type); } } declareLuaTypes(types); initializeLuaTypes(types); // we avoid runtime validation on mobile, this works but should be unnecessary // as issues with be caught on OSX/WINDOWS development platforms #if LOOM_PLATFORM == LOOM_PLATFORM_OSX || LOOM_PLATFORM == LOOM_PLATFORM_WIN32 for (UTsize j = 0; j < types.size(); j++) { Type *type = types.at(j); TypeValidatorRT tv(this, type); tv.validate(); } #endif assembly->bootstrap(); }
void LSLuaState::initializeLuaTypes(const utArray<Type *>& types) { for (UTsize i = 0; i < types.size(); i++) { types[i]->cache(); } // initialize all classes for (UTsize i = 0; i < types.size(); i++) { initializeClass(types[i]); } // run static initializers now that all classes have been initialized for (UTsize i = 0; i < types.size(); i++) { lsr_classinitializestatic(VM(), types[i]); } }
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); }
void LSLuaState::declareLuaTypes(const utArray<Type *>& types) { for (UTsize i = 0; i < types.size(); i++) { declareClass(types[i]); } // validate/initialize native types for (UTsize i = 0; i < types.size(); i++) { Type *type = types.at(i); if (type->isNative() || type->hasStaticNativeMember()) { NativeTypeBase *ntb = NativeInterface::getNativeType(type); if (!ntb) { LSError("Unable to get NativeTypeBase for type %s", type->getFullName().c_str()); } if (type->isNativeManaged() != ntb->isManaged()) { if (type->isNativeManaged()) { LSError("Managed mismatch for type %s, script declaration specifies native while native bindings are unmanaged", type->getFullName().c_str()); } else { LSError("Managed mismatch for type %s, script declaration specifies unmanaged while native bindings are managed", type->getFullName().c_str()); } } ntb->validate(type); type->setCTypeName(ntb->getCTypeName()); } } }
// Dump connected clients to the console; useful for telling who is connected to the console! static void listClients() { loom_mutex_lock(gActiveSocketsMutex); // Blast it out to all clients. lmLog(gAssetAgentLogGroup, "Clients"); for (UTsize i = 0; i < gActiveHandlers.size(); i++) { lmLog(gAssetAgentLogGroup, " #%d - %s", gActiveHandlers[i]->getId(), gActiveHandlers[i]->description().c_str()); } loom_mutex_unlock(gActiveSocketsMutex); }
void gkBlenderSceneConverter::applyParents(utArray<Blender::Object*> &children) { UTsize i; for (i = 0; i < children.size(); i++) { Blender::Object* bchild = children.at(i); gkGameObject* gchild = m_gscene->getObject(GKB_IDNAME(bchild)); if (gchild) { gkGameObject* gpar = m_gscene->getObject(GKB_IDNAME(bchild->parent)); if (gpar && !gpar->getProperties().hasBoneParent()) gchild->setParent(gpar); } } }
// Helper to recognize an asset's type from its path/name. static int loom_asset_recognizeAssetTypeFromPath(utString& path) { // Easy out - empty strings are no good! if (path.length() == 0) { return 0; } // Walk backwards to first dot. size_t firstDotPos = path.size() - 1; for (size_t pos = path.size() - 1; pos > 0; pos--) { if (path.at(pos) != '.') { continue; } firstDotPos = pos; break; } // Split out the extension. utString pathExt = path.substr(firstDotPos + 1); // See if we can get a type out of any of the recognizers. int type = 0; for (UTsize i = 0; i < gRecognizerList.size(); i++) { type = gRecognizerList[i](pathExt.c_str()); if (type) { break; } } // No match, so let's use text. if (type == 0) { lmLogInfo(gAssetLogGroup, "Couldn't recognize '%s', defaulting to LATText...", path.c_str()); type = LATText; } return type; }
void NativeDelegate::executeDeferredCalls(lua_State *L) { ensureQueueInit(); loom_mutex_lock(gCallNoteMutex); // Try to resolve the delegate pointer. utArray<NativeDelegate *> *delegates = NULL; if (sActiveNativeDelegates.find(L) != UT_NPOS) { delegates = *(sActiveNativeDelegates.get(L)); } else { // No delegate list, can't do it. loom_mutex_unlock(gCallNoteMutex); return; } for(unsigned int i=0; i<gNDCallNoteQueue.size(); i++) { NativeDelegateCallNote *ndcn = gNDCallNoteQueue[i]; bool found = false; for(unsigned int i=0; i<delegates->size(); i++) { // Look for our delegate. if((*delegates)[i] != ndcn->delegate) continue; // If key mismatches, warn and bail. if((*delegates)[i]->_key != ndcn->delegateKey) { lmLogError(gNativeDelegateGroup, "Found delegate call note with key mismatch (delegate=%x actualKey=%x expectedKey=%x), ignoring...", (*delegates)[i], (*delegates)[i]->_key, ndcn->delegateKey); break; } // Match! found = true; break; } // Bail if no match. if(!found) continue; // Otherwise, let's call it. const NativeDelegate *theDelegate = ndcn->delegate; for(;;) { unsigned char actionType = ndcn->readByte(); bool done = false; char *str = NULL; utByteArray *bytes; switch(actionType) { case MSG_Nop: lmLogError(gNativeDelegateGroup, "Got a nop in delegate data stream."); break; case MSG_PushString: str = ndcn->readString(); theDelegate->pushArgument(str); free(str); break; case MSG_PushByteArray: bytes = ndcn->readByteArray(); theDelegate->pushArgument(bytes); free(bytes); break; case MSG_PushDouble: theDelegate->pushArgument(ndcn->readDouble()); break; case MSG_PushFloat: theDelegate->pushArgument(ndcn->readFloat()); break; case MSG_PushInt: theDelegate->pushArgument((int)ndcn->readInt()); break; case MSG_PushBool: theDelegate->pushArgument(ndcn->readBool()); break; case MSG_Invoke: theDelegate->invoke(); done = true; break; } if(done) break; } } // Purge queue. gNDCallNoteQueue.clear(); loom_mutex_unlock(gCallNoteMutex); }
int loom_asset_queryPendingLoads() { return gAssetLoadQueue.size() > 0 ? 1 : 0; }
// 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!"); } }
void LSLuaState::cacheAssemblyTypes(Assembly *assembly, utArray<Type *>& types) { // setup assembly type lookup field lua_rawgeti(L, LUA_GLOBALSINDEX, LSASSEMBLYLOOKUP); lua_pushlightuserdata(L, assembly); lua_setfield(L, -2, assembly->getName().c_str()); lua_pop(L, 1); assembly->ordinalTypes = new Type *[types.size() + 1]; for (UTsize j = 0; j < types.size(); j++) { Type *type = types.at(j); assembly->types.insert(type->getName(), type); lmAssert(type->getTypeID() > 0 && type->getTypeID() <= (LSTYPEID)types.size(), "LSLuaState::cacheAssemblyTypes TypeID out of range"); assembly->ordinalTypes[type->getTypeID()] = type; const char *typeName = type->getFullName().c_str(); // fast access cache if (!strcmp(typeName, "system.Object")) { objectType = type; } else if (!strcmp(typeName, "system.Null")) { nullType = type; } else if (!strcmp(typeName, "system.Boolean")) { booleanType = type; } else if (!strcmp(typeName, "system.Number")) { numberType = type; } else if (!strcmp(typeName, "system.String")) { stringType = type; } else if (!strcmp(typeName, "system.Function")) { functionType = type; } else if (!strcmp(typeName, "system.Vector")) { vectorType = type; } lua_rawgeti(L, LUA_GLOBALSINDEX, LSINDEXMEMBERINFONAME); lua_pushlightuserdata(L, type); lua_gettable(L, -2); // cache all members for fast lookup of memberinfo -> pre-interned // lua string (interning strings is the devil's work) if (lua_isnil(L, -1)) { lua_pop(L, 1); utArray<MemberInfo *> members; MemberTypes types; types.method = true; types.field = true; types.property = true; type->findMembers(types, members, false); // cache the type to member info table lua_pushlightuserdata(L, type); lua_pushstring(L, type->getName()); lua_settable(L, -3); for (UTsize i = 0; i < members.size(); i++) { MemberInfo *mi = members.at(i); lua_pushlightuserdata(L, mi); lua_pushstring(L, mi->getName()); lua_settable(L, -3); } } else { lua_pop(L, 1); } lua_pop(L, 1); // if we weren't cached during assembly load, cache now if (!typeCache.get(type->getFullName())) { typeCache.insert(type->getFullName(), type); } } lmAssert(nullType, "LSLuaState::cacheAssemblyTypes - system.Null not found"); lmAssert(booleanType, "LSLuaState::cacheAssemblyTypes - system.Boolean not found"); lmAssert(numberType, "LSLuaState::cacheAssemblyTypes - system.Number not found"); lmAssert(stringType, "LSLuaState::cacheAssemblyTypes - system.String not found"); lmAssert(functionType, "LSLuaState::cacheAssemblyTypes - system.Function not found"); lmAssert(vectorType, "LSLuaState::cacheAssemblyTypes - system.Vector not found"); }