diff --git a/LethalLevelLoader/LethalLevelLoader.csproj b/LethalLevelLoader/LethalLevelLoader.csproj
index b05116b..044617d 100644
--- a/LethalLevelLoader/LethalLevelLoader.csproj
+++ b/LethalLevelLoader/LethalLevelLoader.csproj
@@ -64,6 +64,7 @@
+
diff --git a/LethalLevelLoader/Plugin.cs b/LethalLevelLoader/Plugin.cs
index c8e7274..ae3ddbb 100644
--- a/LethalLevelLoader/Plugin.cs
+++ b/LethalLevelLoader/Plugin.cs
@@ -48,6 +48,8 @@ private void Awake()
Logger.LogInfo($"LethalLevelLoader loaded!!");
+ ConfigLoader.BindGeneralConfigs();
+
//We do this here to try and assure this doesn't accidently catch anything from any AssetBundles
LevelLoader.vanillaWaterShader = Shader.Find("Shader Graphs/WaterShaderHDRP");
if (LevelLoader.vanillaWaterShader == null)
@@ -74,8 +76,6 @@ private void Awake()
else
assetBundleLoaderObject.hideFlags = HideFlags.HideAndDontSave;
AssetBundleLoader.onBundlesFinishedLoading += AssetBundleLoader.LoadContentInBundles;
-
- ConfigLoader.BindGeneralConfigs();
}
internal static void OnBeforeSetupInvoke()
diff --git a/LethalLevelLoader/Tools/AssetBundleLoader.cs b/LethalLevelLoader/Tools/AssetBundleLoader.cs
index 9bfd61a..f5553a4 100644
--- a/LethalLevelLoader/Tools/AssetBundleLoader.cs
+++ b/LethalLevelLoader/Tools/AssetBundleLoader.cs
@@ -1,4 +1,4 @@
-using DunGen;
+using DunGen;
using DunGen.Graph;
using HarmonyLib;
using System;
@@ -12,6 +12,7 @@
using System.Runtime.CompilerServices;
using TMPro;
using Unity.Netcode;
+using UnityEditor;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.SceneManagement;
@@ -141,6 +142,51 @@ internal void LoadBundles()
CurrentLoadingStatus = LoadingStatus.Complete;
onBundlesFinishedLoading?.Invoke();
}
+
+ try
+ {
+ LoadAssetsInEditor();
+ }
+ catch (FileNotFoundException) { }
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ private static void LoadAssetsInEditor()
+ {
+ if (!Application.isEditor)
+ return;
+
+ var modAssets = AssetDatabase.FindAssets("t:ExtendedMod");
+ var extendedMods = new HashSet(obtainedExtendedModsDictionary.Values);
+
+ foreach (var modAsset in modAssets)
+ {
+ var modPath = AssetDatabase.GUIDToAssetPath(modAsset);
+ var mod = AssetDatabase.LoadAssetAtPath(modPath);
+ if (extendedMods.Contains(mod))
+ {
+ DebugHelper.Log($"Skipping already-registered extended mod in editor: {modPath}", DebugType.Developer);
+ continue;
+ }
+ DebugHelper.Log($"Loading extended mod in editor: {modPath}", DebugType.Developer);
+ RegisterExtendedMod(mod);
+ }
+
+ var contentAssets = AssetDatabase.FindAssets("t:ExtendedContent");
+ var extendedContents = new HashSet(obtainedExtendedModsDictionary.Values.SelectMany(m => m.ExtendedContents));
+
+ foreach (var contentAsset in contentAssets)
+ {
+ var contentPath = AssetDatabase.GUIDToAssetPath(contentAsset);
+ var content = AssetDatabase.LoadAssetAtPath(contentPath);
+ if (extendedContents.Contains(content))
+ {
+ DebugHelper.Log($"Skipping already-registered extended content in editor: {contentPath}", DebugType.Developer);
+ continue;
+ }
+ DebugHelper.Log($"Loading extended content in editor: {contentPath}", DebugType.Developer);
+ RegisterNewExtendedContent(content, "EditorContent");
+ }
}
internal static void OnBundlesFinishedLoadingInvoke()
@@ -215,6 +261,11 @@ internal static void RegisterExtendedMod(ExtendedMod extendedMod)
ExtendedMod matchingExtendedMod = null;
foreach (ExtendedMod registeredExtendedMod in obtainedExtendedModsDictionary.Values)
{
+ if (extendedMod == registeredExtendedMod)
+ {
+ DebugHelper.Log($"Skipping registration of duplicate extended mod {extendedMod.ModName}!", DebugType.Developer);
+ return;
+ }
if (extendedMod.ModMergeSetting == ModMergeSetting.MatchingModName && registeredExtendedMod.ModMergeSetting == ModMergeSetting.MatchingModName)
{
if (registeredExtendedMod.ModName == extendedMod.ModName)
@@ -251,11 +302,12 @@ internal static void RegisterExtendedMod(ExtendedMod extendedMod)
}
else
{
- obtainedExtendedModsDictionary.Add(extendedMod.AuthorName, extendedMod);
+ obtainedExtendedModsDictionary.Add(extendedMod.ModName, extendedMod);
List serializedExtendedContents = new List(extendedMod.ExtendedContents);
extendedMod.UnregisterAllExtendedContent();
foreach (ExtendedContent extendedContent in serializedExtendedContents)
{
+ DebugHelper.Log($"Registering {extendedContent} to {extendedMod.ModName}", DebugType.Developer);
try
{
extendedMod.RegisterExtendedContent(extendedContent);
@@ -308,10 +360,10 @@ internal static void OnBundlesFinishedLoading()
//foreach (KeyValuePair loadedAssetBundles in assetBundleLoadTimes)
//DebugHelper.Log(loadedAssetBundles.Key + " Loaded In " + loadedAssetBundles.Value, DebugType.User);
- foreach (KeyValuePair obtainedExtendedMod in obtainedExtendedModsDictionary)
+ foreach (ExtendedMod obtainedExtendedMod in obtainedExtendedModsDictionary.Values)
{
- PatchedContent.ExtendedMods.Add(obtainedExtendedMod.Value);
- DebugHelper.DebugExtendedMod(obtainedExtendedMod.Value);
+ PatchedContent.ExtendedMods.Add(obtainedExtendedMod);
+ DebugHelper.DebugExtendedMod(obtainedExtendedMod);
}
PatchedContent.ExtendedMods = new List(PatchedContent.ExtendedMods.OrderBy(o => o.ModName).ToList());
@@ -402,7 +454,7 @@ internal static ExtendedMod GetOrCreateExtendedMod(string contentSourceName)
//This function should probably just be in NetworkRegisterContent
internal static void LoadContentInBundles()
{
- bool foundExtendedLevelScene;
+ DebugHelper.Log($"Finding scenes to patch into NetworkSceneManager.", DebugType.Developer);
List obtainedExtendedModsList = obtainedExtendedModsDictionary.Values.OrderBy(o => o.ModName).ToList();
List sceneNames = new List();
@@ -416,32 +468,46 @@ internal static void LoadContentInBundles()
sceneNames.Add(sceneName.Name);
}
+ IEnumerable allSceneBundles = assetBundles.Values.Where(b => b.isStreamedSceneAssetBundle);
+ IEnumerable allEditorScenes = GetAllScenePathsInEditor();
+
+ DebugHelper.Log($"All bundle scenes:\n{allSceneBundles.SelectMany(b => b.GetAllScenePaths()).Join(p => p, "\n")}", DebugType.Developer);
+ DebugHelper.Log($"All editor scenes:\n{allEditorScenes.Join(p => p, "\n")}", DebugType.Developer);
+
+ var sceneNamesAndPaths = new Dictionary();
+
+ foreach (var assetBundle in allSceneBundles)
+ {
+ foreach (var scenePath in assetBundle.GetAllScenePaths())
+ {
+ var sceneName = GetSceneName(scenePath);
+ if (!sceneNamesAndPaths.TryAdd(sceneName, scenePath))
+ DebugHelper.LogWarning($"Duplicate scene name {sceneName} found in asset bundle {assetBundle.name}", DebugType.Developer);
+ }
+ }
+
+ foreach (var scenePath in allEditorScenes)
+ {
+ var sceneName = GetSceneName(scenePath);
+ if (!sceneNamesAndPaths.TryAdd(sceneName, scenePath))
+ DebugHelper.LogWarning($"Duplicate scene name {sceneName} found in editor scenes", DebugType.Developer);
+ }
+
foreach (ExtendedMod extendedMod in obtainedExtendedModsList)
{
foreach (ExtendedLevel extendedLevel in new List(extendedMod.ExtendedLevels))
{
- foundExtendedLevelScene = false;
- string debugString = "Could Not Find Scene File For ExtendedLevel: " + extendedLevel.SelectableLevel.name + ", Unregistering Early. \nSelectable Scene Name Is: " + extendedLevel.SelectableLevel.sceneName + ". Scenes Found In Bundles Are: " + "\n";
- foreach (KeyValuePair assetBundle in assetBundles)
- if (assetBundle.Value != null && assetBundle.Value.isStreamedSceneAssetBundle)
- foreach (string scenePath in assetBundle.Value.GetAllScenePaths())
- {
- debugString += ", " + GetSceneName(scenePath);
- if (sceneNames.Contains(GetSceneName(scenePath)))
- {
- //DebugHelper.Log("Found Scene File For ExtendedLevel: " + extendedLevel.selectableLevel.name + ". Scene Path Is: " + scenePath);
- foundExtendedLevelScene = true;
- NetworkScenePatcher.AddScenePath(GetSceneName(scenePath));
- if (!PatchedContent.AllLevelSceneNames.Contains(GetSceneName(scenePath)))
- PatchedContent.AllLevelSceneNames.Add(GetSceneName(scenePath));
- }
- }
-
- if (foundExtendedLevelScene == false)
+ if (!sceneNamesAndPaths.TryGetValue(extendedLevel.SelectableLevel.sceneName, out string path))
{
- DebugHelper.LogError(debugString, DebugType.User);
+ DebugHelper.LogError($"Could not find scene {extendedLevel.SelectableLevel.sceneName} for level {extendedLevel.SelectableLevel.name}. Scene names are: {sceneNamesAndPaths.Keys.Join(s => s, ", ")}", DebugType.User);
extendedMod.UnregisterExtendedContent(extendedLevel);
+ continue;
}
+
+ DebugHelper.LogError($"Found scene with name {extendedLevel.SelectableLevel.sceneName} and path {path}", DebugType.Developer);
+ NetworkScenePatcher.AddScenePath(extendedLevel.SelectableLevel.sceneName, path);
+ if (!PatchedContent.AllLevelSceneNames.Contains(extendedLevel.SelectableLevel.sceneName))
+ PatchedContent.AllLevelSceneNames.Add(extendedLevel.SelectableLevel.sceneName);
}
}
@@ -449,6 +515,29 @@ internal static void LoadContentInBundles()
DebugHelper.Log("Loaded SceneName: " + loadedSceneName, DebugType.Developer);
}
+ private static string[] GetAllScenePathsInEditor()
+ {
+ try
+ {
+ return GetAllScenePathsInEditorImpl();
+ }
+ catch (FileNotFoundException)
+ {
+ return [];
+ }
+ }
+
+ private static string[] GetAllScenePathsInEditorImpl()
+ {
+ var sceneGUIDs = AssetDatabase.FindAssets("t:Scene");
+ var scenePaths = new string[sceneGUIDs.Length];
+
+ for (var i = 0; i < scenePaths.Length; i++)
+ scenePaths[i] = AssetDatabase.GUIDToAssetPath(sceneGUIDs[i]);
+
+ return scenePaths;
+ }
+
internal static void InitializeBundles()
{
foreach (ExtendedMod extendedMod in PatchedContent.ExtendedMods)
diff --git a/LethalLevelLoader/Tools/NetworkScenePatcher.cs b/LethalLevelLoader/Tools/NetworkScenePatcher.cs
index 10d1c01..34ce024 100644
--- a/LethalLevelLoader/Tools/NetworkScenePatcher.cs
+++ b/LethalLevelLoader/Tools/NetworkScenePatcher.cs
@@ -3,11 +3,13 @@
using MonoMod.Cil;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Unity.Netcode;
+using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using static HookHelper;
@@ -15,21 +17,23 @@
public static class NetworkScenePatcher
{
// start of script
- static List scenePaths = new();
+ static List sceneNames = new();
+ static Dictionary scenePaths = new();
static Dictionary scenePathToBuildIndex = new();
static Dictionary buildIndexToScenePath = new();
static Dictionary sceneHashToScenePath = new();
- public static void AddScenePath(string scenePath)
+ public static void AddScenePath(string sceneName, string scenePath)
{
- if (scenePaths.Contains(scenePath))
+ if (sceneNames.Contains(sceneName))
{
//Debug.LogError($"Can not add scene path {scenePath} to the network scene patcher! (already exists in scene paths list)");
return;
}
- DebugHelper.Log("Adding ScenePath: " + scenePath, DebugType.User);
- scenePaths.Add(scenePath);
+ DebugHelper.Log("Adding ScenePath: " + sceneName, DebugType.User);
+ sceneNames.Add(sceneName);
+ scenePaths.TryAdd(sceneName, scenePath);
}
@@ -48,6 +52,8 @@ internal static void Patch()
hooks.ILHook("SceneHashFromNameOrPath", ReplaceBuildIndexByScenePath);
hooks.ILHook("ValidateSceneEvent", ReplaceBuildIndexByScenePath);
hooks.ILHook("ScenePathFromHash", ReplaceScenePathByBuildIndex);
+
+ hooks.ILHook("LoadSceneAsync", ReplaceLoadSceneAsync);
}
internal static void Unpatch()
{
@@ -95,6 +101,47 @@ static int GetBuildIndexByScenePath(string scenePath)
}
return val;
}
+
+ static void ReplaceLoadSceneAsync(ILContext il)
+ {
+ ILCursor script = new(il);
+ MethodInfo replacement = methodof(LoadSceneAsync);
+
+ while (script.TryGotoNext(instr => instr.MatchCall(typeof(SceneManager), "LoadSceneAsync")))
+ {
+ script.Remove();
+ script.Emit(OpCodes.Call, replacement);
+ }
+ }
+
+ static AsyncOperation LoadSceneAsync(string sceneName, LoadSceneMode mode)
+ {
+ DebugHelper.Log($"Loading scene {sceneName}.", DebugType.Developer);
+ var result = SceneManager.LoadSceneAsync(sceneName, mode);
+
+ if (result == null)
+ {
+ try
+ {
+ return LoadSceneInEditorAsync(sceneName, mode);
+ }
+ catch (FileNotFoundException) { }
+ }
+
+ return result;
+ }
+
+ static AsyncOperation LoadSceneInEditorAsync(string sceneName, LoadSceneMode mode)
+ {
+ DebugHelper.Log($"Trying to load scene {sceneName} through EditorSceneManager.", DebugType.Developer);
+ if (!scenePaths.TryGetValue(sceneName, out var path))
+ {
+ DebugHelper.Log($"Failed to find scene path for scene {sceneName} to load in editor.", DebugType.Developer);
+ return null;
+ }
+ return EditorSceneManager.LoadSceneAsyncInPlayMode(path, new LoadSceneParameters(mode));
+ }
+
static void GenerateScenesInBuild_Hook(Action orig, NetworkSceneManager self)
{
scenePathToBuildIndex.Clear();
@@ -104,10 +151,10 @@ static void GenerateScenesInBuild_Hook(Action orig, Network
orig(self);
int count = SceneManager.sceneCountInBuildSettings;
- for (int i = 0; i < scenePaths.Count; i++)
+ for (int i = 0; i < sceneNames.Count; i++)
{
int buildIndex = count + i;
- string scenePath = scenePaths[i];
+ string scenePath = sceneNames[i];
uint hash = scenePath.Hash32();
self.HashToBuildIndex.Add(hash, buildIndex);