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)

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