Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow LLL to find ExtendedMod/ExtendedContent and load scenes in editor #159

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
139 changes: 114 additions & 25 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 @@ -251,11 +302,12 @@ internal static void RegisterExtendedMod(ExtendedMod extendedMod)
}
else
{
obtainedExtendedModsDictionary.Add(extendedMod.AuthorName, extendedMod);
obtainedExtendedModsDictionary.Add(extendedMod.ModName, extendedMod);
List<ExtendedContent> serializedExtendedContents = new List<ExtendedContent>(extendedMod.ExtendedContents);
extendedMod.UnregisterAllExtendedContent();
foreach (ExtendedContent extendedContent in serializedExtendedContents)
{
DebugHelper.Log($"Registering {extendedContent} to {extendedMod.ModName}", DebugType.Developer);
try
{
extendedMod.RegisterExtendedContent(extendedContent);
Expand Down Expand Up @@ -308,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 @@ -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<ExtendedMod> obtainedExtendedModsList = obtainedExtendedModsDictionary.Values.OrderBy(o => o.ModName).ToList();
List<string> sceneNames = new List<string>();

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

IEnumerable<AssetBundle> allSceneBundles = assetBundles.Values.Where(b => b.isStreamedSceneAssetBundle);
IEnumerable<string> 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<string, string>();

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<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