Browse Source

Merge pull request #299 from Areloch/LevelDependencyLoadingAndFixes

Level Asset Dependency handling and various level fixes
Brian Roberts 5 years ago
parent
commit
8e61600a82

+ 119 - 0
Engine/source/T3D/Scene.cpp

@@ -1,4 +1,5 @@
 #include "Scene.h"
+#include "T3D/assets/LevelAsset.h"
 
 Scene * Scene::smRootScene = nullptr;
 Vector<Scene*> Scene::smSceneList;
@@ -180,6 +181,93 @@ void Scene::unpackUpdate(NetConnection *conn, BitStream *stream)
 
 }
 
+void Scene::dumpUtilizedAssets()
+{
+   Con::printf("Dumping utilized assets in scene!");
+
+   Vector<StringTableEntry> utilizedAssetsList;
+   for (U32 i = 0; i < mPermanentObjects.size(); i++)
+   {
+      mPermanentObjects[i]->getUtilizedAssets(&utilizedAssetsList);
+   }
+
+   for (U32 i = 0; i < mDynamicObjects.size(); i++)
+   {
+      mDynamicObjects[i]->getUtilizedAssets(&utilizedAssetsList);
+   }
+
+   for (U32 i = 0; i < utilizedAssetsList.size(); i++)
+   {
+      Con::printf("Utilized Asset: %s", utilizedAssetsList[i]);
+   }
+
+   Con::printf("Utilized Asset dump complete!");
+}
+
+StringTableEntry Scene::getOriginatingFile()
+{
+   return getFilename();
+}
+
+StringTableEntry Scene::getLevelAsset()
+{
+   StringTableEntry levelFile = getFilename();
+
+   if (levelFile == StringTable->EmptyString())
+      return StringTable->EmptyString();
+
+   AssetQuery* query = new AssetQuery();
+   query->registerObject();
+
+   S32 foundAssetcount = AssetDatabase.findAssetLooseFile(query, levelFile);
+   if (foundAssetcount == 0)
+      return StringTable->EmptyString();
+   else
+      return query->mAssetList[0];
+}
+
+bool Scene::saveScene(StringTableEntry fileName)
+{
+   //So, we ultimately want to not only save out the level, but also collate all the assets utilized
+   //by the static objects in the scene so we can have those before we parse the level file itself
+   //Useful for preloading or stat tracking
+
+   //First, save the level file
+   if (fileName == StringTable->EmptyString())
+   {
+      fileName = getOriginatingFile();
+   }
+
+   bool saveSuccess = save(fileName);
+
+   if (!saveSuccess)
+      return false;
+
+   //Get the level asset
+   StringTableEntry levelAsset = getLevelAsset();
+   if (levelAsset == StringTable->EmptyString())
+      return saveSuccess;
+
+   LevelAsset* levelAssetDef = AssetDatabase.acquireAsset<LevelAsset>(levelAsset);
+   levelAssetDef->clearAssetDependencyFields("staticObjectAssetDependency");
+
+   //Next, lets build out our 
+   Vector<StringTableEntry> utilizedAssetsList;
+   for (U32 i = 0; i < mPermanentObjects.size(); i++)
+   {
+      mPermanentObjects[i]->getUtilizedAssets(&utilizedAssetsList);
+   }
+
+   for (U32 i = 0; i < utilizedAssetsList.size(); i++)
+   {
+      levelAssetDef->addAssetDependencyField("staticObjectAssetDependency", utilizedAssetsList[i]);
+   }
+
+   saveSuccess = levelAssetDef->saveAsset();
+
+   return saveSuccess;
+}
+
 //
 Vector<SceneObject*> Scene::getObjectsByClass(String className, bool checkSubscenes)
 {
@@ -251,3 +339,34 @@ DefineEngineMethod(Scene, getObjectsByClass, String, (String className), (""),
    //return object->getObjectsByClass(className);
    return "";
 }
+
+DefineEngineMethod(Scene, dumpUtilizedAssets, void, (), ,
+   "Get the root Scene object that is loaded.\n"
+   "@return The id of the Root Scene. Will be 0 if no root scene is loaded")
+{
+   object->dumpUtilizedAssets();
+}
+
+DefineEngineMethod(Scene, getOriginatingFile, const char*, (), ,
+   "Get the root Scene object that is loaded.\n"
+   "@return The id of the Root Scene. Will be 0 if no root scene is loaded")
+{
+   return object->getOriginatingFile();
+}
+
+DefineEngineMethod(Scene, getLevelAsset, const char*, (), ,
+   "Get the root Scene object that is loaded.\n"
+   "@return The id of the Root Scene. Will be 0 if no root scene is loaded")
+{
+   return object->getLevelAsset();
+}
+
+DefineEngineMethod(Scene, save, bool, (const char* fileName), (""),
+   "Save out the object to the given file.\n"
+   "@param fileName The name of the file to save to."
+   "@param selectedOnly If true, only objects marked as selected will be saved out.\n"
+   "@param preAppendString Text which will be preprended directly to the object serialization.\n"
+   "@param True on success, false on failure.")
+{
+   return object->saveScene(StringTable->insert(fileName));
+}

+ 7 - 0
Engine/source/T3D/Scene.h

@@ -62,6 +62,13 @@ public:
    void addDynamicObject(SceneObject* object);
    void removeDynamicObject(SceneObject* object);
 
+   void dumpUtilizedAssets();
+
+   StringTableEntry getOriginatingFile();
+   StringTableEntry getLevelAsset();
+
+   bool saveScene(StringTableEntry fileName);
+
    //
    //Networking
    U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream);

+ 43 - 0
Engine/source/T3D/assets/LevelAsset.cpp

@@ -330,9 +330,52 @@ void LevelAsset::setNavmeshFile(const char* pNavmeshFile)
    refreshAsset();
 }
 
+void LevelAsset::loadDependencies()
+{
+   //First, load any material, animation, etc assets we may be referencing in our asset
+   // Find any asset dependencies.
+   AssetManager::typeAssetDependsOnHash::Iterator assetDependenciesItr = mpOwningAssetManager->getDependedOnAssets()->find(mpAssetDefinition->mAssetId);
+
+   // Does the asset have any dependencies?
+   if (assetDependenciesItr != mpOwningAssetManager->getDependedOnAssets()->end())
+   {
+      // Iterate all dependencies.
+      while (assetDependenciesItr != mpOwningAssetManager->getDependedOnAssets()->end() && assetDependenciesItr->key == mpAssetDefinition->mAssetId)
+      {
+         //Force it to be loaded by acquiring it
+         StringTableEntry assetId = assetDependenciesItr->value;
+         mAssetDependencies.push_back(AssetDatabase.acquireAsset<AssetBase>(assetId));
+
+         // Next dependency.
+         assetDependenciesItr++;
+      }
+   }
+}
+
+void LevelAsset::unloadDependencies()
+{
+   for (U32 i = 0; i < mAssetDependencies.size(); i++)
+   {
+      AssetBase* assetDef = mAssetDependencies[i];
+      AssetDatabase.releaseAsset(assetDef->getAssetId());
+   }
+}
+
 DefineEngineMethod(LevelAsset, getLevelFile, const char*, (),,
    "Creates a new script asset using the targetFilePath.\n"
    "@return The bool result of calling exec")
 {
    return object->getLevelPath();
 }
+
+DefineEngineMethod(LevelAsset, loadDependencies, void, (), ,
+   "Initiates the loading of asset dependencies for this level.")
+{
+   return object->loadDependencies();
+}
+
+DefineEngineMethod(LevelAsset, unloadDependencies, void, (), ,
+   "Initiates the unloading of previously loaded asset dependencies for this level.")
+{
+   return object->unloadDependencies();
+}

+ 5 - 0
Engine/source/T3D/assets/LevelAsset.h

@@ -67,6 +67,8 @@ class LevelAsset : public AssetBase
 
    StringTableEntry        mGamemodeName;
 
+   Vector<AssetBase*>      mAssetDependencies;
+
 public:
    LevelAsset();
    virtual ~LevelAsset();
@@ -78,6 +80,9 @@ public:
    /// Declare Console Object.
    DECLARE_CONOBJECT(LevelAsset);
 
+   void loadDependencies();
+   void unloadDependencies();
+
    void                    setLevelFile(const char* pImageFile);
    inline StringTableEntry getLevelFile(void) const { return mLevelFile; };
    void                    setPostFXPresetFile(const char* pPostFXPresetFile);

+ 0 - 1
Engine/source/T3D/assets/TerrainAsset.cpp

@@ -221,7 +221,6 @@ bool TerrainAsset::loadTerrain()
 
             //Force the asset to become initialized if it hasn't been already
             AssetPtr<TerrainMaterialAsset> matAsset = assetId;
-
             mTerrMaterialAssets.push_front(matAsset);
          }
 

+ 6 - 0
Engine/source/T3D/tsStatic.cpp

@@ -1691,6 +1691,12 @@ void TSStatic::updateMaterials()
    mShapeInstance->initMaterialList();
 }
 
+void TSStatic::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
+{
+   if(!mShapeAsset.isNull())
+      usedAssetsList->push_back_unique(mShapeAssetId);
+}
+
 //------------------------------------------------------------------------
 //These functions are duplicated in tsStatic and shapeBase.
 //They each function a little differently; but achieve the same purpose of gathering

+ 2 - 0
Engine/source/T3D/tsStatic.h

@@ -286,6 +286,8 @@ public:
 
    bool isAnimated() { return mPlayAmbient; }
 
+   virtual void getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList);
+
 private:
    virtual void   onStaticModified(const char* slotName, const char* newValue = NULL);
 protected:

+ 13 - 21
Engine/source/assets/assetManager.cpp

@@ -253,30 +253,22 @@ bool AssetManager::loadModuleAutoLoadAssets(ModuleDefinition* pModuleDefinition)
 
          if (assetDef->mAssetType == pAutoloadAssets->getAssetType())
          {
-            //TODO: this is stupid and ugly, need to properly automagically parse the class for registration
-            AssetBase* assetBase = nullptr;
+            String assetPath = assetDef->mAssetBaseFilePath;
+            assetPath.replace("//", "/");
 
-            if (assetDef->mAssetType == StringTable->insert("GUIAsset"))
-            {
-               assetBase = mTaml.read<GUIAsset>(assetDef->mAssetBaseFilePath);
-            }
-            else if (assetDef->mAssetType == StringTable->insert("ScriptAsset"))
-            {
-               assetBase = mTaml.read<ScriptAsset>(assetDef->mAssetBaseFilePath);
-            }
-            else if (assetDef->mAssetType == StringTable->insert("MaterialAsset"))
-            {
-               assetBase = mTaml.read<MaterialAsset>(assetDef->mAssetBaseFilePath);
-            }
-            else if (assetDef->mAssetType == StringTable->insert("GameObjectAsset"))
-            {
-               assetBase = mTaml.read<GameObjectAsset>(assetDef->mAssetBaseFilePath);
-            }
+            String autoLoadPath = pModuleDefinition->getModulePath();
+            autoLoadPath += "/";
+            autoLoadPath += pAutoloadAssets->getPath();
 
-            //load the asset now if valid
-            if (assetBase)
+            if (pAutoloadAssets->getPath() == StringTable->EmptyString() || assetPath.startsWith(autoLoadPath.c_str()))
             {
-               assetBase->setOwned(this, assetDef);
+               AssetBase* assetBase = dynamic_cast<AssetBase*>(mTaml.read(assetDef->mAssetBaseFilePath));
+
+               //load the asset now if valid
+               if (assetBase)
+               {
+                  assetBase->setOwned(this, assetDef);
+               }
             }
          }
       }

+ 2 - 0
Engine/source/scene/sceneObject.h

@@ -942,6 +942,8 @@ class SceneObject : public NetObject, private SceneContainer::Link, public Proce
    /// notification that a direct child object has been detached
    virtual void onLostChild(SceneObject *subObject);   
 // PATHSHAPE END
+
+   virtual void getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList) {}
 };
 
 #endif  // _SCENEOBJECT_H_

+ 5 - 0
Engine/source/terrain/terrData.cpp

@@ -1420,6 +1420,11 @@ void TerrainBlock::getMinMaxHeight( F32 *minHeight, F32 *maxHeight ) const
    *maxHeight = fixedToFloat( sq->maxHeight );
 }
 
+void TerrainBlock::getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList)
+{
+   if (!mTerrainAsset.isNull())
+      usedAssetsList->push_back_unique(mTerrainAssetId);
+}
 //-----------------------------------------------------------------------------
 // Console Methods
 //-----------------------------------------------------------------------------

+ 2 - 0
Engine/source/terrain/terrData.h

@@ -474,6 +474,8 @@ public:
    U32 packUpdate   (NetConnection *conn, U32 mask, BitStream *stream);
    void unpackUpdate(NetConnection *conn,           BitStream *stream);
    void inspectPostApply();
+
+   virtual void getUtilizedAssets(Vector<StringTableEntry>* usedAssetsList);
  
 protected:
    bool mIgnoreZodiacs;

+ 4 - 4
Templates/BaseGame/game/core/clientServer/Core_ClientServer.cs

@@ -60,11 +60,11 @@ function Core_ClientServer::onDestroy( %this )
 }
 
 //-----------------------------------------------------------------------------
-function StartGame( %mission, %hostingType )
+function StartGame( %levelAsset, %hostingType )
 {
-   if( %mission $= "" )
+   if( %levelAsset $= "" )
    {
-      %mission = $selectedLevelFile;
+      %levelAsset = $selectedLevelAsset;
    }
 
    if (%hostingType !$= "")
@@ -88,7 +88,7 @@ function StartGame( %mission, %hostingType )
       Canvas.repaint();
    }
 
-   createAndConnectToLocalServer( %serverType, %mission );
+   createAndConnectToLocalServer( %serverType, %levelAsset );
 }
 
 function JoinGame( %serverIndex )

+ 11 - 4
Templates/BaseGame/game/core/clientServer/scripts/server/levelLoad.cs

@@ -33,22 +33,26 @@ $MissionLoadPause = 5000;
 
 //-----------------------------------------------------------------------------
 //This is the first call made by the server to kick the loading process off
-function loadMission( %missionName, %isFirstMission ) 
+function loadMission( %levelAsset, %isFirstMission ) 
 {
    endMission();
-   echo("*** LOADING MISSION: " @ %missionName);
+   $Server::LevelAsset = AssetDatabase.acquireAsset(%levelAsset);
+   
+   echo("*** LOADING MISSION: " @ $Server::LevelAsset.LevelName);
    echo("*** Stage 1 load");
 
    // increment the mission sequence (used for ghost sequencing)
    $missionSequence++;
    $missionRunning = false;
-   $Server::MissionFile = %missionName;
+   $Server::MissionFile = $Server::LevelAsset.getLevelFile();
    $Server::LoadFailMsg = "";
+   
+   $Server::LevelAsset.loadDependencies();
 
    // Extract mission info from the mission file,
    // including the display name and stuff to send
    // to the client.
-   buildLoadInfo( %missionName );
+   buildLoadInfo( $Server::MissionFile );
 
    // Download mission info to the clients
    %count = ClientGroup.getCount();
@@ -163,6 +167,9 @@ function endMission()
    getScene(0).delete();
    MissionCleanup.delete();
    
+   if(isObject($Server::LevelAsset))
+      AssetDatabase.releaseAsset($Server::LevelAsset.getAssetId()); //cleanup
+   
   if ($Pref::Server::EnableDatablockCache)
     resetDatablockCache();
    DatablockFilesList.empty();

+ 6 - 6
Templates/BaseGame/game/core/clientServer/scripts/server/server.cs

@@ -93,9 +93,9 @@ function portInit(%port)
 /// create a local client connection to the server.
 //
 /// @return true if successful.
-function createAndConnectToLocalServer( %serverType, %level )
+function createAndConnectToLocalServer( %serverType, %levelAsset )
 {
-   if( !createServer( %serverType, %level ) )
+   if( !createServer( %serverType, %levelAsset ) )
       return false;
    
    %conn = new GameConnection( ServerConnection );
@@ -124,7 +124,7 @@ function createAndConnectToLocalServer( %serverType, %level )
 
 /// Create a server with either a "SinglePlayer" or "MultiPlayer" type
 /// Specify the level to load on the server
-function createServer(%serverType, %level)
+function createServer(%serverType, %levelAsset)
 {
    if($Game::firstTimeServerRun == true)
    {
@@ -135,7 +135,7 @@ function createServer(%serverType, %level)
    // working with the server session we think we are.
    $Server::Session++;
    
-   if (%level $= "")
+   if (%levelAsset $= "")
    {
       error("createServer(): level name unspecified");
       return false;
@@ -143,7 +143,7 @@ function createServer(%serverType, %level)
    
    // Make sure our level name is relative so that it can send
    // across the network correctly
-   %level = makeRelativePath(%level, getWorkingDirectory());
+   //%level = makeRelativePath(%level, getWorkingDirectory());
 
    destroyServer();
 
@@ -176,7 +176,7 @@ function createServer(%serverType, %level)
    // the server has been created
    onServerCreated();
 
-   loadMission(%level, true);
+   loadMission(%levelAsset, true);
    
    $Game::running = true;
    

+ 5 - 70
Templates/BaseGame/game/data/ui/guis/chooseLevelDlg.cs

@@ -51,6 +51,9 @@ function ChooseLevelDlg::onWake( %this )
    for(%i=0; %i < %count; %i++)
 	{
 	   %assetId = %assetQuery.getAsset(%i);
+	   
+	   if(AssetDatabase.getAssetModule(%assetId).ModuleId $= "ToolsModule")
+	      continue;
       
       %levelAsset = AssetDatabase.acquireAsset(%assetId);
       
@@ -93,53 +96,6 @@ function ChooseLevelDlg::onWake( %this )
    else
       LevelSelectTitle.setText("CREATE SERVER");
    
-   /*for (%i = 0; %i < LevelList.rowCount(); %i++)
-   {
-      %preview = new GuiButtonCtrl() {
-         profile = "GuiMenuButtonProfile";
-         internalName = "SmallPreview" @ %i;
-         Extent = "368 35";
-         text = getField(CL_levelList.getRowText(%i), 0);
-         command = "ChooseLevelWindow.previewSelected(ChooseLevelWindow->SmallPreviews->SmallPreview" @ %i @ ");";
-         buttonType = "RadioButton";
-      };
-
-      ChooseLevelWindow->SmallPreviews.add(%preview);
-      
-      %rowText = CL_levelList.getRowText(%i);
-
-      // Set the level index
-      %preview.levelIndex = %i;
-
-      // Get the name
-      %name = getField(CL_levelList.getRowText(%i), 0);
-
-      %preview.levelName = %name;
-
-      %file = getField(CL_levelList.getRowText(%i), 1);
-
-      // Find the preview image
-      %levelPreview = getField(CL_levelList.getRowText(%i), 3);
-
-      // Test against all of the different image formats
-      // This should probably be moved into an engine function
-      if (isFile(%levelPreview @ ".png") ||
-          isFile(%levelPreview @ ".jpg") ||
-          isFile(%levelPreview @ ".bmp") ||
-          isFile(%levelPreview @ ".gif") ||
-          isFile(%levelPreview @ ".jng") ||
-          isFile(%levelPreview @ ".mng") ||
-          isFile(%levelPreview @ ".tga"))
-      {
-         %preview.bitmap = %levelPreview;
-      }
-
-      // Get the description
-      %desc = getField(CL_levelList.getRowText(%i), 2);
-
-      %preview.levelDesc = %desc;
-   }*/
-   
    ChooseLevelButtonHolder.setActive();
 }
 
@@ -184,28 +140,7 @@ function ChooseLevelDlg::addMissionFile( %this, %file )
 
 function ChooseLevelDlg::addLevelAsset( %this, %levelAsset )
 {
-   %file = %levelAsset.getLevelFile();
-   
-   /*%levelName = fileBase(%file);
-   %levelDesc = "A Torque level";
-
-   %LevelInfoObject = getLevelInfo(%file);
-
-   if (%LevelInfoObject != 0)
-   {
-      if(%LevelInfoObject.levelName !$= "")
-         %levelName = %LevelInfoObject.levelName;
-      else if(%LevelInfoObject.name !$= "")
-         %levelName = %LevelInfoObject.name;
-
-      if (%LevelInfoObject.desc0 !$= "")
-         %levelDesc = %LevelInfoObject.desc0;
-         
-      if (%LevelInfoObject.preview !$= "")
-         %levelPreview = %LevelInfoObject.preview;
-         
-      %LevelInfoObject.delete();
-   }*/
+   %file = %levelAsset.getAssetId();
    
    %levelName = %levelAsset.LevelName;
    %levelDesc = %levelAsset.description;
@@ -224,7 +159,7 @@ function LevelList::onChange(%this)
    ChooseLevelWindow->LevelName.text = getField(%levelEntry, 0);
    
    // Get the level file
-   $selectedLevelFile = getField(%levelEntry, 1);
+   $selectedLevelAsset = getField(%levelEntry, 1);
    
    // Find the preview image
    %levelPreview = getField(%levelEntry, 3);

+ 1 - 1
Templates/BaseGame/game/tools/Tools.cs

@@ -16,7 +16,7 @@ function ToolsModule::onCreate(%this)
    // to find exactly which subsystems should be readied before kicking things off. 
    // ----------------------------------------------------------------------------
    
-   ModuleDatabase.LoadExplicit( "MainEditor" );
+   //ModuleDatabase.LoadExplicit( "MainEditor" );
    //ModuleDatabase.LoadExplicit( "Tools_ObjectViewer" );
 }
 

+ 7 - 1
Templates/BaseGame/game/tools/Tools.module

@@ -5,4 +5,10 @@
 	ScriptFile="Tools.cs"
 	CreateFunction="onCreate"
 	DestroyFunction="onDestroy"
-	Group="Tools"/>
+	Group="Tools">
+	<DeclaredAssets
+        canSave="true"
+        canSaveDynamicFields="true"
+        Extension="asset.taml"
+        Recurse="true" />
+</ModuleDefinition>

+ 0 - 141
Templates/BaseGame/game/tools/levels/DefaultEditorFile.mis

@@ -1,141 +0,0 @@
-//--- OBJECT WRITE BEGIN ---
-new Scene(EditorTemplateLevel) {
-   canSave = "1";
-   canSaveDynamicFields = "1";
-   isSubScene = "0";
-   isEditing = "0";
-   isDirty = "0";
-      cdTrack = "2";
-      CTF_scoreLimit = "5";
-      Enabled = "1";
-      musicTrack = "lush";
-
-   new LevelInfo(theLevelInfo) {
-      nearClip = "0.1";
-      visibleDistance = "1000";
-      visibleGhostDistance = "0";
-      decalBias = "0.0015";
-      fogColor = "0.6 0.6 0.7 1";
-      fogDensity = "0";
-      fogDensityOffset = "700";
-      fogAtmosphereHeight = "0";
-      canvasClearColor = "0 0 0 255";
-      ambientLightBlendPhase = "1";
-      ambientLightBlendCurve = "0 0 -1 -1";
-      soundAmbience = "AudioAmbienceDefault";
-      soundDistanceModel = "Linear";
-      canSave = "1";
-      canSaveDynamicFields = "1";
-         advancedLightmapSupport = "0";
-         desc0 = "A blank room template that acts as a starting point.";
-         Enabled = "1";
-         LevelName = "Blank Room Template";
-   };
-   new SkyBox(theSky) {
-      Material = "BlankSkyMat";
-      drawBottom = "0";
-      fogBandHeight = "0";
-      dirtyGameObject = "0";
-      position = "0 0 0";
-      rotation = "1 0 0 0";
-      scale = "1 1 1";
-      canSave = "1";
-      canSaveDynamicFields = "1";
-   };
-   new Sun(theSun) {
-      azimuth = "230.396";
-      elevation = "45";
-      color = "0.968628 0.901961 0.901961 1";
-      ambient = "0.337255 0.533333 0.619608 1";
-      brightness = "1";
-      castShadows = "1";
-      staticRefreshFreq = "250";
-      dynamicRefreshFreq = "8";
-      coronaEnabled = "1";
-      coronaScale = "0.5";
-      coronaTint = "1 1 1 1";
-      coronaUseLightColor = "1";
-      flareScale = "1";
-      attenuationRatio = "0 1 1";
-      shadowType = "PSSM";
-      texSize = "2048";
-      overDarkFactor = "3000 1500 750 250";
-      shadowDistance = "200";
-      shadowSoftness = "0.25";
-      numSplits = "4";
-      logWeight = "0.9";
-      fadeStartDistance = "0";
-      lastSplitTerrainOnly = "0";
-      representedInLightmap = "0";
-      shadowDarkenColor = "0 0 0 -1";
-      includeLightmappedGeometryInShadow = "0";
-      dirtyGameObject = "0";
-      position = "0 0 0";
-      rotation = "1 0 0 0";
-      scale = "1 1 1";
-      canSave = "1";
-      canSaveDynamicFields = "1";
-         bias = "0.1";
-         Blur = "1";
-         Enabled = "1";
-         height = "1024";
-         lightBleedFactor = "0.8";
-         minVariance = "0";
-         pointShadowType = "PointShadowType_Paraboloid";
-         shadowBox = "-100 -100 -100 100 100 100";
-         splitFadeDistances = "1 1 1 1";
-         width = "3072";
-   };
-   new GroundPlane() {
-      squareSize = "128";
-      scaleU = "25";
-      scaleV = "25";
-      Material = "Grid_512_Grey";
-      dirtyGameObject = "0";
-      canSave = "1";
-      canSaveDynamicFields = "1";
-         Enabled = "1";
-         position = "0 0 0";
-         rotation = "1 0 0 0";
-         scale = "1 1 1";
-   };
-   new Skylight() {
-      Enabled = "1";
-      ReflectionMode = "Baked Cubemap";
-      dirtyGameObject = "0";
-      position = "1.37009 -5.23561 46.5817";
-      rotation = "1 0 0 0";
-      canSave = "1";
-      canSaveDynamicFields = "1";
-      persistentId = "d5eb3afb-dced-11e9-a423-bb0e346e3870";
-         reflectionPath = "tools/levels/BlankRoom/probes/";
-   };
-   new TSStatic() {
-      ShapeAsset = "pbr:material_ball";
-      playAmbient = "1";
-      meshCulling = "0";
-      originSort = "0";
-      overrideColor = "0 0 0 0";
-      collisionType = "Collision Mesh";
-      decalType = "Collision Mesh";
-      allowPlayerStep = "0";
-      alphaFadeEnable = "0";
-      alphaFadeStart = "100";
-      alphaFadeEnd = "150";
-      alphaFadeInverse = "0";
-      renderNormals = "0";
-      forceDetail = "-1";
-      ignoreZodiacs = "0";
-      useGradientRange = "0";
-      gradientRange = "0 180";
-      invertGradientRange = "0";
-      dirtyGameObject = "0";
-      position = "-0.000554562 -0.0734091 1.05277";
-      rotation = "1 0 0 0";
-      scale = "1 1 1";
-      canSave = "1";
-      canSaveDynamicFields = "1";
-         materialSlot0 = "base_material_ball";
-   };
-};
-//--- OBJECT WRITE END ---

+ 10 - 0
Templates/BaseGame/game/tools/levels/DefaultEditorLevel.asset.taml

@@ -0,0 +1,10 @@
+<LevelAsset
+    canSave="true"
+    canSaveDynamicFields="true"
+    AssetName="DefaultEditorLevel"
+    LevelFile="@assetFile=DefaultEditorLevel.mis"
+    LevelName="DefaultEditorLevel"
+    isSubScene="false"
+    description="An empty room"
+    staticObjectAssetDependency0="@Asset=FPSGameplay:station01"
+    VersionId="1" />

+ 1 - 1
Templates/BaseGame/game/tools/main.cs

@@ -272,7 +272,7 @@ function fastLoadWorldEdit(%val)
          
       if( !$missionRunning )
       {
-         EditorNewLevel("tools/levels/DefaultEditorLevel.mis");
+         EditorNewLevel("ToolsModule:DefaultEditorLevel");
       }
       else
       {

+ 14 - 5
Templates/BaseGame/game/tools/worldEditor/scripts/menuHandlers.ed.cs

@@ -199,7 +199,7 @@ function EditorOpenDeclarationInTorsion( %object )
    EditorOpenFileInTorsion( makeFullPath( %fileName ), %object.getDeclarationLine() );
 }
 
-function EditorNewLevel( %file )
+function EditorNewLevel( %level )
 {
    %saveFirst = false;
    if ( EditorIsDirty() )
@@ -219,18 +219,18 @@ function EditorNewLevel( %file )
       Editor.getUndoManager().clearAll();
    }
 
-   if( %file $= "" )
+   if( %level $= "" )
    {
-      %file = "tools/levels/DefaultEditorLevel.mis";
+      %level = "ToolsModule:DefaultEditorLevel";
    }
 
    if( !$missionRunning )
    {
       activatePackage( "BootEditor" );
-      StartGame( %file );
+      StartGame( %level );
    }
    else
-      EditorOpenMission(%file);
+      EditorOpenMission(%level);
 
    //EWorldEditor.isDirty = true;
    //ETerrainEditor.isDirty = true;
@@ -360,6 +360,15 @@ function EditorSaveMissionAs( %levelAsset )
    if( fileExt( %missionName ) !$= ".mis" )
       %missionName = %missionName @ ".mis";
       
+   //Update to be our active
+   $Server::MissionFile = %missionName;
+   %Server::LevelAsset = %levelAssetDef;
+   
+   //Do the save
+   EditorSaveMission();
+   
+   //TODO: doublecheck that we rename the scene properly
+      
    //Make sure we have a selected module so we can create our module
    //if(AssetBrowser.selectedModule $= "")
    //   Canvas.pushDialog(