Skip to content

Commit

Permalink
Allow LLL to find ExtendedMod/ExtendedContent and load scenes in editor
Browse files Browse the repository at this point in the history
  • Loading branch information
Zaggy1024 committed Jan 10, 2025
1 parent 0fd905e commit 2c28db6
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 33 deletions.
1 change: 1 addition & 0 deletions LethalLevelLoader/LethalLevelLoader.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<PackageReference Include="Evaisa.LethalLib" Version="0.16.0" />
<PackageReference Include="MaxWasUnavailable.LethalModDataLib" Version="1.2.2" />
<PackageReference Include="LethalCompany.GameLibs.Steam" Version="56.0.1-ngd.0" Publicize="true" />
<PackageReference Include="Unity3D.SDK" Version="2021.1.14.1" IncludeAssets="compile" />
</ItemGroup>

<Target Name="NetcodePatch" AfterTargets="PostBuildEvent">
Expand Down
4 changes: 2 additions & 2 deletions LethalLevelLoader/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -74,8 +76,6 @@ private void Awake()
else
assetBundleLoaderObject.hideFlags = HideFlags.HideAndDontSave;
AssetBundleLoader.onBundlesFinishedLoading += AssetBundleLoader.LoadContentInBundles;

ConfigLoader.BindGeneralConfigs();
}

internal static void OnBeforeSetupInvoke()
Expand Down
135 changes: 111 additions & 24 deletions LethalLevelLoader/Tools/AssetBundleLoader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using DunGen;
using DunGen;
using DunGen.Graph;
using HarmonyLib;
using System;
Expand All @@ -12,6 +12,7 @@
using System.Runtime.CompilerServices;
using TMPro;
using Unity.Netcode;
using UnityEditor;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.SceneManagement;
Expand Down Expand Up @@ -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<ExtendedMod>(obtainedExtendedModsDictionary.Values);

foreach (var modAsset in modAssets)
{
var modPath = AssetDatabase.GUIDToAssetPath(modAsset);
var mod = AssetDatabase.LoadAssetAtPath<ExtendedMod>(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<ExtendedContent>(obtainedExtendedModsDictionary.Values.SelectMany(m => m.ExtendedContents));

foreach (var contentAsset in contentAssets)
{
var contentPath = AssetDatabase.GUIDToAssetPath(contentAsset);
var content = AssetDatabase.LoadAssetAtPath<ExtendedContent>(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()
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -309,10 +360,10 @@ internal static void OnBundlesFinishedLoading()
//foreach (KeyValuePair<string, string> loadedAssetBundles in assetBundleLoadTimes)
//DebugHelper.Log(loadedAssetBundles.Key + " Loaded In " + loadedAssetBundles.Value, DebugType.User);

foreach (KeyValuePair<string, ExtendedMod> 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<ExtendedMod>(PatchedContent.ExtendedMods.OrderBy(o => o.ModName).ToList());
Expand Down Expand Up @@ -403,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<ExtendedMod> obtainedExtendedModsList = obtainedExtendedModsDictionary.Values.OrderBy(o => o.ModName).ToList();
List<string> sceneNames = new List<string>();

Expand All @@ -417,39 +468,75 @@ internal static void LoadContentInBundles()
sceneNames.Add(sceneName.Name);
}

DebugHelper.Log($"All bundle scenes:\n{assetBundles.Values.Where(b => b.isStreamedSceneAssetBundle).SelectMany(b => b.GetAllScenePaths()).Join(p => p, "\n")}", DebugType.Developer);
DebugHelper.Log($"All editor scenes:\n{GetAllScenePathsInEditor().Join(p => p, "\n")}", DebugType.Developer);

var sceneNamesAndPaths = new Dictionary<string, string>();

foreach (var assetBundle in assetBundles.Values)
{
if (!assetBundle.isStreamedSceneAssetBundle)
continue;
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 GetAllScenePathsInEditor())
{
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<ExtendedLevel>(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<string, AssetBundle> 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);
}
}

foreach (string loadedSceneName in PatchedContent.AllLevelSceneNames)
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)
Expand Down
61 changes: 54 additions & 7 deletions LethalLevelLoader/Tools/NetworkScenePatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,37 @@
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;

public static class NetworkScenePatcher
{
// start of script
static List<string> scenePaths = new();
static List<string> sceneNames = new();
static Dictionary<string, string> scenePaths = new();

static Dictionary<string, int> scenePathToBuildIndex = new();
static Dictionary<int, string> buildIndexToScenePath = new();
static Dictionary<uint, string> 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);
}


Expand All @@ -48,6 +52,8 @@ internal static void Patch()
hooks.ILHook<NetworkSceneManager>("SceneHashFromNameOrPath", ReplaceBuildIndexByScenePath);
hooks.ILHook<NetworkSceneManager>("ValidateSceneEvent", ReplaceBuildIndexByScenePath);
hooks.ILHook<NetworkSceneManager>("ScenePathFromHash", ReplaceScenePathByBuildIndex);

hooks.ILHook<DefaultSceneManagerHandler>("LoadSceneAsync", ReplaceLoadSceneAsync);
}
internal static void Unpatch()
{
Expand Down Expand Up @@ -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<NetworkSceneManager> orig, NetworkSceneManager self)
{
scenePathToBuildIndex.Clear();
Expand All @@ -104,10 +151,10 @@ static void GenerateScenesInBuild_Hook(Action<NetworkSceneManager> 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);
Expand Down

0 comments on commit 2c28db6

Please sign in to comment.