Skip to content

Commit

Permalink
Add harmony dep, make stages dispatch updates, better warning message
Browse files Browse the repository at this point in the history
  • Loading branch information
Sam Byass committed Mar 2, 2020
1 parent f796c8c commit 63574c2
Show file tree
Hide file tree
Showing 18 changed files with 237 additions and 63 deletions.
11 changes: 11 additions & 0 deletions About/About.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@
<li>Ludeon.RimWorld</li>
<li>UnlimitedHugs.HugsLib</li>
</loadBefore>
<loadAfter>
<li>brrainz.harmony</li>
</loadAfter>
<modDependencies>
<li>
<packageId>brrainz.harmony</packageId>
<displayName>Harmony</displayName>
<steamWorkshopUrl>steam://url/CommunityFilePage/2009463077</steamWorkshopUrl>
<downloadUrl>https://github.com/pardeike/HarmonyRimWorld/releases/latest</downloadUrl>
</li>
</modDependencies>
<description>An enhanced loading screen.</description>

</ModMetaData>
3 changes: 2 additions & 1 deletion Source/BetterLoading.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
<Reference Include="UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath Condition="Exists('../../../RimWorldLinux_Data/')">../../../RimWorldLinux_Data/Managed/UnityEngine.dll</HintPath>
<HintPath Condition="Exists('..\..\..\RimWorldWin64_Data\')">..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<HintPath Condition="Exists('../../../RimWorldLinux_Data/')">../../../RimWorldLinux_Data/Managed/UnityEngine.CoreModule.dll</HintPath>
Expand Down Expand Up @@ -89,7 +90,7 @@
<Compile Include="Utils.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Lib.Harmony" Version="2.0.0.7" />
<PackageReference Include="Lib.Harmony" Version="2.0.0.8" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Expand Down
19 changes: 17 additions & 2 deletions Source/BetterLoadingApi.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using BetterLoading.Stage;
using Verse;
Expand All @@ -7,9 +8,16 @@ namespace BetterLoading
{
public static class BetterLoadingApi
{
public static void AddInitialLoadStage(LoadingStage stage)
/// <summary>
/// Invoked when a loading stage changes (updates its progress), and is called SYNCHRONOUSLY, ON THE UI THREAD.
/// If you care about your users, at all, don't do long-running code here.
/// </summary>
public static event Action<LoadingStage> OnStageChangeSync = stage => { };

public static void AddInitialLoadStage<T>(T stage) where T: LoadingStage
{
LoadingScreen.BootLoadList.Add(stage);
LoadingScreen.RegisterStageInstance(stage);
}

public static List<LoadingStage> GetInitialLoadStages()
Expand All @@ -27,5 +35,12 @@ public static void InsertInitialLoadStage(LoadingStage stage, int where)

LoadingScreen.BootLoadList.Insert(where, stage);
}

public static void DispatchChange(LoadingStage? stage)
{
if (stage == null) return;

OnStageChangeSync(stage);
}
}
}
40 changes: 28 additions & 12 deletions Source/BetterLoadingMain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ BetterLoading did not display because not all of your modded .dll files (assembl
Don't report this to the BetterLoading dev. If you want to report it to anyone, report it to the developers of the mods below, but it's probably your fault.
The assemblies that failed to load are:
" + string.Join("\n", DllPathsThatFailedToLoad.Select(kvp => $"{kvp.Key.Name} - {kvp.Value.Select(e => e.dllName + ".dll").ToCommaList()}").ToArray());
" + string.Join("\n", DllPathsThatFailedToLoad.Select(kvp => $"{kvp.Key.Name} - {kvp.Value.Select(e => e.dllName + ".dll").ToCommaList()}").ToArray())
+ "\nIf you would like to see what info BetterLoading has been able to work out in terms of dependencies";

foreach (var kvp in DllPathsThatFailedToLoad)
{
Expand Down Expand Up @@ -135,20 +136,35 @@ BetterLoading did not display because not all of your modded .dll files (assembl
.ToList();

//Try find mods containing the DLLs we're dependent on but can't get
var unsatisfiedDeps = missingAssemblies
var dependentMods = missingAssemblies
.Where(asm => !asm.Contains("Harmony")) //Ignore harmony as that'll likely break
.Select(e => ModLister.AllInstalledMods.FirstOrDefault(m => ModContainsAssembly(m, e)))
.Where(mod => mod != null)
.ToList();

Log.Message($"\t{dllLoadError.dllName} appears to have a dependency on these mods: {unsatisfiedDeps.Select(m => m.Name).ToStringSafeEnumerable()}");
if (dependentMods.Count > 0)
{
Log.Message($"\t{dllLoadError.dllName} appears to have a dependency on these mod(s): {dependentMods.Select(m => m.Name).ToStringSafeEnumerable()}");

var notLoaded = dependentMods.Where(requiredMod => LoadedModManager.RunningMods.All(runningMod => runningMod.Name != requiredMod.Name)).ToList();
if (notLoaded.Count > 0)
notLoaded.ForEach(m => Log.Warning($"\t{modThatFailedLoad.Name} depends on {m.Name} which is not enabled, so it didn't load properly."));

var notLoaded = unsatisfiedDeps.Where(requiredMod => LoadedModManager.RunningMods.All(runningMod => runningMod.Name != requiredMod.Name)).ToList();
if(notLoaded.Count > 0)
notLoaded.ForEach(m => Log.Warning($"\t{modThatFailedLoad.Name} depends on {m.Name} which is not enabled, so it didn't load properly."));
var modsLoadedAfterTarget = LoadedModManager.RunningMods.Skip(LoadedModManager.RunningModsListForReading.FindIndex(i => i.Name == modThatFailedLoad.Name)).Take(int.MaxValue).ToList();
var depsLoadedAfterDependent = modsLoadedAfterTarget.Where(loadedAfter => dependentMods.Any(dep => dep.Name == loadedAfter.Name)).ToList();
if (depsLoadedAfterDependent.Count > 0)
depsLoadedAfterDependent.ForEach(m => Log.Warning($"\t{modThatFailedLoad.Name} is loaded before {m.Name} but depends on it, so must be loaded after. It didn't load properly because of this."));
}

var modsLoadedAfterTarget = LoadedModManager.RunningMods.Skip(LoadedModManager.RunningModsListForReading.FindIndex(i => i.Name == modThatFailedLoad.Name)).Take(int.MaxValue).ToList();
var depsLoadedAfterDependent = modsLoadedAfterTarget.Where(loadedAfter => unsatisfiedDeps.Any(dep => dep.Name == loadedAfter.Name)).ToList();
if(depsLoadedAfterDependent.Count > 0)
depsLoadedAfterDependent.ForEach(m => Log.Warning($"\t{modThatFailedLoad.Name} is loaded before {m.Name} but depends on it, so must be loaded after. It didn't load properly because of this."));
if (dependentMods.Count != missingAssemblies.Count)
{
var notInAnyMods = missingAssemblies
.Where(asm => ModLister.AllInstalledMods.All(m => !ModContainsAssembly(m, asm)))
.Select(asm => $"{asm}.dll")
.ToList();

Log.Message($"\t{dllLoadError.dllName} (also) depends on these DLL(s) which couldn't be found in any installed mods: {notInAnyMods.ToStringSafeEnumerable()}");
}
}
}
}
Expand All @@ -170,10 +186,10 @@ private static bool ModContainsAssembly(ModMetaData mod, string assemblyName)
{
//Add default ones - common folder, version folder, + root
var pathWithVer = Path.Combine(mod.RootDir.FullName, VersionControl.CurrentVersionStringWithoutBuild);
if(Directory.Exists(pathWithVer))
if (Directory.Exists(pathWithVer))
searchPaths.Add(pathWithVer);
var commonPath = Path.Combine(mod.RootDir.FullName, ModContentPack.CommonFolderName);
if(Directory.Exists(commonPath))
if (Directory.Exists(commonPath))
searchPaths.Add(commonPath);
searchPaths.Add(mod.RootDir.FullName);
}
Expand Down
28 changes: 18 additions & 10 deletions Source/Compat/HugsLib/StageHugsLibInit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@ public class StageHugsLibInit : LoadingStage
private static bool _hasEnumeratedChildren;
private static bool _done;

private static List<object> _children;
private static List<object>? _children;
private static int _numChildrenInitialized;
private static int _numChildrenCheckedForUpdate;
private static int _numChildrenDefLoaded;

private static object _currentChildMod;
private static Harmony hInstance;
private static object? _currentChildMod;
private static Harmony? hInstance;

private static PropertyInfo _modIdentifierProperty;

private static StageHugsLibInit? inst;

public StageHugsLibInit(Harmony instance) : base(instance)
{
}
Expand All @@ -33,11 +35,6 @@ public override string GetStageName()
return "Initializing HugsLib";
}

public override void BecomeActive()
{
Log.Message($"At becomeActive, toexecutewhenfinished has {LongEventHandlerMirror.ToExecuteWhenFinished.Count} items and eventqueue has {LongEventHandlerMirror.EventQueue.Count} items ");
}

public override string? GetCurrentStepName()
{
if (!_hasEnumeratedChildren)
Expand Down Expand Up @@ -77,6 +74,11 @@ public override int GetMaximumProgress()
return 1 + childCount + childCount + childCount;
}

public override void BecomeActive()
{
inst = LoadingScreen.GetStageInstance<StageHugsLibInit>();
}

public override void DoPatching(Harmony instance)
{
hInstance = instance;
Expand All @@ -88,7 +90,7 @@ public override void DoPatching(Harmony instance)

_modIdentifierProperty = hlAssembly.GetTypes().First(t => t.Name == "ModBase").GetProperty("ModIdentifier");

Log.Message($"[BetterLoading:HugsLib Compat] Resolved required hugslib types as follows: Controller: {controllerType?.FullName} / Update Manager: {updateFeatureManagerType?.FullName} / Mod Identifier: {_modIdentifierProperty?.Name}");
Log.Message($"[BetterLoading:HugsLib Compat] Resolved required hugslib types as follows: Controller: {controllerType?.FullName} / Update Manager: {updateFeatureManagerType?.FullName} / Mod Identifier (Property): {_modIdentifierProperty?.Name}");

hInstance.Patch(AccessTools.Method(controllerType, "LoadReloadInitialize"), postfix: new HarmonyMethod(typeof(StageHugsLibInit), nameof(PostLRI)));
hInstance.Patch(AccessTools.Method(controllerType, "EnumerateChildMods"), postfix: new HarmonyMethod(typeof(StageHugsLibInit), nameof(PostEnumerateChildren)));
Expand All @@ -98,7 +100,7 @@ public override void DoPatching(Harmony instance)
new HarmonyMethod(typeof(StageHugsLibInit), nameof(PostUpdateCheck))
);

Log.Message("[BetterLoading:HugsLib] Successfully blind-patched hugslib.");
Log.Message("[BetterLoading:HugsLib Compat] Successfully blind-patched hugslib.");
}

public static void PostLRI()
Expand Down Expand Up @@ -128,33 +130,39 @@ public static void PreChildInit(object __instance)
if (__instance.GetType().GetProperty("ModIdentifier") == null) return;

_currentChildMod = __instance;
BetterLoadingApi.DispatchChange(inst);
}

public static void PostChildInit()
{
_numChildrenInitialized++;
BetterLoadingApi.DispatchChange(inst);
}

public static void PreUpdateCheck(string modId)
{
_currentChildMod = _children?.Find(m => (string) _modIdentifierProperty.GetValue(m, null) == modId);
BetterLoadingApi.DispatchChange(inst);
}

public static void PostUpdateCheck()
{
_numChildrenCheckedForUpdate++;
BetterLoadingApi.DispatchChange(inst);
}

public static void PreDefsLoaded(object __instance)
{
if (__instance.GetType().GetProperty("ModIdentifier") == null) return;

_currentChildMod = __instance;
BetterLoadingApi.DispatchChange(inst);
}

public static void PostDefsLoaded()
{
_numChildrenDefLoaded++;
BetterLoadingApi.DispatchChange(inst);
}
}
}
2 changes: 2 additions & 0 deletions Source/Folder.DotSettings.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=C_003A_005CProgram_0020Files_0020_0028x86_0029_005CSteam_005Csteamapps_005Ccommon_005CRimWorld_005CRimWorldWin64_005FData_005CManaged_005CUnityEngine_002EAssetBundleModule_002Edll/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
51 changes: 48 additions & 3 deletions Source/LoadingScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public sealed class LoadingScreen : MonoBehaviour
new StageRunStaticCctors(BetterLoadingMain.hInstance),
new StageRunPostFinalizeCallbacks(BetterLoadingMain.hInstance)
};

private static Dictionary<Type, LoadingStage> _loadingStagesByType = new Dictionary<Type, LoadingStage>();

private Texture2D background;
private Texture2D errorBarColor;
Expand All @@ -56,13 +58,40 @@ public sealed class LoadingScreen : MonoBehaviour
public LoadingScreen()
{
Instance = this;
BootLoadList.ForEach(s => _loadingStagesByType[s.GetType()] = s);

_currentStage.BecomeActive();
StageTimingData.ExecutedStages.Add(new StageTimingData
{
start = DateTime.Now,
stage = _currentStage
});
}

internal static void RegisterStageInstance<T>(T stage) where T: LoadingStage
{
try
{
GetStageInstance<T>(); //Verify not in dict already
}
catch (ArgumentException)
{
_loadingStagesByType.Add(typeof(T), stage);
return;
}

throw new ArgumentException($"RegisterStageInstance called for an already registered stage type {typeof(T)} (mapped to {GetStageInstance<T>()}).");
}

internal static T GetStageInstance<T>() where T: LoadingStage
{
if (!_loadingStagesByType.TryGetValue(typeof(T), out var ret))
{
throw new ArgumentException($"GetStageInstance called for an unregistered stage type {typeof(T)}.");
}

return (T) ret;
}

private void Awake()
{
Expand Down Expand Up @@ -114,12 +143,28 @@ public void OnGUI()

//Move to next stage
Log.Message("BetterLoading: Finished stage " + _currentStage.GetStageName() + " at " + DateTime.Now.ToLongTimeString());
_currentStage.BecomeInactive();
try
{
_currentStage.BecomeInactive();
}
catch (Exception e)
{
Log.Error($"[BetterLoading] The stage {_currentStage} errored during BecomeInactive: {e}");
}

StageTimingData.ExecutedStages.Last().end = DateTime.Now;

_currentStage = currentList[idx + 1];
_currentStage.BecomeActive();
Log.Message("BetterLoading: Starting stage " + _currentStage.GetStageName());
try
{
Log.Message("BetterLoading: Starting stage " + _currentStage.GetStageName());
_currentStage.BecomeActive();
}
catch (Exception e)
{
Log.Error($"[BetterLoading] The stage {_currentStage} errored during BecomeActive: {e}");
}

StageTimingData.ExecutedStages.Add(new StageTimingData
{
start = DateTime.Now,
Expand Down
5 changes: 5 additions & 0 deletions Source/Stage/InitialLoad/0StageInitMods.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using BetterLoading.Compat;
using BetterLoading.Compat.HugsLib;
using HarmonyLib;
Expand All @@ -16,6 +17,8 @@ public class StageInitMods : LoadingStage
private static ModContentPack _currentMod = BetterLoadingMain.ourContentPack;
private static bool _completed;

private static StageInitMods? inst;

public StageInitMods(Harmony instance) : base(instance)
{
}
Expand All @@ -38,6 +41,7 @@ public override int GetCurrentProgress()
public override void BecomeActive()
{
_numMods = typeof(Mod).InstantiableDescendantsAndSelf().Select(m => m.FullName).Distinct().Count();
inst = LoadingScreen.GetStageInstance<StageInitMods>();
}

public override void BecomeInactive()
Expand Down Expand Up @@ -74,6 +78,7 @@ public static void OnActivatorCreateInstance(Type type, params object[] args)

_currentModIdx++;
_currentMod = pack;
BetterLoadingApi.DispatchChange(inst);
}
}
}
4 changes: 4 additions & 0 deletions Source/Stage/InitialLoad/1StageReadXML.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public class StageReadXML : LoadingStage
private static int _currentPackIdx = 1;
private int _numPacks = -1;

private static StageReadXML? inst;

public StageReadXML(Harmony instance) : base(instance)
{

Expand Down Expand Up @@ -43,12 +45,14 @@ public override void DoPatching(Harmony instance)
public override void BecomeActive()
{
_numPacks = LoadedModManager.RunningMods.Count();
inst = LoadingScreen.GetStageInstance<StageReadXML>();
}

public static void OnLoadDefsComplete(ModContentPack __instance)
{
_currentPack = __instance;
_currentPackIdx++;
BetterLoadingApi.DispatchChange(inst);
}
}
}
Loading

0 comments on commit 63574c2

Please sign in to comment.