Example #1
0
int loom_net_initialize()
{
    // Platform-specific init.
#if LOOM_PLATFORM == LOOM_PLATFORM_WIN32
    WSADATA wsaData;
    WORD    wVersionRequested = MAKEWORD(2, 0);
    int     err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0)
    {
        lmLogError(netLogGroup, "Failed WinSock initalization with error %d", err);
        return 0;
    }

    if (((LOBYTE(wsaData.wVersion) != 2) || (HIBYTE(wsaData.wVersion) != 0)) &&
        ((LOBYTE(wsaData.wVersion) != 1) || (HIBYTE(wsaData.wVersion) != 1)))
    {
        lmLogError(netLogGroup, "Failed WinSock initalization due to version mismatch");
        WSACleanup();
        return 0;
    }

    // Sanity checks.
    lmAssert(sizeof(SOCKET) <= sizeof(loom_socketId_t), "Can't pack a SOCKET into loom_socketId_t");

    lmLogDebug(netLogGroup, "Initialized WinSock");
    
    return 1;

#else
    // Ignore sigpipe.
    lmLogDebug(netLogGroup, "Disabling signal SIGPIPE");
    signal(SIGPIPE, SIG_IGN);
    return 1;
#endif
}
Example #2
0
NativeDelegateCallNote *NativeDelegate::prepCallbackNote() const
{
    lmLogDebug(gNativeDelegateGroup, "Considering async callback %x", this);
    
    // Are noting currently? Just work with that.
    if(_activeNote)
    {
        lmLogDebug(gNativeDelegateGroup, " OUT due to activeNote already present");
        return _activeNote;
    }

    // See if we should try to go async.
    if(_allowAsync == false)
    {
        lmLogDebug(gNativeDelegateGroup, " OUT due to async being disallowed");
        return NULL;
    }

    if(smMainThreadID == platform_getCurrentThreadId())
        return NULL;

    // Only do this for async delegates off main thread.
    lmLogDebug(gNativeDelegateGroup, "Prepping async callback!");
    _activeNote = lmNew(NULL) NativeDelegateCallNote(this);
    return _activeNote;
}
Example #3
0
void *loom_asset_lock(const char *name, unsigned int type, int block)
{
    const char *namePtr = stringtable_insert(name);

    loom_mutex_lock(gAssetLock);

    // Look it up.
    loom_asset_t *asset = loom_asset_getAssetByName(namePtr, 1);
    lmAssert(asset != NULL, "Didn't get asset even though we should have!");

    // If not loaded, and we aren't ready to block, return NULL.
    if ((block == 0) && (asset->state != loom_asset_t::Loaded))
    {
        lmLogDebug(gAssetLogGroup, "Not blocking and not loaded yet; lock of '%s' failed.", namePtr);
        loom_mutex_unlock(gAssetLock);
        return NULL;
    }

    // Otherwise, let's force it to load now.
    if (asset->state != loom_asset_t::Loaded)
    {
        lmLogDebug(gAssetLogGroup, "Blocking so forcing load of '%s'.", namePtr);

        loom_asset_preload(namePtr);

        lmAssert(loom_asset_isOnTrackToLoad(asset), "Preloaded but wasn't on track to load!");

        while (loom_asset_checkLoadedPercentage(namePtr) != 1.f && loom_asset_isOnTrackToLoad(asset))
        {
            lmLogDebug(gAssetLogGroup, "Pumping load of '%s'...", namePtr);
            loom_asset_pump();
        }

        if (asset->state != loom_asset_t::Loaded)
        {
            lmLogError(gAssetLogGroup, "Unable to load asset '%s'!", name);
            loom_mutex_unlock(gAssetLock);
            return NULL;
        }
    }

    // Check type.
    if (asset->type != type)
    {
        lmLogError(gAssetLogGroup, "Tried to lock asset '%s' with wrong type, assetType=%x, requestedType=%x", name, asset->type, type);
        loom_mutex_unlock(gAssetLock);
        return NULL;
    }

    // Inc count.
    asset->blob->incRef();

    loom_mutex_unlock(gAssetLock);

    lmLogDebug(gAssetLogGroup, "Locked asset '%s'...", namePtr);

    // Return ptr.
    return asset->blob->bits;
}
Example #4
0
void Graphics::reset(int width, int height, uint32_t flags)
{
    lmAssert(sInitialized, "Please make sure to call Graphics::initialize first");

    lmLogDebug(gGFXLogGroup, "Graphics::reset - %dx%d %x", width, height, flags);

    // clear context loss state
    sContextLost = false;

	Loom2D::Matrix mvp;
	mvp.scale(2.0f / width, 2.0f / height);
	mvp.translate(-1.0f, -1.0f);
	//mvp.copyToMatrix4(sMVPInverted);
	// Inverted is normal due to OpenGL origin being bottom left
	if (!(flags & FLAG_INVERTED)) {
		mvp.scale(1.0f, -1.0f);
	}
	mvp.copyToMatrix4(sMVP);
	
	sCurrentModelViewProjection = sMVP;

    // cache current values
    sWidth  = width;
    sHeight = height;
    sFlags = flags;
}
Example #5
0
// We don't track every file. This function filters potential files.
static bool checkInWhitelist(utString path)
{
    // Note the platform-specific version of our whitelisted folders.
    static utString assetPath = "./assets"; platform_normalizePath(const_cast<char*>(assetPath.c_str()));
    static utString binPath   = "./bin";    platform_normalizePath(const_cast<char*>(binPath.c_str()));
    static utString srcPath   = "./src";    platform_normalizePath(const_cast<char*>(srcPath.c_str()));

    // Just prefix match against assets for now - ignore things in other folders.
    lmLogDebug(gAssetAgentLogGroup, "Whitelisting path %s prefix %s\n", path.c_str(), path.substr(0, 6).c_str());
    platform_normalizePath(const_cast<char*>(path.c_str()));
    if (path.substr(path.length() - 3, 3) == "tmp")
    {
        return false;
    }
    if (path.substr(0, 8) == assetPath)
    {
        return true;
    }
    if (path.substr(0, 5) == srcPath)
    {
        return true;
    }
    if (path.substr(0, 5) == binPath)
    {
        return true;
    }
    return false;
}
Example #6
0
void loom_asset_flush(const char *name)
{
   // 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);

    // Delete it + unload it.
    loom_asset_t *asset = loom_asset_getAssetByName(name, 0);

   if(!asset || asset->isSupplied)
   {
      loom_mutex_unlock(gAssetLock);
      return;
   }
    
   lmLogDebug(gAssetLogGroup, "Flushing '%s'", name);

    if (asset->blob)
    {
        asset->blob->decRef();
        asset->blob = NULL;
    }

    asset->state = loom_asset_t::Unloaded;

    // Fire subscribers.
    if(!gShuttingDown)
        loom_asset_notifySubscribers(asset->name.c_str());

    loom_mutex_unlock(gAssetLock);
}
Example #7
0
void *loom_asset_imageDeserializer( void *buffer, size_t bufferLen, LoomAssetCleanupCallback *dtor )
{
   loom_asset_image_t *img;

   lmAssert(buffer != NULL, "buffer should not be null");

   img = (loom_asset_image_t*)lmAlloc(gAssetAllocator, sizeof(loom_asset_image_t));

    // parse any orientation info from exif format
   img->orientation = exifinfo_parse_orientation(buffer, (unsigned int)bufferLen);

   img->bits = stbi_load_from_memory((const stbi_uc *)buffer, (int)bufferLen, &img->width, &img->height, &img->bpp, 4);
   
   *dtor = loom_asset_imageDtor;
   
   if(!img->bits)
   {
      lmLogError(gImageAssetGroup, "Image load failed due to this cryptic reason: %s", stbi_failure_reason());
      lmFree(gAssetAllocator, img);
      return 0;
   }
   
   lmLogDebug(gImageAssetGroup, "Allocated %d bytes for an image!", img->width * img->height * 4);
   
   return img;
}
Example #8
0
void finishProfilerBlock(profilerBlock_t *block)
{
    int curTime = platform_getMilliseconds();

    if (curTime - block->startTime > block->thresholdMs)
    {
        lmLogDebug(gProfilerLogGroup, "%s exceeded threshold (%dms > %dms)", block->name, curTime - block->startTime, block->thresholdMs);
    }
}
Example #9
0
// Helper to fully shut down the listen socket; helps quite a bit on OS X.
static void shutdownListenSocket()
{
    if (gListenSocket)
    {
        lmLogDebug(gAssetAgentLogGroup, "Shutting down listen socket...");
        loom_net_closeTCPSocket(gListenSocket);
        lmLog(gAssetAgentLogGroup, "Done! Goodbye.");
        gListenSocket = 0;
    }
}
Example #10
0
static void loom_net_setSocketReuseAddress(loom_socketId_t s, int reuse)
{
#if LOOM_PLATFORM_IS_APPLE == 1
    int reuseTmp = reuse;
    int status   = setsockopt((SOCKET)s, SOL_SOCKET, SO_REUSEPORT, &reuseTmp, sizeof(reuseTmp));
    lmAssert(status == 0, "Failed trying to set socket reuse address status due to %d", status);
#else
    // If you get issues where the socket stays bound after a listening process (like the assetAgent)
    // terminates you may need to set SO_REUSEADDR or SO_REUSEPORT or equivalent on your platform.
    lmLogDebug(netLogGroup, "Note: this platform doesn't support reusing port, but it probably doesn't matter.");
#endif
}
Example #11
0
int loom_net_writeTCPSocket(loom_socketId_t s, void *buffer, int bytesToWrite)
{
#if LOOM_PLATFORM == LOOM_PLATFORM_WIN32
    int winsockErrorCode;
#endif

    int bytesLeft = bytesToWrite;
    for ( ; ; )
    {
        int result = send((SOCKET)s, buffer, bytesLeft, MSG_NOSIGNAL);

        if (result >= 0)
        {
            bytesLeft -= result;
            if (bytesLeft != 0)
            {
                lmLogDebug(netLogGroup, "Partial write on socket %d, expected %d but wrote %d! Retrying...", s, bytesToWrite, result);

                // Set up to try again by advancing into the buffer.
                buffer = (void *)((char *)buffer + result);
                continue;
            }

            return bytesToWrite - bytesLeft;
        }

#if LOOM_PLATFORM == LOOM_PLATFORM_WIN32
        winsockErrorCode = WSAGetLastError();
        if (winsockErrorCode != WSAEWOULDBLOCK)
        {
            break;
        }
#else
        if (errno != EAGAIN)
        {
            break;
        }
#endif

        if (loom_net_isSocketDead(s))
        {
            break;
        }

        loom_thread_sleep(5);
    }

    return -1;
}
Example #12
0
// Convert a path into its canonical form.
static int makeAssetPathCanonical(const char *pathIn, char pathOut[MAXPATHLEN])
{
    // First thing, safe pathOut.
    pathOut[0] = 0;

    char cwd[MAXPATHLEN];
    char* cwdres = getcwd(cwd, MAXPATHLEN);

    char* resolvedPathPtr = NULL;
    // Note, man page suggests that realpath won't work right for
    // non-existant folders/files.
    if (cwdres != NULL)
    {
        resolvedPathPtr = platform_realpath(pathIn, NULL);
    }

    if (resolvedPathPtr == NULL)
    {
        lmLogError(gAssetAgentLogGroup, "Failed to resolve path %s via realpath due to %s", pathIn, strerror(errno));
        return 0;
    }

    // Now slurp off the prefix if possible.
    lmLogDebug(gAssetAgentLogGroup, "Checking for prefix '%s' '%s' '%s'", resolvedPathPtr, cwd, pathIn);

    lmAssert(resolvedPathPtr, "No resolved path!");
    lmAssert(cwd, "Could not get working dir?");

    const char *prefixOffset = strstr(resolvedPathPtr, cwd);
    if (prefixOffset != NULL)
    {
        // Great, it's known to us.
        strncpy(pathOut, prefixOffset + strlen(cwd) + 1, MAXPATHLEN);
        // free(resolvedPathPtr);
        return 1;
    }
    else
    {
        // Nope, unknown.
        // free(resolvedPathPtr);
        return 0;
    }
}
Example #13
0
int
main(int argc, char *argv[])
{
#ifdef MSVC_DEBUG_HEAP
    // Get current flag
    int tmpFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);

    tmpFlag |= _CRTDBG_ALLOC_MEM_DF;
    tmpFlag |= _CRTDBG_LEAK_CHECK_DF;

    // Set flag to the new value.
    _CrtSetDbgFlag(tmpFlag);
#endif

#ifdef WIN32
    // When on windows, do some workarounds so our console window
    // behaves properly.
    
    // put the program name into argv[0]
    char filename[_MAX_PATH];
    GetModuleFileNameA(NULL, filename, _MAX_PATH);
    argv[0] = filename;

    bool fromRuby = false;

    for (int i = 1; i < argc; i++)
    {
        if (!stricmp(argv[i], "ProcessID") && i + 1 < argc)
        {
            fromRuby = true;

            char *pEnd;  

            long int pid = strtol (argv[i + 1], &pEnd, 10);

            LS::Process::rubyProcessId = pid;

            memmove(argv + i, argv + i + 2, (argc - i - 2)*sizeof(char*));
            argc -= 2;
            i--;
            break;
        }
    }

    LS::Process::consoleAttached = false;
    if (!fromRuby && AttachConsole(ATTACH_PARENT_PROCESS))
    {
        HANDLE consoleHandleOut = GetStdHandle(STD_OUTPUT_HANDLE);
        int fdOut = _open_osfhandle((intptr_t)consoleHandleOut, _O_TEXT);
        FILE *fpOut = _fdopen(fdOut, "w");
        *stdout = *fpOut;
        setvbuf(stdout, NULL, _IONBF, 0);

        //redirect unbuffered STDERR to the console
        HANDLE consoleHandleError = GetStdHandle(STD_ERROR_HANDLE);
        int fdError = _open_osfhandle((intptr_t)consoleHandleError, _O_TEXT);
        FILE *fpError = _fdopen(fdError, "w");
        *stderr = *fpError;
        setvbuf(stderr, NULL, _IONBF, 0);

        LS::Process::consoleAttached = true;
    }
#endif

    // Initialize logging.
    loom_log_initialize();

    LSLuaState::initCommandLine(argc, (const char**) argv);

    /* Enable standard application logging */
    SDL_LogSetAllPriority(SDL_LOG_PRIORITY_INFO);
    SDL_LogSetOutputFunction(sdlLogOutput, NULL);

    SDL_Init(
        SDL_INIT_TIMER |
        SDL_INIT_VIDEO | 
        SDL_INIT_JOYSTICK |
        SDL_INIT_HAPTIC |
        SDL_INIT_GAMECONTROLLER |
        SDL_INIT_EVENTS
    );

    

    int ret;

    
#if LOOM_RENDERER_OPENGLES2
    ret = SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
    lmAssert(ret == 0, "SDL Error: %s", SDL_GetError());
    ret = SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
    lmAssert(ret == 0, "SDL Error: %s", SDL_GetError());
#endif
    
    int stencilSize = 1;
    ret = SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, stencilSize);
    lmAssert(ret == 0, "SDL Error: %s", SDL_GetError());
    
    // Set up SDL window.
    if ((gSDLWindow = SDL_CreateWindow(
        "Loom",
        0, 0,
        100,
        100,
        SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN
#if LOOM_PLATFORM == LOOM_PLATFORM_IOS
        | SDL_WINDOW_BORDERLESS
#endif
        | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI)) == NULL)
    {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateWindow(): %s\n", SDL_GetError());
        exit(0);
    }

    gContext = SDL_GL_CreateContext(gSDLWindow);
    if (!gContext) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_GL_CreateContext(): %s\n", SDL_GetError());
        exit(2);
    }

    ret = SDL_GL_SetSwapInterval(-1);
    if (ret != 0) {
        lmLog(coreLogGroup, "Late swap tearing not supported, using vsync");
        SDL_GL_SetSwapInterval(1);
    }

    // And show the window with proper settings.
    SDL_SetWindowPosition(gSDLWindow, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);

    SDL_StopTextInput();

    // Initialize Loom!
    loom_appSetup();    
    supplyEmbeddedAssets();

    /* Main render loop */
    gLoomExecutionDone = 0;

    /* Display SDL version */
    SDL_version compiled;
    SDL_version linked;

    SDL_VERSION(&compiled);
    SDL_GetVersion(&linked);

    lmLogDebug(coreLogGroup, "Compiled with SDL version %d.%d.%d and linking against SDL version %d.%d.%d ...", compiled.major, compiled.minor, compiled.patch, linked.major, linked.minor, linked.patch);

    /* Game Controller */
    // Enable controller events
    SDL_GameControllerEventState(SDL_ENABLE);

    //Open all connected game controllers
    LoomGameController::openAll();
    
#ifdef __EMSCRIPTEN__
    emscripten_set_main_loop(loop, 0, 1);
#else
    while (!gLoomExecutionDone) loop();
#endif

    //Close all opened game controllers before closing application
    LoomGameController::closeAll();
    loom_appShutdown();
    
#ifdef WIN32
    LS::Process::cleanupConsole();
#endif

    exit(0);
    return 0; /* to prevent compiler warning */
}
Example #14
0
void NativeDelegate::disallowAsync()
{
    lmLogDebug(gNativeDelegateGroup, "SETTING ASYNC OFF %x", this);
    _allowAsync = false;
}
Example #15
0
    virtual bool handleMessage(int fourcc, AssetProtocolHandler *handler, NetworkBuffer& buffer)
    {
        switch (fourcc)
        {
        case LOOM_FOURCC('F', 'I', 'L', 'E'):
           {
               // How many pending files?
               gPendingFiles = buffer.readInt();
               loom_asset_notifyPendingCountChange();

               // Read the filename.
               char *path;
               int  fileStringLength;
               buffer.readString(&path, &fileStringLength);

               // And the file length.
               int bitsLength = buffer.readInt();

               // Checkpoint at end!
               buffer.readCheckpoint(0xDEADBEE3);

               // Prepare the buffer!
               if (pendingFile != NULL)
               {
                   lmLogError(gAssetLogGroup, "Got a new FILE '%s' while still processing existing file '%s'!", path, pendingFilePath.c_str());
                   wipePendingData();
               }

               // Update the pending file state.
               pendingFilePath = path;
               lmFree(NULL, path);
               path = NULL;

               pendingFileLength = bitsLength;
               pendingFile       = (const char *)lmAlloc(gAssetAllocator, pendingFileLength);

               // Log it.
               lmLogDebug(gAssetLogGroup, "FILE '%s' %d bytes incoming.", pendingFilePath.c_str(), bitsLength);

               // Awesome, sit back and wait for chunks to come in.
               return true;
           }

        case LOOM_FOURCC('F', 'C', 'H', 'K'):
           {
               // How many pending files?
               gPendingFiles = buffer.readInt();
               loom_asset_notifyPendingCountChange();

               // Get the offset.
               int chunkOffset = buffer.readInt();

               // Read bits into the buffer.
               char *fileBits;
               int  fileBitsLength;
               buffer.readString(&fileBits, &fileBitsLength);
               memcpy((void *)(pendingFile + chunkOffset), (void *)fileBits, fileBitsLength);
               lmFree(NULL, fileBits);

               // Checkpoint at end!
               buffer.readCheckpoint(0xDEADBEE2);

               int lastByteOffset = chunkOffset + fileBitsLength;

               // Log it.
               lmLogDebug(gAssetLogGroup, "FILE '%s' %d of %d bytes!", pendingFilePath.c_str(), lastByteOffset, pendingFileLength);

               // If it's the last one, instate it and wipe our buffer.
               if (lastByteOffset == pendingFileLength)
               {
                   // And this resolves a file so decrement the pending count. This way
                   // we will get to zero.
                   gPendingFiles--;
                   loom_asset_notifyPendingCountChange();

                   // Instate the new asset data.
                   loom_asset_t *asset    = loom_asset_getAssetByName(pendingFilePath.c_str(), 1);
                   int          assetType = loom_asset_recognizeAssetTypeFromPath(pendingFilePath);
                   if (assetType == 0)
                   {
                       lmLogDebug(gAssetLogGroup, "Couldn't infer file type for '%s', ignoring.", pendingFilePath.c_str());
                       wipePendingData();
                       return true;
                   }

                   lmLogWarn(gAssetLogGroup, "Applying new version of '%s', %d bytes.", pendingFilePath.c_str(), pendingFileLength);
                   LoomAssetCleanupCallback dtor = NULL;
                   void *assetBits = loom_asset_deserializeAsset(pendingFilePath.c_str(), assetType, pendingFileLength, (void *)pendingFile, &dtor);
                   asset->instate(assetType, assetBits, dtor);

                   // And wipe the pending date.
                   wipePendingData();
               }
         }

         return true;
      }

    return false;
    }
Example #16
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!");
    }
}
Example #17
0
void *loom_asset_soundDeserializer( void *buffer, size_t bufferLen, LoomAssetCleanupCallback *dtor )
{
    loom_asset_sound_t *sound = (loom_asset_sound_t*)lmAlloc(gAssetAllocator, sizeof(loom_asset_sound_t));
    memset(sound, 0, sizeof(loom_asset_sound_t));
    unsigned char *charBuff = (unsigned char *)buffer;

    // Look for magic header in buffer.
    if(charBuff[0] == 0x4f 
        && charBuff[1] == 0x67
        && charBuff[2] == 0x67
        && charBuff[3] == 0x53)
    {
        // It's an Ogg, assume vorbis and throw it to stb_vorbis.
        int channels = 0;
        short *outputBuffer = NULL;
        int sampleCount = stb_vorbis_decode_memory(charBuff, (int)bufferLen, &channels, &outputBuffer);
        if(sampleCount < 0)
        {
            lmLogError(gSoundAssetGroup, "Failed to decode Ogg Vorbis");
            loom_asset_soundDtor(&sound);
            return NULL;
        }

        sound->channels = channels;
        sound->bytesPerSample = 2;
        sound->sampleCount = sampleCount;
        sound->bufferSize = sampleCount * channels * 2;
        sound->sampleRate = 44100; // TODO: This should be variable

        // We can skip this if we get clever about allocations in stbv.
        sound->buffer = lmAlloc(gAssetAllocator, sound->bufferSize);
        memcpy(sound->buffer, outputBuffer, sound->bufferSize);

        free(outputBuffer);
    }
    else if((charBuff[0] == 0x49 // ID3
        &&   charBuff[1] == 0x44
        &&   charBuff[2] == 0x33)
        ||  (charBuff[0] == 0xff // Missing ID3 Tag
        &&   charBuff[1] == 0xfb))
    {
        // It's an MP3, y'all!
        short *outBuffer = (short*)lmAlloc(gAssetAllocator, MP3_MAX_SAMPLES_PER_FRAME * 2);
        mp3_info_t mp3Info;

        // Decode once to get total size.
        size_t totalBytes = 0;
        size_t bytesRead = 0, bytesLeft = bufferLen;

        mp3_decoder_t decmp3 = mp3_create();
        for(;;)
        {
            int bytesDecoded = mp3_decode(decmp3, charBuff + bytesRead, (int)bytesLeft, outBuffer, &mp3Info);
            bytesRead += bytesDecoded;
            bytesLeft -= bytesDecoded;
            totalBytes += mp3Info.audio_bytes;
            if(bytesDecoded > 0)
                continue;

            // Clean up.
            mp3_done(decmp3);
            break;
        }

        // Great, set up the sound asset.
        // TODO: Warn about non 44.1khz mp3s.
        sound->channels = mp3Info.channels;
        sound->bytesPerSample = 2;
        sound->sampleCount = (int)totalBytes / sound->bytesPerSample;
        sound->bufferSize = sound->channels * sound->bytesPerSample * sound->sampleCount;
        sound->sampleRate = 44100; // TODO: This should be variable
        sound->buffer = lmAlloc(gAssetAllocator, sound->bufferSize);

        // Decode again to get real samples.        
        decmp3 = mp3_create();
        bytesRead = 0; bytesLeft = bufferLen;
        int curBufferOffset = 0;
        for(;;)
        {
            int bytesDecoded = mp3_decode(decmp3, charBuff + bytesRead, (int)bytesLeft, outBuffer, &mp3Info);
            bytesRead += bytesDecoded;
            bytesLeft -= bytesDecoded;

            memcpy(((unsigned char*)sound->buffer) + curBufferOffset, outBuffer, mp3Info.audio_bytes);
            curBufferOffset += mp3Info.audio_bytes;

            if(bytesDecoded > 0)
                continue;

            // Clean up.
            mp3_done(decmp3);
            break;
        }

        // Awesome, all set!
        lmFree(gAssetAllocator, outBuffer);
    }
    else if(charBuff[0] == 0x52 // 'RIFF'
         && charBuff[1] == 0x49
         && charBuff[2] == 0x46
         && charBuff[3] == 0x46)
    {
        // We've got a wav file
        wav_info wav;
        bool wavLoadSuccess = load_wav(charBuff, bufferLen, NULL, &wav);
        if (!wavLoadSuccess)
        {
            lmLogError(gSoundAssetGroup, "Failed to load wav format info");
            loom_asset_soundDtor(sound);
            return 0;
        }
        
        sound->channels = wav.numChannels;
        sound->bytesPerSample = wav.sampleSize / 8; // wav sample size is in bits
        if (sound->bytesPerSample != 1 && sound->bytesPerSample != 2)
        {
            lmLogError(gSoundAssetGroup, "Unsupported wav format. Currently only 8-bit or 16-bit PCM are supported");
            loom_asset_soundDtor(sound);
            return 0;
        }
        sound->bufferSize = wav.sampleDataSize;
        sound->sampleCount = sound->bufferSize / sound->bytesPerSample;
        sound->sampleRate = wav.samplesPerSecond;
        
        sound->buffer = lmAlloc(gAssetAllocator, sound->bufferSize);
        bool dataCopySuccess = load_wav(charBuff, bufferLen, (uint8_t*)sound->buffer, NULL);
        if (!dataCopySuccess)
        {
            lmLogError(gSoundAssetGroup, "Failed to copy wav data");
            loom_asset_soundDtor(sound);
            return 0;
        }
    }
    else
    {
        lmLogError(gSoundAssetGroup, "Failed to identify sound buffer by magic number!");
        loom_asset_soundDtor(sound);
        return 0;
    }

   *dtor = loom_asset_soundDtor;
   if(!sound->buffer)
   {
      lmLogError(gSoundAssetGroup, "Sound load failed due to this cryptic reason: %s", "(unknown)");
      lmFree(gAssetAllocator, sound);
      return 0;
   }

    lmLogDebug(gSoundAssetGroup, "Sound allocation: %d bytes", sound->bufferSize);
    return sound;
}
Example #18
0
void DLLEXPORT assetAgent_run(IdleCallback idleCb, LogCallback logCb, FileChangeCallback changeCb)
{
    loom_log_initialize();
    platform_timeInitialize();
    stringtable_initialize();
    loom_net_initialize();

    // Put best effort towards closing our listen socket when we shut down, to
    // avoid bugs on OSX where the OS won't release it for a while.

#if LOOM_PLATFORM == LOOM_PLATFORM_OSX || LOOM_PLATFORM == LOOM_PLATFORM_LINUX
    atexit(shutdownListenSocket);
    signal(SIGINT, shutdownListenSocketSignalHandler);
#endif

    // Set up mutexes.
    gActiveSocketsMutex = loom_mutex_create();
    gFileScannerLock    = loom_mutex_create();
    gCallbackLock       = loom_mutex_create();

    // Note callbacks.
    gLogCallback        = logCb;
    gFileChangeCallback = changeCb;


    utString *sdkPath = optionGet("sdk");
    if (sdkPath != NULL) TelemetryServer::setClientRootFromSDK(sdkPath->c_str());
    const char *ltcPath = getenv("LoomTelemetry");
    if (ltcPath != NULL) TelemetryServer::setClientRoot(ltcPath);

    if (optionEquals("telemetry", "true")) TelemetryServer::start();



    // Set up the log callback.
    loom_log_addListener(fileWatcherLogListener, NULL);

    lmLogDebug(gAssetAgentLogGroup, "Starting file watcher thread...");
    gFileWatcherThread = loom_thread_start((ThreadFunction)fileWatcherThread, NULL);
    lmLogDebug(gAssetAgentLogGroup, "   o OK!");

    lmLogDebug(gAssetAgentLogGroup, "Starting socket listener thread...");
    gSocketListenerThread = loom_thread_start((ThreadFunction)socketListeningThread, NULL);
    lmLogDebug(gAssetAgentLogGroup, "   o OK!");

    // Loop till it's time to quit.
    while (!gQuitFlag)
    {
        // Serve the idle callback.
        if (idleCb)
        {
            idleCb();
        }

        // And anything in the queue.
        while (CallbackQueueNote *cqn = dequeueCallback())
        {
            if (!cqn)
            {
                break;
            }

            // Issue the call.
            if (cqn->type == QNT_Change)
            {
                gFileChangeCallback(cqn->text.c_str());
            }
            else if (cqn->type == QNT_Log)
            {
                gLogCallback(cqn->text.c_str());
            }
            else
            {
                lmAssert(false, "Unknown callback queue note type.");
            }

            // Clean it up.
            //free((void *)cqn->text);
            lmDelete(NULL, cqn);
        }
        // Pump any remaining socket writes
        loom_net_pump();

        // Poll at about 60hz.
        loom_thread_sleep(16);
    }

    // Clean up the socket.
    shutdownListenSocket();
}