Torque3D Documentation / _generateds / moduleManager.cpp

moduleManager.cpp

Engine/source/module/moduleManager.cpp

More...

Public Variables

Detailed Description

Public Variables

ModuleManager ModuleDatabase 

Public Functions

IMPLEMENT_CONOBJECT(ModuleManager )

moduleDefinitionVersionIdSort(const void * a, const void * b)

   1
   2//-----------------------------------------------------------------------------
   3// Copyright (c) 2013 GarageGames, LLC
   4//
   5// Permission is hereby granted, free of charge, to any person obtaining a copy
   6// of this software and associated documentation files (the "Software"), to
   7// deal in the Software without restriction, including without limitation the
   8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   9// sell copies of the Software, and to permit persons to whom the Software is
  10// furnished to do so, subject to the following conditions:
  11//
  12// The above copyright notice and this permission notice shall be included in
  13// all copies or substantial portions of the Software.
  14//
  15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21// IN THE SOFTWARE.
  22//-----------------------------------------------------------------------------
  23
  24#include "moduleManager.h"
  25
  26#ifndef _MODULE_MERGE_DEFINITION_H
  27#include "moduleMergeDefinition.h"
  28#endif
  29
  30#ifndef _TAML_MODULE_ID_UPDATE_VISITOR_H_
  31#include "tamlModuleIdUpdateVisitor.h"
  32#endif
  33
  34#ifndef _MODULE_CALLBACKS_H_
  35#include "moduleCallbacks.h"
  36#endif
  37
  38#ifndef _CONSOLETYPES_H_
  39#include "console/consoleTypes.h"
  40#endif
  41
  42// Script bindings.
  43#include "moduleManager_ScriptBinding.h"
  44
  45//-----------------------------------------------------------------------------
  46
  47IMPLEMENT_CONOBJECT( ModuleManager );
  48
  49//-----------------------------------------------------------------------------
  50
  51ModuleManager ModuleDatabase;
  52
  53//-----------------------------------------------------------------------------
  54
  55S32 QSORT_CALLBACK moduleDefinitionVersionIdSort( const void* a, const void* b )
  56{
  57    // Fetch module definitions.
  58   ModuleDefinition* pDefinition1 = *(ModuleDefinition**)a;
  59   ModuleDefinition* pDefinition2 = *(ModuleDefinition**)b;
  60
  61   // Fetch version Ids.
  62   const U32 versionId1 = pDefinition1->getVersionId();
  63   const U32 versionId2 = pDefinition2->getVersionId();
  64
  65   // We sort higher version Id first.
  66   return versionId1 > versionId2 ? -1 : versionId1 < versionId2 ? 1 : 0;
  67}
  68
  69//-----------------------------------------------------------------------------
  70
  71ModuleManager::ModuleManager() :
  72    mEnforceDependencies(true),
  73    mEchoInfo(true),
  74    mDatabaseLocks( 0 )
  75{
  76    // Set module extension.
  77    dStrcpy( mModuleExtension, MODULE_MANAGER_MODULE_DEFINITION_EXTENSION );
  78}
  79
  80//-----------------------------------------------------------------------------
  81
  82bool ModuleManager::onAdd()
  83{
  84    if( !Parent::onAdd() )
  85        return false;
  86
  87    // Register listeners.
  88    mNotificationListeners.registerObject();
  89
  90    return true;
  91}
  92
  93//-----------------------------------------------------------------------------
  94
  95void ModuleManager::onRemove()
  96{
  97    // Clear database.
  98    clearDatabase();
  99
 100    // Unregister object.
 101    mNotificationListeners.unregisterObject();
 102
 103    // Call parent.
 104    Parent::onRemove();
 105}
 106
 107//-----------------------------------------------------------------------------
 108
 109void ModuleManager::initPersistFields()
 110{
 111    // Call parent.
 112    Parent::initPersistFields();
 113
 114    addField( "EnforceDependencies", TypeBool, Offset(mEnforceDependencies, ModuleManager), "Whether the module manager enforces any dependencies on module definitions it discovers or not." );
 115    addField( "EchoInfo", TypeBool, Offset(mEchoInfo, ModuleManager), "Whether the module manager echos extra information to the console or not." );
 116}
 117
 118//-----------------------------------------------------------------------------
 119
 120void ModuleManager::onDeleteNotify( SimObject *object )
 121{
 122    // Cast to a module definition.
 123    ModuleDefinition* pModuleDefinition = dynamic_cast<ModuleDefinition*>( object );
 124
 125    // Ignore if not appropriate.
 126    if ( pModuleDefinition == NULL )
 127        return;
 128
 129    // Warn.
 130    Con::warnf( "Module Manager::onDeleteNotify() - Notified of a module definition deletion for module Id '%s' of version Id '%d' however this should not happen and can cause module database corruption.",
 131        pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
 132}
 133
 134//-----------------------------------------------------------------------------
 135
 136bool ModuleManager::setModuleExtension( const char* pExtension )
 137{
 138    // Sanity!
 139    AssertFatal( pExtension != NULL, "Cannot set module extension with NULL extension." );
 140
 141    // Did we find an extension period?
 142    if ( *pExtension == '.' )
 143    {
 144        // Yes, so warn.
 145        Con::warnf("Module Manager: Failed to set extension as supplied extension contains an initial period: '%s'.", pExtension );
 146        return false;
 147    }
 148
 149    // Is the extension too large?
 150    if ( dStrlen( pExtension ) > sizeof( mModuleExtension ) )
 151    {
 152        // Yes, so warn.
 153        Con::warnf("Module Manager: Failed to set extension as supplied extension is too large: '%s'.", pExtension );
 154        return false;
 155    }
 156
 157    // Set module extension.
 158    dStrcpy( mModuleExtension, pExtension );
 159
 160    return true;
 161}
 162
 163//-----------------------------------------------------------------------------
 164
 165bool ModuleManager::scanModules( const char* pPath, const bool rootOnly )
 166{
 167    // Lock database.
 168    LockDatabase( this );
 169
 170    // Sanity!
 171    AssertFatal( pPath != NULL, "Cannot scan module with NULL path." );
 172
 173    // Expand module location.
 174    char pathBuffer[1024];
 175    Con::expandPath( pathBuffer, sizeof(pathBuffer), pPath );
 176
 177    // Info.
 178    if ( mEchoInfo )
 179    {
 180        Con::printSeparator();
 181        Con::printf( "Module Manager: Started scanning '%s'...", pathBuffer );
 182    }
 183
 184    Vector<StringTableEntry> directories;
 185
 186    // Find directories.
 187    if ( !Platform::dumpDirectories( pathBuffer, directories, rootOnly ? 1 : -1 ) )
 188    {
 189        // Failed so warn.
 190        Con::warnf( "Module Manager: Failed to scan module directories in path '%s'.", pathBuffer );
 191        return false;
 192    }
 193
 194    // Fetch extension length.
 195    const U32 extensionLength = dStrlen( mModuleExtension );
 196
 197    Vector<Platform::FileInfo> files;
 198
 199    // Iterate directories.
 200    for( Vector<StringTableEntry>::iterator basePathItr = directories.begin(); basePathItr != directories.end(); ++basePathItr )
 201    {
 202        // Fetch base path.
 203        StringTableEntry basePath = *basePathItr;
 204
 205        // Skip if we're only processing the root and this is not the root.
 206        if ( rootOnly && basePathItr != directories.begin() )
 207            continue;
 208
 209        // Find files.
 210        files.clear();
 211        if ( !Platform::dumpPath( basePath, files, 0 ) )
 212        {
 213            // Failed so warn.
 214            Con::warnf( "Module Manager: Failed to scan modules files in directory '%s'.", basePath );
 215            return false;
 216        }
 217
 218        // Iterate files.
 219        for ( Vector<Platform::FileInfo>::iterator fileItr = files.begin(); fileItr != files.end(); ++fileItr )
 220        {
 221            // Fetch file info.
 222            Platform::FileInfo* pFileInfo = fileItr;
 223
 224            // Fetch filename.
 225            const char* pFilename = pFileInfo->pFileName;
 226
 227            // Find filename length.
 228            const U32 filenameLength = dStrlen( pFilename );
 229
 230            // Skip if extension is longer than filename.
 231            if ( extensionLength > filenameLength )
 232                continue;
 233
 234            // Skip if extension not found.
 235            if ( dStricmp( pFilename + filenameLength - extensionLength, mModuleExtension ) != 0 )
 236                continue;
 237
 238            // Register module.
 239            registerModule( basePath, pFileInfo->pFileName );
 240        }
 241
 242        // Stop processing if we're only processing the root.
 243        if ( rootOnly )
 244            break;
 245    }
 246
 247    // Info.
 248    if ( mEchoInfo )
 249    {
 250        Con::printf( "Module Manager: Finished scanning '%s'.", pathBuffer );
 251    }
 252
 253    return true;
 254}
 255
 256//-----------------------------------------------------------------------------
 257
 258bool ModuleManager::loadModuleGroup( const char* pModuleGroup )
 259{
 260    // Lock database.
 261    LockDatabase( this );
 262
 263    // Sanity!
 264    AssertFatal( pModuleGroup != NULL, "Cannot load module group with NULL group name." );
 265
 266    typeModuleLoadEntryVector   moduleResolvingQueue;
 267    typeModuleLoadEntryVector   moduleReadyQueue;
 268
 269    // Fetch module group.
 270    StringTableEntry moduleGroup = StringTable->insert( pModuleGroup );
 271
 272    // Info.
 273    if ( mEchoInfo )
 274    {
 275        Con::printSeparator();
 276        Con::printf( "Module Manager: Loading group '%s':" ,moduleGroup );
 277    }
 278
 279    // Is the module group already loaded?
 280    if ( findGroupLoaded( moduleGroup ) != NULL )
 281    {
 282        // Yes, so warn.
 283        Con::warnf( "Module Manager: Cannot load group '%s' as it is already loaded.", moduleGroup );
 284        return false;
 285    }
 286
 287    // Find module group.
 288    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
 289
 290    // Did we find the module group?
 291    if ( moduleGroupItr == mGroupModules.end() )
 292    {
 293        // No, so info.
 294        if ( mEchoInfo )
 295        {
 296            Con::printf( "Module Manager: No modules found for module group '%s'.", moduleGroup );
 297        }
 298        
 299        return true;
 300    }
 301
 302    // Yes, so fetch the module Ids.
 303    typeModuleIdVector* pModuleIds = moduleGroupItr->value;
 304
 305    // Iterate module groups.
 306    for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
 307    {
 308        // Fetch module Id.
 309        StringTableEntry moduleId = *moduleIdItr;
 310
 311        // Finish if we could not resolve the dependencies for module Id (of any version Id).
 312        if ( !resolveModuleDependencies( moduleId, 0, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
 313            return false;
 314    }
 315
 316    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
 317    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
 318    {
 319        // Fetch load ready module definition.
 320        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
 321
 322        // Fetch the module Id loaded entry.
 323        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
 324
 325        // Did we find a loaded entry?
 326        if ( pLoadedModuleEntry != NULL )
 327        {
 328            // Yes, so is it the one we need to load?
 329            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
 330            {
 331                // Yes, so warn.
 332                Con::warnf( "Module Manager: Cannot load module group '%s' as the module Id '%s' at version Id '%d' is required but the module Id is already loaded but at version Id '%d'.",
 333                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
 334                return false;
 335            }
 336        }
 337    }
 338
 339    // Info.
 340    if ( mEchoInfo )
 341    {
 342        // Info.
 343        Con::printf( "Module Manager: Group '%s' and its dependencies is comprised of the following '%d' module(s):", moduleGroup, moduleReadyQueue.size() );
 344
 345        // Iterate the modules echoing them.
 346        for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
 347        {
 348            // Fetch the ready entry.
 349            ModuleDefinition* pModuleDefinition = moduleReadyItr->mpModuleDefinition;
 350
 351            // Info.
 352            Con::printf( "> module Id '%s' at version Id '%d':", pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
 353        }
 354    }
 355
 356    // Add module group.
 357    mGroupsLoaded.push_back( moduleGroup );
 358
 359    // Reset modules loaded count.
 360    U32 modulesLoadedCount = 0;
 361
 362    // Iterate the modules, executing their script files and call their create function.
 363    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
 364    {
 365        // Fetch the ready entry.
 366        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
 367
 368        // Fetch load ready module definition.
 369        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;
 370
 371        // Fetch any loaded entry for the module Id.
 372        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
 373
 374        // Is the module already loaded.
 375        if ( pLoadedEntry != NULL )
 376        {
 377            // Yes, so increase load count.
 378            pLoadedEntry->mpModuleDefinition->increaseLoadCount();
 379
 380            // Skip.
 381            continue;
 382        }
 383
 384        // No, so info.
 385        if ( mEchoInfo )
 386        {
 387            Con::printSeparator();
 388            Con::printf( "Module Manager: Loading group '%s' : module Id '%s' at version Id '%d' in group '%s' using the script file '%s'.",
 389                moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
 390        }
 391
 392        // Is the module deprecated?
 393        if ( pLoadReadyModuleDefinition->getDeprecated() )
 394        {
 395            // Yes, so warn.
 396            Con::warnf( "Module Manager: Caution: module Id '%s' at version Id '%d' in group '%s' is deprecated.  You should use a newer version!",
 397                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup() );
 398        }
 399
 400        // Add the path expando for module.
 401        Con::addPathExpando( pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getModulePath() );
 402
 403        // Create a scope set.
 404        SimSet* pScopeSet = new SimSet;
 405        pScopeSet->registerObject( pLoadReadyModuleDefinition->getModuleId() );
 406        pReadyEntry->mpModuleDefinition->mScopeSet = pScopeSet->getId();
 407
 408        // Increase load count.
 409        pReadyEntry->mpModuleDefinition->increaseLoadCount();
 410
 411        // Queue module loaded.
 412        mModulesLoaded.push_back( *pReadyEntry );
 413
 414        // Bump modules loaded count.
 415        modulesLoadedCount++;
 416
 417        // Raise notifications.
 418        raiseModulePreLoadNotifications( pLoadReadyModuleDefinition );
 419
 420        // Do we have a script file-path specified?
 421        if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString() )
 422        {
 423            // Yes, so execute the script file.
 424            const bool scriptFileExecuted = dAtob( Con::executef("exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) );
 425
 426            // Did we execute the script file?
 427            if ( scriptFileExecuted )
 428            {
 429                // Yes, so is the create method available?
 430                if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getCreateFunction() ) )
 431                {
 432                    // Yes, so call the create method.
 433                    Con::executef( pScopeSet, pLoadReadyModuleDefinition->getCreateFunction() );
 434                }
 435            }
 436            else
 437            {
 438                // No, so warn.
 439                Con::errorf( "Module Manager: Cannot load module group '%s' as the module Id '%s' at version Id '%d' as it failed to have the script file '%s' loaded.",
 440                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
 441            }
 442        }
 443
 444        // Raise notifications.
 445        raiseModulePostLoadNotifications( pLoadReadyModuleDefinition );
 446    }
 447
 448    // Info.
 449    if ( mEchoInfo )
 450    {
 451        Con::printSeparator();
 452        Con::printf( "Module Manager: Finish loading '%d' module(s) for group '%s'.", modulesLoadedCount, moduleGroup );
 453        Con::printSeparator();
 454    }
 455
 456    return true;
 457}
 458
 459//-----------------------------------------------------------------------------
 460
 461bool ModuleManager::unloadModuleGroup( const char* pModuleGroup )
 462{
 463    // Lock database.
 464    LockDatabase( this );
 465
 466    // Sanity!
 467    AssertFatal( pModuleGroup != NULL, "Cannot unload module group with NULL group name." );
 468
 469    typeModuleLoadEntryVector   moduleResolvingQueue;
 470    typeModuleLoadEntryVector   moduleReadyQueue;
 471
 472    // Fetch module group.
 473    StringTableEntry moduleGroup = StringTable->insert( pModuleGroup );
 474
 475    // Info.
 476    if ( mEchoInfo )
 477    {
 478        Con::printSeparator();
 479        Con::printf( "Module Manager: Unloading group '%s':" , moduleGroup );
 480    }
 481
 482    // Find the group loaded iterator.
 483    typeGroupVector::iterator groupLoadedItr = findGroupLoaded( moduleGroup );
 484
 485    // Is the module group already unloaded?
 486    if ( groupLoadedItr == NULL )
 487    {
 488        // No, so warn.
 489        Con::warnf( "Module Manager: Cannot unload group '%s' as it is not loaded.", moduleGroup );
 490        return false;
 491    }
 492
 493    // Find module group.
 494    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
 495
 496    // Did we find the module group?
 497    if ( moduleGroupItr == mGroupModules.end() )
 498    {
 499        // No, so info.
 500        if ( mEchoInfo )
 501        {
 502            Con::printf( "Module Manager: No modules found for module group '%s'.", moduleGroup );
 503            return true;
 504        }
 505    }
 506
 507    // Yes, so fetch the module Ids.
 508    typeModuleIdVector* pModuleIds = moduleGroupItr->value;
 509
 510    // Iterate module groups.
 511    for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
 512    {
 513        // Fetch module Id.
 514        StringTableEntry moduleId = *moduleIdItr;
 515
 516        // Finish if we could not resolve the dependencies for module Id (of any version Id).
 517        if ( !resolveModuleDependencies( moduleId, 0, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
 518            return false;
 519    }
 520
 521    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
 522    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
 523    {
 524        // Fetch load ready module definition.
 525        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
 526
 527        // Fetch the module Id loaded entry.
 528        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
 529
 530        // Did we find a loaded entry?
 531        if ( pLoadedModuleEntry != NULL )
 532        {
 533            // Yes, so is it the one we need to load?
 534            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
 535            {
 536                // Yes, so warn.
 537                Con::warnf( "Module Manager: Cannot unload module group '%s' as the module Id '%s' at version Id '%d' is required but the module Id is loaded but at version Id '%d'.",
 538                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
 539                return false;
 540            }
 541        }
 542    }
 543
 544    // Remove module group.
 545    mGroupsLoaded.erase_fast( groupLoadedItr );
 546
 547    // Reset modules unloaded count.
 548    U32 modulesUnloadedCount = 0;
 549
 550    // Iterate the modules in reverse order calling their destroy function.
 551    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.end()-1; moduleReadyItr >= moduleReadyQueue.begin(); --moduleReadyItr )
 552    {
 553        // Fetch the ready entry.
 554        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
 555
 556        // Fetch load ready module definition.
 557        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;;
 558
 559        // Fetch any loaded entry for the module Id.
 560        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
 561
 562        // Is the module loaded.
 563        if ( pLoadedEntry == NULL )
 564        {
 565            // No, so warn.
 566            if ( mEchoInfo )
 567            {
 568                Con::printf( "Module Manager: Unloading group '%s' but could not unload module Id '%s' at version Id '%d'.",
 569                    moduleGroup, pLoadedEntry->mpModuleDefinition->getModuleId(), pLoadedEntry->mpModuleDefinition->getVersionId() );
 570            }
 571            // Skip.
 572            continue;
 573        }
 574
 575        // Reduce load count.
 576        pLoadedEntry->mpModuleDefinition->reduceLoadCount();
 577
 578        // Sanity!
 579        AssertFatal( pLoadedEntry->mpModuleDefinition->getLoadCount() >= 0, "ModuleManager::unloadModuleGroup() - Encountered an invalid load count." );
 580
 581        // Do we need to unload?
 582        if ( pLoadedEntry->mpModuleDefinition->getLoadCount() == 0 )
 583        {
 584            // Yes, so info.
 585            if ( mEchoInfo )
 586            {
 587                Con::printSeparator();
 588                Con::printf( "Module Manager: Unload group '%s' with module Id '%s' at version Id '%d' in group '%s'.",
 589                    moduleGroup, pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleGroup() );
 590            }
 591
 592            // Raise notifications.
 593            raiseModulePreUnloadNotifications( pLoadReadyModuleDefinition );
 594
 595            // Fetch the module Id loaded entry.
 596            typeModuleLoadEntryVector::iterator moduleLoadedItr = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
 597
 598            // Sanity!
 599            AssertFatal( moduleLoadedItr != NULL, "ModuleManager::unloadModuleGroup() - Cannot find module to unload it." );
 600
 601            // Dequeue module loaded.
 602            mModulesLoaded.erase_fast( moduleLoadedItr );
 603
 604            // Fetch scope set.
 605            SimSet* pScopeSet = dynamic_cast<SimSet*>(Sim::findObject(pLoadReadyModuleDefinition->mScopeSet));
 606
 607            // Is the destroy method available?
 608            if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getDestroyFunction() ) )
 609            {
 610                // Yes, so call the destroy method.
 611                Con::executef( pScopeSet, pLoadReadyModuleDefinition->getDestroyFunction() );
 612            }
 613
 614            // Remove scope set.
 615            pScopeSet->deleteAllObjects();
 616            pScopeSet->unregisterObject();
 617            pLoadReadyModuleDefinition->mScopeSet = 0;
 618
 619            // Remove path expando for module.
 620            Con::removePathExpando( pLoadReadyModuleDefinition->getModuleId() );
 621
 622            // Bump modules unloaded count.
 623            modulesUnloadedCount++;
 624
 625            // Raise notifications.
 626            raiseModulePostUnloadNotifications( pLoadReadyModuleDefinition );
 627        }
 628    }
 629
 630    // Info.
 631    if ( mEchoInfo )
 632    {
 633        Con::printSeparator();
 634        Con::printf( "Module Manager: Finish unloading '%d' module(s) for group '%s'.", modulesUnloadedCount, moduleGroup );
 635        Con::printSeparator();
 636    }
 637
 638    return true;
 639}
 640
 641//-----------------------------------------------------------------------------
 642
 643bool ModuleManager::loadModuleExplicit( const char* pModuleId, const U32 versionId )
 644{
 645    // Lock database.
 646    LockDatabase( this );
 647
 648    // Sanity!
 649    AssertFatal( pModuleId != NULL, "Cannot load explicit module Id with NULL module Id." );
 650
 651    typeModuleLoadEntryVector   moduleResolvingQueue;
 652    typeModuleLoadEntryVector   moduleReadyQueue;
 653
 654    // Fetch module Id.
 655    StringTableEntry moduleId = StringTable->insert( pModuleId );
 656
 657    // Fetch modules definitions.
 658    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
 659
 660    // Did we find the module Id?
 661    if ( pDefinitions == NULL )
 662    {
 663        // No, so warn.
 664        Con::warnf( "Module Manager: Cannot load explicit module Id '%s' as it does not exist.", moduleId );
 665        return false;
 666    }
 667
 668    // Fetch module group.
 669    StringTableEntry moduleGroup = pDefinitions->mModuleGroup;
 670
 671    // Info.
 672    if ( mEchoInfo )
 673    {
 674        Con::printSeparator();
 675        Con::printf( "Module Manager: Loading explicit module Id '%s' at version Id '%d':", moduleId, versionId );
 676    }
 677
 678    // Finish if we could not resolve the dependencies for module Id (of any version Id).
 679    if ( !resolveModuleDependencies( moduleId, versionId, moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
 680        return false;
 681
 682    // Check the modules we want to load to ensure that we do not have incompatible modules loaded already.
 683    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
 684    {
 685        // Fetch load ready module definition.
 686        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;
 687
 688        // Fetch the module Id loaded entry.
 689        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
 690
 691        // Did we find a loaded entry?
 692        if ( pLoadedModuleEntry != NULL )
 693        {
 694            // Yes, so is it the one we need to load?
 695            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
 696            {
 697                // Yes, so warn.
 698                Con::warnf( "Module Manager: Cannot load explicit module Id '%s' at version Id '%d' as the module Id is already loaded but at version Id '%d'.",
 699                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
 700                return false;
 701            }
 702        }
 703    }
 704
 705    // Info.
 706    if ( mEchoInfo )
 707    {
 708        // Info.
 709        Con::printf( "Module Manager: Explicit load of module Id '%s' at version Id '%d' and its dependencies is comprised of the following '%d' module(s):", moduleId, versionId, moduleReadyQueue.size() );
 710
 711        // Iterate the modules echoing them.
 712        for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
 713        {
 714            // Fetch the ready entry.
 715            ModuleDefinition* pModuleDefinition = moduleReadyItr->mpModuleDefinition;
 716
 717            // Info.
 718            Con::printf( "> module Id '%s' at version Id '%d'", pModuleDefinition->getModuleId(), pModuleDefinition->getVersionId() );
 719        }
 720    }
 721
 722    // Reset modules loaded count.
 723    U32 modulesLoadedCount = 0;
 724
 725    // Iterate the modules, executing their script files and call their create function.
 726    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
 727    {
 728        // Fetch the ready entry.
 729        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
 730
 731        // Fetch load ready module definition.
 732        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;
 733
 734        // Fetch any loaded entry for the module Id.
 735        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
 736
 737        // Is the module already loaded.
 738        if ( pLoadedEntry != NULL )
 739        {
 740            // Yes, so increase load count.
 741            pLoadedEntry->mpModuleDefinition->increaseLoadCount();
 742
 743            // Skip.
 744            continue;
 745        }
 746
 747        // No, so info.
 748        if ( mEchoInfo )
 749        {
 750            Con::printSeparator();
 751            Con::printf( "Module Manager: Loading explicit module Id '%s' at version Id '%d' using the script file '%s'.",
 752                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
 753        }
 754
 755        // Is the module deprecated?
 756        if ( pLoadReadyModuleDefinition->getDeprecated() )
 757        {
 758            // Yes, so warn.
 759            Con::warnf( "Module Manager: Caution: module Id '%s' at version Id '%d' is deprecated,  You should use a newer version!",
 760                pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
 761        }
 762
 763        // Add the path expando for module.
 764        Con::addPathExpando( pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getModulePath() );
 765
 766        // Create a scope set.
 767        SimSet* pScopeSet = new SimSet;
 768        pScopeSet->registerObject( pLoadReadyModuleDefinition->getModuleId() );
 769        pReadyEntry->mpModuleDefinition->mScopeSet = pScopeSet->getId();
 770
 771        // Increase load count.
 772        pReadyEntry->mpModuleDefinition->increaseLoadCount();
 773
 774        // Queue module loaded.
 775        mModulesLoaded.push_back( *pReadyEntry );
 776
 777        // Bump modules loaded count.
 778        modulesLoadedCount++;
 779
 780        // Raise notifications.
 781        raiseModulePreLoadNotifications( pLoadReadyModuleDefinition );
 782
 783        // Do we have a script file-path specified?
 784        if ( pLoadReadyModuleDefinition->getModuleScriptFilePath() != StringTable->EmptyString() )
 785        {
 786            // Yes, so execute the script file.
 787            const bool scriptFileExecuted = dAtob( Con::executef("exec", pLoadReadyModuleDefinition->getModuleScriptFilePath() ) );
 788
 789            // Did we execute the script file?
 790            if ( scriptFileExecuted )
 791            {
 792                // Yes, so is the create method available?
 793                if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getCreateFunction() ) )
 794                {
 795                    // Yes, so call the create method.
 796                    Con::executef( pScopeSet, pLoadReadyModuleDefinition->getCreateFunction() );
 797                }
 798            }
 799            else
 800            {
 801                // No, so warn.
 802                Con::errorf( "Module Manager: Cannot load explicit module Id '%s' at version Id '%d' as it failed to have the script file '%s' loaded.",
 803                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadReadyModuleDefinition->getModuleScriptFilePath() );
 804            }
 805        }
 806
 807        // Raise notifications.
 808        raiseModulePostLoadNotifications( pLoadReadyModuleDefinition );
 809    }
 810
 811    // Info.
 812    if ( mEchoInfo )
 813    {
 814        Con::printSeparator();
 815        Con::printf( "Module Manager: Finish loading '%d' explicit module(s).", modulesLoadedCount );
 816        Con::printSeparator();
 817    }
 818
 819    return true;
 820}
 821
 822//-----------------------------------------------------------------------------
 823
 824bool ModuleManager::unloadModuleExplicit( const char* pModuleId )
 825{
 826    // Lock database.
 827    LockDatabase( this );
 828
 829    // Sanity!
 830    AssertFatal( pModuleId != NULL, "Cannot unload explicit module Id with NULL module Id." );
 831
 832    typeModuleLoadEntryVector   moduleResolvingQueue;
 833    typeModuleLoadEntryVector   moduleReadyQueue;
 834
 835    // Fetch module Id.
 836    StringTableEntry moduleId = StringTable->insert( pModuleId );
 837
 838    // Fetch modules definitions.
 839    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
 840
 841    // Did we find the module Id?
 842    if ( pDefinitions == NULL )
 843    {
 844        // No, so warn.
 845        Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' as it does not exist.", moduleId );
 846        return false;
 847    }
 848
 849    // Find if the module is actually loaded.
 850    ModuleDefinition* pLoadedModule = findLoadedModule( moduleId );
 851
 852    // Is the module loaded?
 853    if ( pLoadedModule == NULL )
 854    {
 855        // No, so warn.
 856        Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' as it is not loaded.", moduleId );
 857        return false;
 858    }
 859
 860    // Fetch module group.
 861    StringTableEntry moduleGroup = pDefinitions->mModuleGroup;
 862
 863    // Info.
 864    if ( mEchoInfo )
 865    {
 866        Con::printSeparator();
 867        Con::printf( "Module Manager: Unloading explicit module Id '%s':" , moduleId );
 868    }
 869
 870    // Finish if we could not resolve the dependencies for module Id (of any version Id).
 871    if ( !resolveModuleDependencies( moduleId, pLoadedModule->getVersionId(), moduleGroup, false, moduleResolvingQueue, moduleReadyQueue ) )
 872        return false;
 873
 874    // Check the modules we want to unload to ensure that we do not have incompatible modules loaded already.
 875    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.begin(); moduleReadyItr != moduleReadyQueue.end(); ++moduleReadyItr )
 876    {
 877        // Fetch load ready module definition.
 878        ModuleDefinition* pLoadReadyModuleDefinition = moduleReadyItr->mpModuleDefinition;;
 879
 880        // Fetch the module Id loaded entry.
 881        ModuleLoadEntry* pLoadedModuleEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
 882
 883        // Did we find a loaded entry?
 884        if ( pLoadedModuleEntry != NULL )
 885        {
 886            // Yes, so is it the one we need to load?
 887            if ( pLoadedModuleEntry->mpModuleDefinition != pLoadReadyModuleDefinition )
 888            {
 889                // Yes, so warn.
 890                Con::warnf( "Module Manager: Cannot unload explicit module Id '%s' at version Id '%d' as the module Id is loaded but at version Id '%d'.",
 891                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId(), pLoadedModuleEntry->mpModuleDefinition->getVersionId() );
 892                return false;
 893            }
 894        }
 895    }
 896
 897    // Reset modules unloaded count.
 898    U32 modulesUnloadedCount = 0;
 899
 900    // Iterate the modules in reverse order calling their destroy function.
 901    for ( typeModuleLoadEntryVector::iterator moduleReadyItr = moduleReadyQueue.end()-1; moduleReadyItr >= moduleReadyQueue.begin(); --moduleReadyItr )
 902    {
 903        // Fetch the ready entry.
 904        ModuleLoadEntry* pReadyEntry = moduleReadyItr;
 905
 906        // Fetch load ready module definition.
 907        ModuleDefinition* pLoadReadyModuleDefinition = pReadyEntry->mpModuleDefinition;;
 908
 909        // Fetch any loaded entry for the module Id.
 910        ModuleLoadEntry* pLoadedEntry = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
 911
 912        // Is the module loaded.
 913        if ( pLoadedEntry == NULL )
 914        {
 915            // No, so warn.
 916            if ( mEchoInfo )
 917            {
 918                Con::printf( "Module Manager: Unloading explicit module Id '%s' at version Id '%d' but ignoring as it is not loaded.",
 919                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
 920            }
 921
 922            // Skip.
 923            continue;
 924        }
 925
 926        // Reduce load count.
 927        pLoadedEntry->mpModuleDefinition->reduceLoadCount();
 928
 929        // Sanity!
 930        AssertFatal( pLoadedEntry->mpModuleDefinition->getLoadCount() >= 0, "ModuleManager::unloadModuleGroup() - Encountered an invalid load count." );
 931
 932        // Do we need to unload?
 933        if ( pLoadedEntry->mpModuleDefinition->getLoadCount() == 0 )
 934        {
 935            // Yes, so info.
 936            if ( mEchoInfo )
 937            {
 938                Con::printSeparator();
 939                Con::printf( "Module Manager: Unload explicit module Id '%s' at version Id '%d'.",
 940                    pLoadReadyModuleDefinition->getModuleId(), pLoadReadyModuleDefinition->getVersionId() );
 941            }
 942
 943            // Raise notifications.
 944            raiseModulePreUnloadNotifications( pLoadReadyModuleDefinition );
 945
 946            // Fetch the module Id loaded entry.
 947            typeModuleLoadEntryVector::iterator moduleLoadedItr = findModuleLoaded( pLoadReadyModuleDefinition->getModuleId() );
 948
 949            // Sanity!
 950            AssertFatal( moduleLoadedItr != NULL, "ModuleManager::unloadModuleExplicit() - Cannot find module to unload it." );
 951
 952            // Dequeue module loaded.
 953            mModulesLoaded.erase_fast( moduleLoadedItr );
 954
 955            // Fetch scope set.
 956            SimSet* pScopeSet = dynamic_cast<SimSet*>(Sim::findObject(pLoadReadyModuleDefinition->mScopeSet));
 957
 958            // Is the destroy method available?
 959            if ( pScopeSet->isMethod( pLoadReadyModuleDefinition->getDestroyFunction() ) )
 960            {
 961                // Yes, so call the destroy method.
 962                Con::executef( pScopeSet, pLoadReadyModuleDefinition->getDestroyFunction() );
 963            }
 964
 965            // Remove scope set.
 966            pScopeSet->deleteAllObjects();
 967            pScopeSet->unregisterObject();
 968            pLoadReadyModuleDefinition->mScopeSet = 0;
 969
 970            // Remove path expando for module.
 971            Con::removePathExpando( pLoadReadyModuleDefinition->getModuleId() );
 972
 973            // Bump modules unloaded count.
 974            modulesUnloadedCount++;
 975
 976            // Raise notifications.
 977            raiseModulePostUnloadNotifications( pLoadReadyModuleDefinition );
 978        }
 979    }
 980
 981    // Info.
 982    if ( mEchoInfo )
 983    {
 984        Con::printSeparator();
 985        Con::printf( "Module Manager: Finish unloading '%d' explicit module(s).", modulesUnloadedCount );
 986        Con::printSeparator();
 987    }
 988
 989    return true;
 990}
 991
 992//-----------------------------------------------------------------------------
 993
 994ModuleDefinition* ModuleManager::findModule( const char* pModuleId, const U32 versionId )
 995{
 996    // Sanity!
 997    AssertFatal( pModuleId != NULL, "Cannot find module with NULL module Id." );
 998
 999    // Find module definition.
1000    ModuleDefinitionEntry::iterator moduleItr = findModuleDefinition( StringTable->insert( pModuleId ), versionId );
1001
1002     // Finish if module was not found.
1003    if ( moduleItr == NULL )
1004        return NULL;
1005
1006    return *moduleItr;
1007}
1008
1009//-----------------------------------------------------------------------------
1010
1011ModuleDefinition* ModuleManager::findLoadedModule( const char* pModuleId )
1012{
1013    // Sanity!
1014    AssertFatal( pModuleId != NULL, "Cannot find module with NULL module Id." );
1015
1016    // Fetch module Id.
1017    StringTableEntry moduleId = StringTable->insert( pModuleId );
1018
1019    // Iterate loaded modules.
1020    for ( typeModuleLoadEntryVector::iterator loadedModuleItr = mModulesLoaded.begin(); loadedModuleItr != mModulesLoaded.end(); ++loadedModuleItr )
1021    {
1022        // Skip if not the module.
1023        if ( loadedModuleItr->mpModuleDefinition->getModuleId() != moduleId )
1024            continue;
1025
1026        return loadedModuleItr->mpModuleDefinition;
1027    }
1028
1029    return NULL;
1030}
1031
1032//-----------------------------------------------------------------------------
1033
1034void ModuleManager::findModules( const bool loadedOnly, typeConstModuleDefinitionVector& moduleDefinitions )
1035{
1036    // Iterate module Ids.
1037    for( typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.begin(); moduleIdItr != mModuleIdDatabase.end(); ++moduleIdItr )
1038    {
1039        // Fetch module definition entry.
1040        ModuleDefinitionEntry* pModuleDefinitionEntry = moduleIdItr->value;
1041
1042        // Iterate module definitions.
1043        for ( typeModuleDefinitionVector::iterator moduleDefinitionItr = pModuleDefinitionEntry->begin(); moduleDefinitionItr != pModuleDefinitionEntry->end(); ++moduleDefinitionItr )
1044        {
1045            // Fetch module definition.
1046            ModuleDefinition* pModuleDefinition = *moduleDefinitionItr;
1047
1048            // Are we searching for loaded modules only?
1049            if ( loadedOnly )
1050            {
1051                // Yes, so skip if the module is not loaded.
1052                if ( pModuleDefinition->getLoadCount() == 0 )
1053                    continue;
1054
1055                // Use module definition.
1056                moduleDefinitions.push_back( pModuleDefinition );
1057
1058                // Finish iterating module definitions as only a single module in this entry can be loaded concurrently.
1059                break;
1060            }
1061
1062            // use module definition.
1063            moduleDefinitions.push_back( pModuleDefinition );
1064        }
1065    }
1066}
1067
1068//-----------------------------------------------------------------------------
1069
1070void ModuleManager::findModuleTypes( const char* pModuleType, const bool loadedOnly, typeConstModuleDefinitionVector& moduleDefinitions )
1071{
1072    // Fetch module type.
1073    StringTableEntry moduleType = StringTable->insert( pModuleType );
1074
1075    // Iterate module Ids.
1076    for( typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.begin(); moduleIdItr != mModuleIdDatabase.end(); ++moduleIdItr )
1077    {
1078        // Fetch module definition entry.
1079        ModuleDefinitionEntry* pModuleDefinitionEntry = moduleIdItr->value;
1080
1081        // Skip if note the module type we're searching for.
1082        if ( pModuleDefinitionEntry->mModuleType != moduleType )
1083            continue;
1084
1085        // Iterate module definitions.
1086        for ( typeModuleDefinitionVector::iterator moduleDefinitionItr = pModuleDefinitionEntry->begin(); moduleDefinitionItr != pModuleDefinitionEntry->end(); ++moduleDefinitionItr )
1087        {
1088            // Fetch module definition.
1089            ModuleDefinition* pModuleDefinition = *moduleDefinitionItr;
1090
1091            // Are we searching for loaded modules only?
1092            if ( loadedOnly )
1093            {
1094                // Yes, so skip if the module is not loaded.
1095                if ( pModuleDefinition->getLoadCount() == 0 )
1096                    continue;
1097
1098                // Use module definition.
1099                moduleDefinitions.push_back( pModuleDefinition );
1100
1101                // Finish iterating module definitions as only a single module in this entry can be loaded concurrently.
1102                break;
1103            }
1104
1105            // use module definition.
1106            moduleDefinitions.push_back( pModuleDefinition );
1107        }
1108    }
1109}
1110
1111//-----------------------------------------------------------------------------
1112
1113StringTableEntry ModuleManager::copyModule( ModuleDefinition* pSourceModuleDefinition, const char* pTargetModuleId, const char* pTargetPath, const bool useVersionPathing )
1114{
1115    // Sanity!
1116    AssertFatal( pSourceModuleDefinition != NULL, "Cannot copy module using a NULL source module definition." );
1117    AssertFatal( pTargetModuleId != NULL, "Cannot copy module using a NULL target module Id." );
1118    AssertFatal( pTargetPath != NULL, "Cannot copy module using a NULL target path." );
1119
1120    // Fetch the source module Id.
1121    StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
1122
1123    // Is the source module definition registered with this module manager?
1124    if ( pSourceModuleDefinition->getModuleManager() != this )
1125    {
1126        // No, so warn.
1127        Con::warnf("Module Manager: Cannot copy module Id '%s' as it is not registered with this module manager.", sourceModuleId );
1128        return StringTable->EmptyString();
1129    }
1130
1131    // Fetch the target module Id.
1132    StringTableEntry targetModuleId = StringTable->insert( pTargetModuleId );
1133
1134    // Extend moduleId/VersionId pathing.
1135    char versionPathBuffer[1024];
1136
1137    // Are we using version pathing?
1138    if ( useVersionPathing )
1139    {
1140        // Yes, so format it.
1141        dSprintf( versionPathBuffer, sizeof(versionPathBuffer), "%s/%s/%d",
1142            pTargetPath, targetModuleId, pSourceModuleDefinition->getVersionId() );
1143    }
1144    else
1145    {
1146        // No, so a straight copy.
1147        dSprintf( versionPathBuffer, sizeof(versionPathBuffer), "%s", pTargetPath );
1148    }
1149
1150    // Expand the path.
1151    char targetPathBuffer[1024];
1152    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), versionPathBuffer );
1153    pTargetPath = targetPathBuffer;
1154
1155    // Info.
1156    if ( mEchoInfo )
1157    {
1158        Con::printf( "Module Manager: Started copying module Id '%s' to target directory '%s'.", sourceModuleId, pTargetPath );
1159    }
1160
1161    // Is the target folder a directory?
1162    if ( !Platform::isDirectory( pTargetPath ) )
1163    {
1164        // No, so we have to ensure that there is a trailing slash as that indicates a folder (not a file) when creating a path in the platform code.
1165        char createDirectoryBuffer[1024];
1166        Con::expandPath( createDirectoryBuffer, sizeof(createDirectoryBuffer), pTargetPath, NULL, true );
1167
1168        // No, so can we create it?
1169        if ( !Platform::createPath( createDirectoryBuffer ) )
1170        {
1171            // No, so warn.
1172            Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as directory was not found and could not be created.",
1173                sourceModuleId, pTargetPath );
1174            return StringTable->EmptyString();
1175        }
1176    }
1177
1178    // Copy the source module to the target folder.
1179    if ( !dPathCopy( pSourceModuleDefinition->getModulePath(), pTargetPath, false ) )
1180    {
1181        // Warn.
1182        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as directory copy failed.",
1183            sourceModuleId, pTargetPath );
1184        return StringTable->EmptyString();
1185    }
1186
1187    // Format the new source module definition file-path.
1188    char newModuleDefinitionSourceFileBuffer[1024];
1189    dSprintf( newModuleDefinitionSourceFileBuffer, sizeof(newModuleDefinitionSourceFileBuffer), "%s/%s", pTargetPath, pSourceModuleDefinition->getModuleFile() );
1190
1191    // Finish if source/target module Ids are identical.
1192    if ( sourceModuleId == targetModuleId )
1193        return StringTable->insert( newModuleDefinitionSourceFileBuffer );
1194
1195    // Format the new target module definition file-path.
1196    char newModuleDefinitionTargetFileBuffer[1024];
1197    dSprintf( newModuleDefinitionTargetFileBuffer, sizeof(newModuleDefinitionTargetFileBuffer), "%s/%s.%s", pTargetPath, targetModuleId, MODULE_MANAGER_MODULE_DEFINITION_EXTENSION );
1198
1199    // Rename the module definition.
1200    if ( !dFileRename( newModuleDefinitionSourceFileBuffer, newModuleDefinitionTargetFileBuffer ) )
1201    {
1202        // Warn.
1203        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as renaming the module from '%s' to '%s' failed.",
1204            sourceModuleId, pTargetPath, newModuleDefinitionSourceFileBuffer, newModuleDefinitionTargetFileBuffer );
1205        return StringTable->EmptyString();
1206    }
1207
1208    Vector<StringTableEntry> directories;
1209
1210    // Find directories.
1211    if ( !Platform::dumpDirectories( pTargetPath, directories, -1 ) )
1212    {
1213        // Warn.
1214        Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as sub-folder scanning/renaming failed.",
1215            sourceModuleId, pTargetPath );
1216        return StringTable->EmptyString();
1217    }
1218
1219    TamlModuleIdUpdateVisitor moduleIdUpdateVisitor;
1220    moduleIdUpdateVisitor.setModuleIdFrom( sourceModuleId );
1221    moduleIdUpdateVisitor.setModuleIdTo( targetModuleId );
1222
1223    Vector<Platform::FileInfo> files;
1224
1225    const char* pExtension = (const char*)"Taml";
1226    const U32 extensionLength = dStrlen(pExtension);
1227
1228    // Iterate directories.
1229    for( Vector<StringTableEntry>::iterator basePathItr = directories.begin(); basePathItr != directories.end(); ++basePathItr )
1230    {
1231        // Fetch base path.
1232        StringTableEntry basePath = *basePathItr;
1233
1234        // Find files.
1235        files.clear();
1236        if ( !Platform::dumpPath( basePath, files, 0 ) )
1237        {
1238            // Warn.
1239            Con::warnf("Module Manager: Cannot copy module Id '%s' using target directory '%s' as sub-folder scanning/renaming failed.",
1240                sourceModuleId, pTargetPath );
1241            return StringTable->EmptyString();
1242        }
1243
1244        // Iterate files.
1245        for ( Vector<Platform::FileInfo>::iterator fileItr = files.begin(); fileItr != files.end(); ++fileItr )
1246        {
1247            // Fetch file info.
1248            Platform::FileInfo* pFileInfo = fileItr;
1249
1250            // Fetch filename.
1251            const char* pFilename = pFileInfo->pFileName;
1252
1253            // Find filename length.
1254            const U32 filenameLength = dStrlen( pFilename );
1255
1256            // Skip if extension is longer than filename.
1257            if ( extensionLength >= filenameLength )
1258                continue;
1259
1260            // Skip if extension not found.
1261            if ( dStricmp( pFilename + filenameLength - extensionLength, pExtension ) != 0 )
1262                continue;
1263
1264            char parseFileBuffer[1024];
1265            dSprintf( parseFileBuffer, sizeof(parseFileBuffer), "%s/%s", pFileInfo->pFullPath, pFilename );
1266
1267            // Parse file.            
1268            if ( !mTaml.parse( parseFileBuffer, moduleIdUpdateVisitor ) )
1269            {
1270                // Warn.
1271                Con::warnf("Module Manager: Failed to parse file '%s' whilst copying module Id '%s' using target directory '%s'.",
1272                    parseFileBuffer, sourceModuleId, pTargetPath );
1273                return StringTable->EmptyString();
1274            }
1275        }
1276    }
1277
1278    // Info.
1279    if ( mEchoInfo )
1280    {
1281        Con::printf( "Module Manager: Finished copying module Id '%s' to target directory '%s'.", sourceModuleId, pTargetPath );
1282    }
1283
1284    return StringTable->insert( newModuleDefinitionTargetFileBuffer );
1285}
1286
1287//-----------------------------------------------------------------------------
1288
1289bool ModuleManager::synchronizeDependencies( ModuleDefinition* pRootModuleDefinition, const char* pTargetDependencyPath )
1290{
1291    // Sanity!
1292    AssertFatal( pRootModuleDefinition != NULL, "Cannot synchronize dependencies with NULL root module definition." );
1293    AssertFatal( pTargetDependencyPath != NULL, "Cannot synchronize dependencies with NULL target dependency path." );
1294
1295    // Fetch the root module Id.
1296    StringTableEntry rootModuleId = pRootModuleDefinition->getModuleId();
1297
1298    // Is the root module definition registered with this module manager?
1299    if ( pRootModuleDefinition->getModuleManager() != this )
1300    {
1301        // No, so warn.
1302        Con::warnf("Cannot synchronize dependencies for module Id '%s' as it is not registered with this module manager.", rootModuleId );
1303        return false;
1304    }
1305
1306    // Expand the path.
1307    char targetPathBuffer[1024];
1308    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), pTargetDependencyPath );
1309    pTargetDependencyPath = targetPathBuffer;
1310
1311    // Is the target dependency folder a directory?
1312    if ( !Platform::isDirectory( pTargetDependencyPath ) )
1313    {
1314        // No, so we have to ensure that there is a trailing slash as that indicates a folder (not a file) when creating a path in the platform code.
1315        char createDirectoryBuffer[1024];
1316        Con::expandPath( createDirectoryBuffer, sizeof(createDirectoryBuffer), pTargetDependencyPath, NULL, true );
1317
1318        // No, so can we create it?
1319        if ( !Platform::createPath( createDirectoryBuffer ) )
1320        {
1321            // No, so warn.
1322            Con::warnf("Cannot synchronize dependencies for module Id '%s' using target directory '%s' as directory was not found and could not be created.",
1323                rootModuleId, pTargetDependencyPath );
1324            return false;
1325        }
1326    }
1327
1328    typeModuleLoadEntryVector       resolvingQueue;
1329    typeModuleLoadEntryVector       sourceModulesNeeded;
1330
1331    // Could we resolve source dependencies?
1332    if ( !resolveModuleDependencies( rootModuleId, pRootModuleDefinition->getVersionId(), pRootModuleDefinition->getModuleGroup(), true, resolvingQueue, sourceModulesNeeded ) )
1333    {
1334        // No, so warn.
1335        Con::warnf("Cannot synchronize dependencies for root module Id '%s' as its dependencies could not be resolved.", rootModuleId );
1336        return false;
1337    }
1338
1339    // Sanity!
1340    AssertFatal( sourceModulesNeeded.size() > 0, "Cannot synchronize dependencies as no modules were returned." );
1341
1342    // Remove the root module definition.
1343    sourceModulesNeeded.pop_back();
1344
1345    // Initialize the target module manager and scan the target folder for modules.
1346    ModuleManager targetModuleManager;
1347    targetModuleManager.mEnforceDependencies = true;
1348    targetModuleManager.mEchoInfo = false;
1349    targetModuleManager.scanModules( pTargetDependencyPath );
1350
1351    char targetFolderGenerateBuffer[1024];
1352
1353    // Iterate module definitions.
1354    for ( typeModuleLoadEntryVector::iterator sourceModuleItr = sourceModulesNeeded.begin(); sourceModuleItr != sourceModulesNeeded.end(); ++sourceModuleItr )
1355    {
1356        // Fetch module definition.
1357        ModuleDefinition* pSourceModuleDefinition = sourceModuleItr->mpModuleDefinition;
1358        
1359        // Fetch the source module Id,
1360        StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
1361
1362        // Fetch the source module version Id.
1363        const U32 sourceVersionId = pSourceModuleDefinition->getVersionId();
1364
1365        // Fetch the source module build Id.
1366        const U32 sourceBuildId = pSourceModuleDefinition->getBuildId();
1367
1368        // Fetch module definition entry for this module Id in the target.
1369        ModuleDefinitionEntry* pDefinitions = targetModuleManager.findModuleId( sourceModuleId );
1370
1371        // Is the module Id present in the target?
1372        if ( pDefinitions == NULL )
1373        {
1374            // No, so format module Id folder path.
1375            dSprintf( targetFolderGenerateBuffer, sizeof(targetFolderGenerateBuffer), "%s/%s/", pTargetDependencyPath, sourceModuleId );
1376
1377            // Create module Id folder.
1378            if ( !Platform::createPath( targetFolderGenerateBuffer ) )
1379            {
1380                // Warn.
1381                Con::warnf("Cannot synchronize dependencies for module Id '%s' as the target directory '%s' could not be created.", sourceModuleId, targetFolderGenerateBuffer );
1382                return false;
1383            }
1384        }
1385        else
1386        {
1387            // Yes, so fetch the module definition for this module Id and version Id in the target.
1388            ModuleDefinitionEntry::iterator definitionItr = targetModuleManager.findModuleDefinition( sourceModuleId, sourceVersionId );
1389
1390            // Is the specific module definition present in the target?
1391            if ( definitionItr != NULL )
1392            {
1393                // Yes, so fetch the module definition.
1394                ModuleDefinition* pTargetModuleDefinition = *definitionItr;
1395
1396                // Fetch the target module build Id.
1397                const U32 targetBuildId = pTargetModuleDefinition->getBuildId();
1398
1399                // Fetch the target module path.
1400                StringTableEntry targetModulePath = pTargetModuleDefinition->getModulePath();
1401
1402                // Remove the target module definition from the database.
1403                targetModuleManager.removeModuleDefinition( pTargetModuleDefinition );
1404
1405                // Skip if the target definition is the same build Id.
1406                if ( targetBuildId == sourceBuildId )
1407                    continue;
1408
1409                // Delete the target module definition folder.
1410                if ( !Platform::deleteDirectory( targetModulePath ) )
1411                {
1412                    // Warn.
1413                    Con::warnf("Cannot synchronize dependencies for module Id '%s' at version Id '%d' as the old module at '%s' could not be deleted.",
1414                        sourceModuleId, sourceVersionId, targetModulePath );
1415                    return false;
1416                }
1417            }
1418        }
1419
1420        // Format source module path.
1421        char sourceFolderPath[1024];
1422        Con::expandPath( sourceFolderPath, sizeof(sourceFolderPath), pSourceModuleDefinition->getModulePath() );
1423
1424        // Format target module path.
1425        dSprintf( targetFolderGenerateBuffer, sizeof(targetFolderGenerateBuffer), "%s/%s/%d", pTargetDependencyPath, sourceModuleId, sourceVersionId );
1426
1427        // Copy the source module to the target folder.
1428        if (!dPathCopy(sourceFolderPath, targetFolderGenerateBuffer, false))
1429        {
1430            // Warn.
1431            Con::warnf("Cannot synchronize dependencies for module Id '%s' at version Id '%d' as the module could not be copied to '%s'.",
1432                sourceModuleId, sourceVersionId, targetFolderGenerateBuffer );
1433            return false;
1434        }
1435    }
1436
1437    // Find any target modules left, These are orphaned modules not depended upon by any other module.
1438    typeConstModuleDefinitionVector orphanedTargetModules;
1439    targetModuleManager.findModules( false, orphanedTargetModules );
1440
1441    // Iterate module definitions.
1442    for ( typeConstModuleDefinitionVector::const_iterator moduleDefinitionItr = orphanedTargetModules.begin(); moduleDefinitionItr != orphanedTargetModules.end(); ++moduleDefinitionItr )
1443    {
1444        // Fetch orphaned module definition.
1445        const ModuleDefinition* pOrphanedModuleDefinition = *moduleDefinitionItr;
1446       
1447        // Delete the target module definition folder.
1448        if ( !Platform::deleteDirectory( pOrphanedModuleDefinition->getModulePath() ) )
1449        {
1450            // Warn.
1451            Con::warnf("Cannot delete orphaned module Id '%s' at version Id '%d' from '%s'.",
1452                pOrphanedModuleDefinition->getModuleId(), pOrphanedModuleDefinition->getVersionId(), pOrphanedModuleDefinition->getModulePath() );
1453        }
1454    }
1455
1456    return true;
1457}
1458
1459//-----------------------------------------------------------------------------
1460
1461bool ModuleManager::canMergeModules( const char* pMergeSourcePath )
1462{
1463    // Sanity!
1464    AssertFatal( pMergeSourcePath != NULL, "Cannot check merge modules with NULL source path." );
1465
1466    // Expand the source path.
1467    char sourcePathBuffer[1024];
1468    Con::expandPath( sourcePathBuffer, sizeof(sourcePathBuffer), pMergeSourcePath );
1469    pMergeSourcePath = sourcePathBuffer;
1470
1471    // Is the path a valid directory?
1472    if ( !Platform::isDirectory( sourcePathBuffer ) )
1473    {
1474        // No, so warn.
1475        Con::warnf( "Cannot check merge modules as path is invalid '%s'.", sourcePathBuffer );
1476        return false;
1477    }
1478
1479    // Initialize the source module manager and scan the source folder for modules.
1480    ModuleManager mergeModuleManager;
1481    mergeModuleManager.mEnforceDependencies = false;
1482    mergeModuleManager.mEchoInfo = false;
1483    mergeModuleManager.scanModules( pMergeSourcePath );
1484
1485    // Find all the merge modules.
1486    typeConstModuleDefinitionVector mergeModules;
1487    mergeModuleManager.findModules( false, mergeModules );
1488
1489    // Iterate found merge module definitions.
1490    for ( typeConstModuleDefinitionVector::const_iterator mergeModuleItr = mergeModules.begin(); mergeModuleItr != mergeModules.end(); ++mergeModuleItr )
1491    {
1492        // Fetch module definition.
1493        const ModuleDefinition* pMergeModuleDefinition = *mergeModuleItr;
1494
1495        // Fetch module Id.
1496        StringTableEntry moduleId = pMergeModuleDefinition->getModuleId();
1497
1498        // Fetch version Id.
1499        const U32 versionId = pMergeModuleDefinition->getVersionId();
1500
1501        // Fetch module group.
1502        StringTableEntry moduleGroup = pMergeModuleDefinition->getModuleGroup();
1503
1504        // Cannot merge if module already exists.
1505        if ( findModuleDefinition( moduleId, versionId ) != NULL )
1506            return false;
1507
1508        // Cannot merge if module is part of a loaded group.
1509        if ( findGroupLoaded( moduleGroup ) != NULL )
1510            return false;
1511    }
1512
1513    // Can merge modules.
1514    return true;
1515}
1516
1517//-----------------------------------------------------------------------------
1518
1519bool ModuleManager::mergeModules( const char* pMergeTargetPath, const bool removeMergeDefinition, const bool registerNewModules )
1520{
1521    // Sanity!
1522    AssertFatal( pMergeTargetPath != NULL, "Cannot merge modules with a target path of NULL." );
1523
1524    // Is a module merge available?
1525    if ( !isModuleMergeAvailable() )
1526    {
1527        // No, so warn.
1528        Con::warnf( "Cannot merge modules as a module merge is not available." );
1529        return false;
1530    }
1531
1532    // Expand the target path.
1533    char targetPathBuffer[1024];
1534    Con::expandPath( targetPathBuffer, sizeof(targetPathBuffer), pMergeTargetPath );
1535    pMergeTargetPath = targetPathBuffer;
1536
1537    // Fetch module merge file path.
1538    StringTableEntry moduleMergeFilePath = getModuleMergeFilePath();
1539
1540    // Read module merge definition.
1541    Taml taml;
1542    ModuleMergeDefinition* pModuleMergeDefinition = taml.read<ModuleMergeDefinition>( moduleMergeFilePath );
1543    
1544    // Do we have a module merge definition.
1545    if ( pModuleMergeDefinition == NULL )
1546    {
1547        // No, so warn.
1548        Con::warnf( "Cannot merge modules as the module merge definition file failed to load '%s'.", moduleMergeFilePath );
1549        return false;
1550    }
1551
1552    // Fetch the merge source path.
1553    StringTableEntry mergeSourcePath = pModuleMergeDefinition->getModuleMergePath();
1554
1555    // Remove the module merge definition.
1556    pModuleMergeDefinition->deleteObject();
1557    pModuleMergeDefinition = NULL;
1558
1559    // If we cannot merge the modules then we only process modules flagged as critical merge.
1560    const bool criticalMergeOnly = !canMergeModules( mergeSourcePath );
1561
1562    // Initialize the target module manager and scan the target folder for modules.
1563    ModuleManager targetModuleManager;
1564    targetModuleManager.mEnforceDependencies = false;
1565    targetModuleManager.mEchoInfo = false;
1566    targetModuleManager.scanModules( pMergeTargetPath );
1567
1568    // Initialize the source module manager and scan the source folder for modules.
1569    ModuleManager sourceModuleManager;
1570    sourceModuleManager.mEnforceDependencies = false;
1571    sourceModuleManager.mEchoInfo = false;
1572    sourceModuleManager.scanModules( mergeSourcePath );
1573
1574    // Find all the source modules.
1575    typeConstModuleDefinitionVector sourceModules;
1576    sourceModuleManager.findModules( false, sourceModules );
1577
1578    // Iterate found merge module definitions.
1579    for ( typeConstModuleDefinitionVector::const_iterator sourceModuleItr = sourceModules.begin(); sourceModuleItr != sourceModules.end(); ++sourceModuleItr )
1580    {
1581        // Fetch the source module definition.
1582        const ModuleDefinition* pSourceModuleDefinition = *sourceModuleItr;
1583
1584        // Skip if we're performing a critical merge only and the module is not flagged as critical merge.
1585        if ( criticalMergeOnly && pSourceModuleDefinition->getCriticalMerge() )
1586            continue;
1587
1588        // Fetch source module Id.
1589        const StringTableEntry sourceModuleId = pSourceModuleDefinition->getModuleId();
1590
1591        // Fetch source version Id.
1592        const U32 sourceVersionId = pSourceModuleDefinition->getVersionId();
1593
1594        // Fetch source build Id.
1595        const U32 sourceBuildId = pSourceModuleDefinition->getBuildId();
1596
1597        // Format module Id folder path.
1598        char targetModuleIdBuffer[1024];
1599        dSprintf( targetModuleIdBuffer, sizeof(targetModuleIdBuffer), "%s/%s/", pMergeTargetPath, sourceModuleId );
1600
1601        // Flag to indicate if the merged module needs registering.
1602        bool shouldRegisterModule;
1603
1604        // Does the module Id exist?
1605        if ( targetModuleManager.findModuleId( sourceModuleId ) == NULL )
1606        {
1607            // No, so create module Id folder.
1608            if ( !Platform::createPath( targetModuleIdBuffer ) )
1609            {
1610                // Warn.
1611                Con::warnf("Cannot merge modules for module '%s' as the path '%s' could not be created.", sourceModuleId, targetModuleIdBuffer );
1612                return false;
1613            }
1614
1615            // Module Should be registered.
1616            shouldRegisterModule = true;
1617        }
1618        else
1619        {
1620            // Yes, so find the target module definition that matches the source module definition.
1621            ModuleDefinitionEntry::iterator targetModuleDefinitionItr = targetModuleManager.findModuleDefinition( sourceModuleId, sourceVersionId );
1622
1623            // Is there an existing target module definition entry?
1624            if ( targetModuleDefinitionItr != NULL )
1625            {
1626                // Yes, so fetch the target module definition.
1627                const ModuleDefinition* pTargetModuleDefinition = *targetModuleDefinitionItr;
1628
1629                // Fetch target module path.
1630                StringTableEntry targetModulePath = pTargetModuleDefinition->getModulePath();
1631
1632                // Yes, so we have to remove it first.
1633                if ( !Platform::deleteDirectory( targetModulePath ) )
1634                {
1635                    // Module was not deleted so warn.
1636                    Con::warnf( "Failed to remove module folder located at '%s'.  Module will be copied over.", targetModulePath );
1637                }
1638
1639                // Is the build Id being downgraded?
1640                if ( sourceBuildId < pTargetModuleDefinition->getBuildId() )
1641                {
1642                    // Yes, so warn.
1643                    Con::warnf( "Encountered a downgraded build Id for module Id '%s' at version Id '%d'.", sourceModuleId, sourceBuildId );
1644                }
1645
1646                // Module should not be registered.
1647                shouldRegisterModule = false;
1648            }
1649            else
1650            {
1651                // Module Should be registered.
1652                shouldRegisterModule = true;
1653            }
1654        }
1655
1656        // Fetch source module path.
1657        StringTableEntry sourceModulePath = pSourceModuleDefinition->getModulePath();
1658
1659        // Format target version Id folder path.
1660        char targetVersionIdBuffer[1024];
1661        dSprintf( targetVersionIdBuffer, sizeof(targetVersionIdBuffer), "%s%d", targetModuleIdBuffer, sourceVersionId );
1662
1663        // Copy module (allow overwrites as we may have failed to remove the old folder in which case this is likely to fail as well).
1664        if (!dPathCopy(sourceModulePath, targetVersionIdBuffer, false))
1665        {
1666            // Failed to copy module.
1667            Con::warnf( "Failed to copy module folder located at '%s' to location '%s'.  The modules may now be corrupted.", sourceModulePath, targetVersionIdBuffer );
1668        }
1669
1670        // Are we registering new modules and the module needs registering?
1671        if ( registerNewModules && shouldRegisterModule )
1672        {
1673            // Yes, so scan module.
1674            scanModules( targetVersionIdBuffer, true );
1675        }
1676
1677        // Is the module part of a critical merge?
1678        if ( criticalMergeOnly )
1679        {
1680            // Yes, so we need to remove the source module definition.
1681            if ( !Platform::deleteDirectory( sourceModulePath ) )
1682            {
1683                // Module was not deleted so warn.
1684                Con::warnf( "Failed to remove CRITICAL merge module folder located at '%s'.  Module will be copied over.", sourceModulePath );
1685            }
1686        }
1687    }
1688
1689    // Do we need to remove the module merge definition file?
1690    if ( removeMergeDefinition )
1691    {
1692        // Yes, so remove it.
1693        dFileDelete( moduleMergeFilePath );
1694    }
1695
1696    return true;
1697}
1698
1699//-----------------------------------------------------------------------------
1700
1701void ModuleManager::addListener( SimObject* pListener )
1702{
1703    // Sanity!
1704    AssertFatal( pListener != NULL, "Cannot add notifications to a NULL object." );
1705
1706    // Ignore if already added.
1707    if (mNotificationListeners.find( pListener) != mNotificationListeners.end())
1708        return;
1709        
1710    // Add as a listener.
1711    mNotificationListeners.addObject( pListener );
1712}
1713
1714//-----------------------------------------------------------------------------
1715
1716void ModuleManager::removeListener( SimObject* pListener )
1717{
1718    // Sanity!
1719    AssertFatal( pListener != NULL, "Cannot remove notifications from a NULL object." );
1720
1721    // Remove as a listener.
1722    mNotificationListeners.removeObject( pListener );
1723}
1724
1725//-----------------------------------------------------------------------------
1726
1727void ModuleManager::clearDatabase( void )
1728{
1729    // Lock database.
1730    AssertFatal( mDatabaseLocks == 0, "Cannot clear database if database is locked." );
1731
1732    // Iterate groups loaded.
1733    while ( mGroupsLoaded.size() > 0 )
1734    {
1735        // Unload module group.
1736        unloadModuleGroup( *mGroupsLoaded.begin() );
1737    }
1738
1739    // Iterate any other explicit modules that are loaded.
1740    while ( mModulesLoaded.size() > 0 )
1741    {
1742        // Fetch module definition.
1743        ModuleDefinition* pModuleDefinition = mModulesLoaded.begin()->mpModuleDefinition;
1744
1745        // Unload explicit module.
1746        unloadModuleExplicit( pModuleDefinition->getModuleId() );
1747    }
1748
1749    // Iterate modules to delete module definitions.
1750    for ( typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.begin(); moduleItr != mModuleIdDatabase.end(); ++moduleItr )
1751    {
1752        // Fetch modules definitions.
1753        ModuleDefinitionEntry* pDefinitions = moduleItr->value;
1754
1755        // Iterate module definitions.
1756        for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
1757        {
1758            // Fetch module definition.
1759            ModuleDefinition* pModuleDefinition = *definitionItr;
1760
1761            // Remove notification before we delete it.
1762            clearNotify( pModuleDefinition );
1763
1764            // Delete module definition.
1765            pModuleDefinition->deleteObject();
1766        }
1767
1768        // Clear definitions.
1769        delete pDefinitions;        
1770    }
1771
1772    // Clear database.
1773    mModuleIdDatabase.clear();
1774
1775    // Iterate module groups.
1776    for ( typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.begin(); moduleGroupItr != mGroupModules.end(); ++moduleGroupItr )
1777    {
1778        // Delete module group vector.
1779        delete moduleGroupItr->value;
1780    }
1781
1782    // Clear module groups.
1783    mGroupModules.clear();
1784}
1785
1786//-----------------------------------------------------------------------------
1787
1788bool ModuleManager::removeModuleDefinition( ModuleDefinition* pModuleDefinition )
1789{
1790    // Sanity!
1791    AssertFatal( pModuleDefinition != NULL, "Cannot remove module definition if it is NULL." );
1792    
1793    // Fetch module Id.
1794    StringTableEntry moduleId = pModuleDefinition->getModuleId();
1795
1796    // Is the module definition registered with this module manager?
1797    if ( pModuleDefinition->getModuleManager() != this )
1798    {
1799        // No, so warn.
1800        Con::warnf("Cannot remove module definition '%s' as it is not registered with this module manager.", moduleId );
1801        return false;
1802    }
1803
1804    // Is the module definition loaded?
1805    if ( pModuleDefinition->getLoadCount() > 0 )
1806    {
1807        // No, so warn.
1808        Con::warnf("Cannot remove module definition '%s' as it is loaded.", moduleId );
1809        return false;
1810    }
1811
1812    // Find module Id.
1813    typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.find( moduleId );
1814
1815    // Sanity!
1816    AssertFatal( moduleItr != mModuleIdDatabase.end(), "Failed to find module definition." );
1817
1818    // Fetch modules definitions.
1819    ModuleDefinitionEntry* pDefinitions = moduleItr->value;
1820
1821    // Fetch version Id.
1822    const U32 versionId = pModuleDefinition->getVersionId();
1823
1824    // Iterate module definitions.
1825    for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
1826    {
1827        // Skip if this isn't the version Id we're searching for.
1828        if ( versionId != (*definitionItr)->getVersionId() )
1829            continue;
1830
1831        // Remove definition entry.
1832        pDefinitions->erase( definitionItr ); 
1833
1834        // Remove notification before we delete it.
1835        clearNotify( pModuleDefinition );
1836
1837        // Delete module definition.
1838        pModuleDefinition->deleteObject();
1839
1840        // Are there any modules left for this module Id?
1841        if ( findModuleId( moduleId ) == NULL )
1842        {
1843            bool moduleIdFound = false;
1844
1845            // No, so remove from groups.
1846            for( typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.begin(); moduleGroupItr != mGroupModules.end(); ++moduleGroupItr )
1847            {
1848                // Fetch module Ids.
1849                typeModuleIdVector* pModuleIds = moduleGroupItr->value;
1850
1851                // Iterate module Id.
1852                for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
1853                {
1854                    // Skip if this isn't the Id.
1855                    if ( *moduleIdItr != moduleId )
1856                        continue;
1857
1858                    // Remove the module Id.
1859                    pModuleIds->erase( moduleIdItr );
1860
1861                    // Flag as found.
1862                    moduleIdFound = true;
1863
1864                    break;
1865                }
1866
1867                // Finish if found.
1868                if ( moduleIdFound )
1869                    break;
1870            }
1871        }
1872
1873        return true;
1874    }
1875
1876    // Sanity!
1877    AssertFatal( false, "Failed to find module definition." );
1878
1879    return false;
1880}
1881
1882//-----------------------------------------------------------------------------
1883
1884bool ModuleManager::registerModule( const char* pModulePath, const char* pModuleFile )
1885{
1886    // Sanity!
1887    AssertFatal( pModulePath != NULL, "Cannot scan module with NULL module path." );
1888    AssertFatal( pModuleFile != NULL, "Cannot scan module with NULL module file." );
1889
1890    // Make the module path a full-path.
1891    char fullPathBuffer[1024];
1892    Platform::makeFullPathName( pModulePath, fullPathBuffer, sizeof(fullPathBuffer) );
1893    pModulePath = fullPathBuffer;
1894
1895
1896    char formatBuffer[1024];
1897
1898    // Fetch module path trail character.
1899    char modulePathTrail = pModulePath[dStrlen(pModulePath) - 1];
1900
1901    // Format module file-path.
1902    dSprintf( formatBuffer, sizeof(formatBuffer), modulePathTrail == '/' ? "%s%s" : "%s/%s", pModulePath, pModuleFile );
1903
1904    // Read the module file.
1905    ModuleDefinition* pModuleDefinition = mTaml.read<ModuleDefinition>( formatBuffer );
1906
1907    // Did we read a module definition?
1908    if ( pModuleDefinition == NULL )
1909    {
1910        // No, so warn.
1911        Con::warnf( "Module Manager: Failed to read module definition in file '%s'.", formatBuffer );
1912        return false;
1913    }
1914
1915    // Set the module manager.
1916    pModuleDefinition->setModuleManager( this );
1917
1918    // Set module definition path.
1919    pModuleDefinition->setModulePath( pModulePath );
1920
1921    // Set module file.
1922    pModuleDefinition->setModuleFile( pModuleFile );
1923
1924    // Set module file-path.
1925    pModuleDefinition->setModuleFilePath( formatBuffer );
1926
1927    // Fetch module Id.
1928    StringTableEntry moduleId = pModuleDefinition->getModuleId();
1929
1930    // Fetch module version Id.
1931    const U32 versionId = pModuleDefinition->getVersionId();
1932
1933    // Fetch module group.
1934    StringTableEntry moduleGroup = pModuleDefinition->getModuleGroup();
1935
1936    // Fetch module type.
1937    StringTableEntry moduleType = pModuleDefinition->getModuleType();
1938
1939    // Is the module enabled?
1940    if ( !pModuleDefinition->getEnabled() )
1941    {
1942        // No, so warn.
1943        Con::warnf( "Module Manager: Found module: '%s' but it is disabled.", pModuleDefinition->getModuleFilePath() );
1944
1945        // Destroy module definition and finish.
1946        pModuleDefinition->deleteObject();
1947        return false;
1948    }
1949
1950    // Is the module Id valid?
1951    if (moduleId == StringTable->EmptyString())
1952    {
1953        // No, so warn.
1954        Con::warnf( "Module Manager: Found module: '%s' but it has an unspecified module Id.",
1955            pModuleDefinition->getModuleFilePath() );
1956
1957        // Destroy module definition and finish.
1958        pModuleDefinition->deleteObject();
1959        return false;
1960    }
1961
1962    // Is the module version Id valid?
1963    if ( versionId == 0 )
1964    {
1965        // No, so warn.
1966        Con::warnf( "Module Manager: Found Manager: Registering module: '%s' but it has an invalid Version Id of '0'.",
1967            pModuleDefinition->getModuleFilePath() );
1968
1969        // Destroy module definition and finish.
1970        pModuleDefinition->deleteObject();
1971        return false;
1972    }
1973
1974    // Is the module group already loaded?
1975    if ( findGroupLoaded( moduleGroup ) != NULL )
1976    {
1977        // Yes, so warn.
1978        Con::warnf( "Module Manager: Found module: '%s' but it is in a module group '%s' which has already been loaded.",
1979            pModuleDefinition->getModuleFilePath(),
1980            moduleGroup );
1981
1982        // Destroy module definition and finish.
1983        pModuleDefinition->deleteObject();
1984        return false;
1985    }
1986
1987    // Was a script-file specified?
1988    if ( pModuleDefinition->getScriptFile() != StringTable->EmptyString() )
1989    {
1990        // Yes, so format module script file-path.
1991        dSprintf( formatBuffer, sizeof(formatBuffer), modulePathTrail == '/' ? "%s%s" : "%s/%s", pModulePath, pModuleDefinition->getScriptFile() );
1992        pModuleDefinition->setModuleScriptFilePath( formatBuffer );
1993    }
1994
1995    // Format module signature,
1996    dSprintf( formatBuffer, sizeof(formatBuffer), "%s_%d_%d", moduleId, versionId, pModuleDefinition->getBuildId() );
1997    pModuleDefinition->setSignature( formatBuffer );
1998
1999    // Locked the module definition.
2000    pModuleDefinition->setLocked( true );
2001
2002    // Fetch modules definitions.
2003    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
2004
2005    // Did we find the module Id?
2006    if ( pDefinitions != NULL )
2007    {
2008        // Yes, so find the module definition.
2009        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
2010
2011        // Does this version Id already exist?
2012        if ( definitionItr != NULL )
2013        {
2014            // Yes, so warn.
2015            Con::warnf( "Module Manager: Found module: '%s' but it is already registered as module Id '%s' at version Id '%d'.",
2016                pModuleDefinition->getModuleFilePath(), moduleId, versionId );
2017
2018            // Destroy module definition and finish.
2019            pModuleDefinition->deleteObject();
2020            return false;
2021        }
2022
2023        // Is the module group the same as the module definitions we already have?
2024        if ( moduleGroup != pDefinitions->mModuleGroup )
2025        {
2026            // No, so warn.
2027            Con::warnf( "Module Manager: Found module: '%s' but its module group '%s' is not the same as other module definitions of the same module Id.",
2028                pModuleDefinition->getModuleFilePath(), moduleGroup );
2029
2030            // Destroy module definition and finish.
2031            pModuleDefinition->deleteObject();
2032            return false;
2033        }
2034
2035        // Is the module type the same as the module definitions we already have?
2036        if ( moduleType != pDefinitions->mModuleType )
2037        {
2038            // No, so warn.
2039            Con::warnf( "Module Manager: Found module: '%s' but its module type '%s' is not the same as other module definitions of the same module Id.",
2040                pModuleDefinition->getModuleFilePath(), moduleGroup );
2041
2042            // Destroy module definition and finish.
2043            pModuleDefinition->deleteObject();
2044            return false;
2045        }
2046    }
2047    else
2048    {
2049        // No, so create a vector of definitions.
2050        pDefinitions = new ModuleDefinitionEntry( moduleId, moduleGroup, moduleType );
2051
2052        // Insert module Id definitions.
2053        mModuleIdDatabase.insert( moduleId, pDefinitions );
2054    }
2055    
2056    // Add module definition.
2057    pDefinitions->push_back( pModuleDefinition );
2058
2059    // Sort module definitions by version Id so that higher versions appear first.
2060    dQsort( pDefinitions->address(), pDefinitions->size(), sizeof(ModuleDefinition*), moduleDefinitionVersionIdSort );
2061
2062    // Find module group.
2063    typeGroupModuleHash::iterator moduleGroupItr = mGroupModules.find( moduleGroup );
2064
2065    // Did we find the module group?
2066    if ( moduleGroupItr != mGroupModules.end() )
2067    {
2068        // Yes, so fetch module Ids.
2069        typeModuleIdVector* pModuleIds = moduleGroupItr->value;
2070
2071        // Is the module Id already present?
2072        bool moduleIdFound = false;
2073        for( typeModuleIdVector::iterator moduleIdItr = pModuleIds->begin(); moduleIdItr != pModuleIds->end(); ++moduleIdItr )
2074        {
2075            // Skip if this isn't the Id.
2076            if ( *moduleIdItr != moduleId )
2077                continue;
2078
2079            // Flag as found.
2080            moduleIdFound = true;
2081            break;
2082        }
2083
2084        // Add if module Id was not found.
2085        if ( !moduleIdFound )
2086            pModuleIds->push_back( moduleId );
2087    }
2088    else
2089    {
2090        // No, so insert a module Id vector.
2091        moduleGroupItr = mGroupModules.insert( pModuleDefinition->getModuleGroup(), new typeModuleIdVector() );
2092
2093        // Add module Id.
2094        moduleGroupItr->value->push_back( moduleId );
2095    }
2096
2097    // Notify if the module definition is destroyed.
2098    deleteNotify( pModuleDefinition );
2099
2100    // Info.
2101    if ( mEchoInfo )
2102    {
2103#if 1
2104        Con::printf( "Module Manager: Registering: '%s' [ ID='%s', VersionId='%d', BuildId='%d', Description='%s' ].",
2105            pModuleDefinition->getModuleFilePath(),
2106            pModuleDefinition->getModuleId(),
2107            pModuleDefinition->getVersionId(),
2108            pModuleDefinition->getBuildId(),
2109            pModuleDefinition->getModuleDescription()
2110            );
2111#else
2112        Con::printf( "Module Manager: Registering: '%s' [ ID='%s', VersionId='%d', BuildId='%d', Description='%s', Group='%s', Dependencies='%s', ScriptFile='%s', CreateFunction='%s', DestroyFunction='%s' ].",
2113            pModuleDefinition->getModuleFilePath(),
2114            pModuleDefinition->getModuleId(),
2115            pModuleDefinition->getVersionId(),
2116            pModuleDefinition->getBuildId(),
2117            pModuleDefinition->getModuleDescription(),
2118            pModuleDefinition->getModuleGroup(),
2119            pModuleDefinition->getDataField( StringTable->insert("Dependencies"), NULL ),
2120            pModuleDefinition->getScriptFile(),
2121            pModuleDefinition->getCreateFunction(),
2122            pModuleDefinition->getDestroyFunction()
2123            );
2124#endif
2125    }
2126
2127    // Emit notifications.
2128    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
2129    {
2130        Con::executef( *notifyItr, "onModuleRegister", pModuleDefinition->getIdString() );
2131    }
2132
2133    return true;
2134}
2135
2136//-----------------------------------------------------------------------------
2137
2138bool ModuleManager::unregisterModule( const char* pModuleId, const U32 versionId )
2139{
2140    // Sanity!
2141    AssertFatal( pModuleId != NULL, "A module Id cannot be NULL." );
2142
2143    // Fetch module Id.
2144    StringTableEntry moduleId = StringTable->insert( pModuleId );
2145
2146    // Find the module definition.
2147    ModuleDefinition* pModuleDefinition = findModule( pModuleId, versionId );
2148
2149    // Did we find the module definition?
2150    if ( pModuleDefinition == NULL )
2151    {
2152        // No, so warn.
2153        Con::warnf( "Module Manager: Cannot unregister module Id '%s' as it is not registered.", moduleId );
2154        return false;
2155    }
2156
2157    // Remove the module definition.
2158    return removeModuleDefinition( pModuleDefinition );
2159}
2160
2161//-----------------------------------------------------------------------------
2162
2163void ModuleManager::raiseModulePreLoadNotifications( ModuleDefinition* pModuleDefinition )
2164{
2165    // Raise notifications.
2166    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
2167    {
2168        // Fetch listener object.
2169        SimObject* pListener = *notifyItr;
2170
2171        // Perform object callback.
2172        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
2173        if ( pCallbacks != NULL )
2174            pCallbacks->onModulePreLoad( pModuleDefinition );             
2175            
2176        // Perform script callback.
2177        if ( pListener->isMethod( "onModulePreLoad" ) )
2178            Con::executef( pListener, "onModulePreLoad", pModuleDefinition->getIdString() );
2179    }
2180}
2181
2182//-----------------------------------------------------------------------------
2183
2184void ModuleManager::raiseModulePostLoadNotifications( ModuleDefinition* pModuleDefinition )
2185{
2186    // Raise notifications.
2187    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
2188    {
2189        // Fetch listener object.
2190        SimObject* pListener = *notifyItr;
2191
2192        // Perform object callback.
2193        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
2194        if ( pCallbacks != NULL )
2195            pCallbacks->onModulePostLoad( pModuleDefinition );             
2196            
2197        // Perform script callback.
2198        if ( pListener->isMethod( "onModulePostLoad" ) )
2199            Con::executef( pListener, "onModulePostLoad", pModuleDefinition->getIdString() );
2200    }
2201}
2202
2203//-----------------------------------------------------------------------------
2204
2205void ModuleManager::raiseModulePreUnloadNotifications( ModuleDefinition* pModuleDefinition )
2206{
2207    // Raise notifications.
2208    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
2209    {
2210        // Fetch listener object.
2211        SimObject* pListener = *notifyItr;
2212
2213        // Perform object callback.
2214        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
2215        if ( pCallbacks != NULL )
2216            pCallbacks->onModulePreUnload( pModuleDefinition );             
2217            
2218        // Perform script callback.
2219        if ( pListener->isMethod( "onModulePreUnload" ) )
2220            Con::executef( pListener, "onModulePreUnload", pModuleDefinition->getIdString() );
2221    }
2222}
2223
2224//-----------------------------------------------------------------------------
2225
2226void ModuleManager::raiseModulePostUnloadNotifications( ModuleDefinition* pModuleDefinition )
2227{
2228    // Raise notifications.
2229    for( SimSet::iterator notifyItr = mNotificationListeners.begin(); notifyItr != mNotificationListeners.end(); ++notifyItr )
2230    {
2231        // Fetch listener object.
2232        SimObject* pListener = *notifyItr;
2233
2234        // Perform object callback.
2235        ModuleCallbacks* pCallbacks = dynamic_cast<ModuleCallbacks*>( pListener );
2236        if ( pCallbacks != NULL )
2237            pCallbacks->onModulePostUnload( pModuleDefinition );             
2238            
2239        // Perform script callback.
2240        if ( pListener->isMethod( "onModulePostUnload" ) )
2241            Con::executef( pListener, "onModulePostUnload", pModuleDefinition->getIdString() );
2242    }
2243}
2244
2245//-----------------------------------------------------------------------------
2246
2247ModuleManager::ModuleDefinitionEntry* ModuleManager::findModuleId( StringTableEntry moduleId )
2248{
2249    // Sanity!
2250    AssertFatal( moduleId != NULL, "A module Id cannot be NULL." );
2251
2252    // Is the module Id valid?
2253    if ( moduleId == StringTable->EmptyString() )
2254    {
2255        // No, so warn.
2256        Con::warnf( "Module Manager: Invalid Module Id." );
2257        return NULL;
2258    }
2259
2260    // Find module Id.
2261    typeModuleIdDatabaseHash::iterator moduleItr = mModuleIdDatabase.find( moduleId );
2262
2263    // Return appropriately.
2264    return moduleItr != mModuleIdDatabase.end() ? moduleItr->value : NULL;
2265}
2266
2267//-----------------------------------------------------------------------------
2268
2269ModuleManager::ModuleDefinitionEntry::iterator ModuleManager::findModuleDefinition( StringTableEntry moduleId, const U32 versionId )
2270{
2271    // Fetch modules definitions.
2272    ModuleDefinitionEntry* pDefinitions = findModuleId( moduleId );
2273
2274    // Finish if no module definitions for the module Id.
2275    if ( pDefinitions == NULL )
2276        return NULL;
2277
2278    // Iterate module definitions.
2279    for ( ModuleDefinitionEntry::iterator definitionItr = pDefinitions->begin(); definitionItr != pDefinitions->end(); ++definitionItr )
2280    {
2281        // Skip if this isn't the version Id we're searching for.
2282        if ( versionId != (*definitionItr)->getVersionId() )
2283            continue;
2284
2285        // Return module definition iterator.
2286        return definitionItr;
2287    }
2288
2289    // Not found.
2290    return NULL;
2291}
2292
2293//-----------------------------------------------------------------------------
2294
2295bool ModuleManager::resolveModuleDependencies( StringTableEntry moduleId, const U32 versionId, StringTableEntry moduleGroup, bool synchronizedOnly, typeModuleLoadEntryVector& moduleResolvingQueue, typeModuleLoadEntryVector& moduleReadyQueue )
2296{
2297    // Fetch the module Id ready entry.
2298    ModuleLoadEntry* pLoadReadyEntry = findModuleReady( moduleId, moduleReadyQueue );
2299
2300    // Is there a load entry?
2301    if ( pLoadReadyEntry )
2302    {
2303        // Yes, so finish if the version Id is not important,
2304        if ( versionId == 0 )
2305            return true;
2306
2307        // Finish if the version Id are compatible.
2308        if ( versionId == pLoadReadyEntry->mpModuleDefinition->getVersionId() )
2309            return true;
2310
2311        // Is it a strict version Id?
2312        if ( pLoadReadyEntry->mStrictVersionId )
2313        {
2314            // Yes, so warn.
2315            Con::warnf( "Module Manager: A module dependency was detected loading module Id '%s' at version Id '%d' in group '%s' but an version Id '%d' is also required.",
2316                moduleId, versionId, pLoadReadyEntry->mpModuleDefinition->getVersionId(), moduleGroup );
2317            return false;
2318        }
2319
2320        // No, so find the required module version Id.
2321        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
2322
2323        // Did we find the requested module definition.
2324        if ( definitionItr == NULL )
2325        {
2326            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
2327            if ( !mEnforceDependencies )
2328                return true;
2329
2330            // Warn!
2331            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
2332                moduleId, versionId, moduleGroup );
2333            return false;
2334        }
2335
2336        // Set the new module definition.
2337        pLoadReadyEntry->mpModuleDefinition = *definitionItr;
2338
2339        // Set strict version Id.
2340        pLoadReadyEntry->mStrictVersionId = true;
2341                
2342        return true;
2343    }
2344
2345    // Is the module Id load resolving?
2346    if ( findModuleResolving( moduleId, moduleResolvingQueue ) != NULL )
2347    {
2348        // Yes, so a cycle has been detected so warn.
2349        Con::warnf( "Module Manager: A cyclic dependency was detected resolving module Id '%s' at version Id '%d' in group '%s'.",
2350            moduleId, versionId, moduleGroup );
2351        return false;
2352    }
2353
2354    // Reset selected module definition.
2355    ModuleDefinition* pSelectedModuleDefinition = NULL;
2356
2357    // Do we want the latest version Id?
2358    if ( versionId == 0 )
2359    {
2360        // Yes, so find the module Id.
2361        typeModuleIdDatabaseHash::iterator moduleIdItr = mModuleIdDatabase.find( moduleId );
2362
2363        // Did we find the module Id?
2364        if ( moduleIdItr == mModuleIdDatabase.end() )
2365        {
2366            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
2367            if ( !mEnforceDependencies )
2368                return true;
2369
2370            // Warn!
2371            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
2372                moduleId, versionId, moduleGroup );
2373            return false;
2374        }
2375
2376        // Fetch first module definition which should be the highest version Id.
2377        pSelectedModuleDefinition = (*moduleIdItr->value)[0];
2378    }
2379    else
2380    {
2381        // No, so find the module Id at the specific version Id.
2382        ModuleDefinitionEntry::iterator definitionItr = findModuleDefinition( moduleId, versionId );
2383
2384        // Did we find the module definition?
2385        if ( definitionItr == NULL )
2386        {
2387            // No, so we can safely ignore the missing dependency if we're not enforcing dependencies.
2388            if ( !mEnforceDependencies )
2389                return true;
2390
2391            // Warn!
2392            Con::warnf( "Module Manager: A missing module dependency was detected loading module Id '%s' at version Id '%d' in group '%s'.",
2393                moduleId, versionId, moduleGroup );
2394            return false;
2395        }
2396
2397        // Select the module definition.
2398        pSelectedModuleDefinition = *definitionItr;
2399    }
2400
2401    // If we're only resolving synchronized modules and the module is not synchronized then finish.
2402    if ( synchronizedOnly && !pSelectedModuleDefinition->getSynchronized() )
2403        return true;
2404
2405    // Create a load entry.
2406    ModuleLoadEntry loadEntry( pSelectedModuleDefinition, false );
2407
2408    // Fetch module dependencies.
2409    const ModuleDefinition::typeModuleDependencyVector& moduleDependencies = pSelectedModuleDefinition->getDependencies();
2410
2411    // Do we have any module dependencies?
2412    if ( moduleDependencies.size() > 0 )
2413    {
2414        // Yes, so queue this module as resolving.
2415        moduleResolvingQueue.push_back( loadEntry );
2416
2417        // Iterate module dependencies.
2418        for( ModuleDefinition::typeModuleDependencyVector::const_iterator dependencyItr = moduleDependencies.begin(); dependencyItr != moduleDependencies.end(); ++dependencyItr )
2419        {            
2420            // Finish if we could not the dependent module Id at the version Id.
2421            if ( !resolveModuleDependencies( dependencyItr->mModuleId, dependencyItr->mVersionId, moduleGroup, synchronizedOnly, moduleResolvingQueue, moduleReadyQueue ) )
2422                return false;
2423        }
2424
2425        // Remove module as resolving.
2426        moduleResolvingQueue.pop_back();
2427    }
2428
2429    // Queue module as ready.
2430    moduleReadyQueue.push_back( loadEntry );        
2431
2432    return true;
2433}
2434
2435//-----------------------------------------------------------------------------
2436
2437ModuleManager::ModuleLoadEntry* ModuleManager::findModuleResolving( StringTableEntry moduleId, typeModuleLoadEntryVector& moduleResolvingQueue )
2438{
2439    // Iterate module load resolving queue.
2440    for( typeModuleLoadEntryVector::iterator loadEntryItr = moduleResolvingQueue.begin(); loadEntryItr != moduleResolvingQueue.end(); ++loadEntryItr )
2441    {
2442        // Finish if found.
2443        if ( moduleId == loadEntryItr->mpModuleDefinition->getModuleId() )
2444            return loadEntryItr;
2445    }
2446
2447    // Not found.
2448    return NULL;
2449}
2450
2451//-----------------------------------------------------------------------------
2452
2453ModuleManager::ModuleLoadEntry* ModuleManager::findModuleReady( StringTableEntry moduleId, typeModuleLoadEntryVector& moduleReadyQueue )
2454{
2455    // Iterate module load ready queue.
2456    for( typeModuleLoadEntryVector::iterator loadEntryItr = moduleReadyQueue.begin(); loadEntryItr != moduleReadyQueue.end(); ++loadEntryItr )
2457    {
2458        // Finish if found.
2459        if ( moduleId == loadEntryItr->mpModuleDefinition->getModuleId() )
2460            return loadEntryItr;
2461    }
2462
2463    // Not found.
2464    return NULL;
2465}
2466
2467//-----------------------------------------------------------------------------
2468
2469ModuleManager::typeModuleLoadEntryVector::iterator ModuleManager::findModuleLoaded( StringTableEntry moduleId, const U32 versionId )
2470{
2471    // Iterate module loaded queue.
2472    for( typeModuleLoadEntryVector::iterator loadEntryItr = mModulesLoaded.begin(); loadEntryItr != mModulesLoaded.end(); ++loadEntryItr )
2473    {
2474        // Skip if not the module Id we're looking for.
2475        if ( moduleId != loadEntryItr->mpModuleDefinition->getModuleId() )
2476            continue;
2477
2478        // Skip if we are searching for a specific version and it does not match.
2479        if ( versionId != 0 && versionId != loadEntryItr->mpModuleDefinition->getVersionId() )
2480            continue;
2481
2482        return loadEntryItr;
2483    }
2484
2485    // Not found.
2486    return NULL;
2487}
2488
2489//-----------------------------------------------------------------------------
2490
2491ModuleManager::typeGroupVector::iterator ModuleManager::findGroupLoaded( StringTableEntry moduleGroup )
2492{
2493    // Iterate groups loaded queue.
2494    for( typeGroupVector::iterator groupsLoadedItr = mGroupsLoaded.begin(); groupsLoadedItr != mGroupsLoaded.end(); ++groupsLoadedItr )
2495    {
2496        // Finish if found.
2497        if ( moduleGroup == *groupsLoadedItr )
2498            return groupsLoadedItr;
2499    }
2500
2501    // Not found.
2502    return NULL;
2503}
2504
2505//-----------------------------------------------------------------------------
2506
2507StringTableEntry ModuleManager::getModuleMergeFilePath( void ) const
2508{
2509    // Format merge file path.
2510    char filePathBuffer[1024];
2511    dSprintf( filePathBuffer, sizeof(filePathBuffer), "%s/%s", Platform::getExecutablePath(), MODULE_MANAGER_MERGE_FILE );
2512
2513    return StringTable->insert( filePathBuffer );
2514}
2515