Skip to content

Commit

Permalink
Initial port to 1.4
Browse files Browse the repository at this point in the history
  • Loading branch information
SamboyCoding committed Mar 16, 2024
1 parent 22e87d0 commit 47de726
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 44 deletions.
Binary file added 1.4/Assemblies/BetterLoading.dll
Binary file not shown.
Binary file added 1.4/Assemblies/Tomlet.dll
Binary file not shown.
1 change: 1 addition & 0 deletions About/About.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<li>1.1</li>
<li>1.2</li>
<li>1.3</li>
<li>1.4</li>
</supportedVersions>
<loadBefore>
<li>Ludeon.RimWorld</li>
Expand Down
28 changes: 16 additions & 12 deletions LoadFolders.xml
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
<loadFolders>
<v1.1>
<li>1.1-1.2</li>
<li></li>
</v1.1>
<v1.2>
<li>1.1-1.2</li>
<li></li>
</v1.2>
<v1.3>
<li>1.3</li>
<li></li>
</v1.3>
<v1.1>
<li>1.1-1.2</li>
<li></li>
</v1.1>
<v1.2>
<li>1.1-1.2</li>
<li></li>
</v1.2>
<v1.3>
<li>1.3</li>
<li></li>
</v1.3>
<v1.4>
<li>1.4</li>
<li></li>
</v1.4>
</loadFolders>
8 changes: 4 additions & 4 deletions Source/BetterLoading.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<Optimize>true</Optimize>
<OutDir>..\1.3\Assemblies\</OutDir>
<OutputPath>..\1.3\Assemblies\</OutputPath>
<OutDir>..\1.4\Assemblies\</OutDir>
<OutputPath>..\1.4\Assemblies\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutDir>..\1.3\Assemblies\</OutDir>
<OutputPath>..\1.3\Assemblies\</OutputPath>
<OutDir>..\1.4\Assemblies\</OutDir>
<OutputPath>..\1.4\Assemblies\</OutputPath>
</PropertyGroup>
<ItemGroup>
<Reference Include="Assembly-CSharp, Culture=neutral, PublicKeyToken=null">
Expand Down
3 changes: 3 additions & 0 deletions Source/BetterLoadingConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ public class BetterLoadingConfig
[TomlPrecedingComment("The TipCache caches information about loading screen tips so that they can be displayed as soon as the loading screen starts after the first run.")]
public TipCacheConfig TipCache;

[TomlPrecedingComment("Enable verbose logging for BetterLoading. This is useful for debugging, but may slow down the game.")]
public bool VerboseLogging = false;

public BetterLoadingConfig()
{
TipCache = new();
Expand Down
4 changes: 2 additions & 2 deletions Source/Stage/InitialLoad/3StageApplyPatches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ public static void PreLoadPatches(ModContentPack __instance)
BetterLoadingApi.DispatchChange(inst);
}

public static void PostLoadPatches(List<PatchOperation> ___patches)
public static void PostLoadPatches(IEnumerable<PatchOperation> __result)
{
if (_hasFinished) return;

_numPatches = ___patches.Count;
_numPatches = __result.Count();
_currentPatch = 0;
_loadingPatches = false;
BetterLoadingApi.DispatchChange(inst);
Expand Down
28 changes: 19 additions & 9 deletions Source/Stage/InitialLoad/7StageRunPostLoadPreFinalizeCallbacks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,21 @@ public StageRunPostLoadPreFinalizeCallbacks(Harmony instance) : base(instance)

public override string GetStageName()
{
return "Running Post Content Load Callbacks";
return "Running Pre-Static-Constructor Long Events";
}

public override string? GetCurrentStepName()
{
if (_currentAction == null)
return "Waiting for tasks to start being processed...";

if(_currentAction is { Target: ModContentPack mcp, Method.Name: nameof(ModContentPack.ReloadContent) + "Int" })
return $"Reloading content for {mcp.Name}";

return (_currentAction.Method.DeclaringType?.FullName ?? "<unknown anonymous method>") + (_currentAction.Target != null ? $" ({_currentAction.Target})" : "");
var methodDeclaringType = _currentAction.Method.DeclaringType?.FullName ?? "<unknown anonymous method>";
var methodName = _currentAction.Method?.Name ?? "<unknown method>";
var target = _currentAction.Target != null ? $"{_currentAction.Target}" : "";
return $"{methodDeclaringType}::{methodName} (on instance {target})";
}

public override bool IsCompleted()
Expand Down Expand Up @@ -93,7 +99,13 @@ public static bool PreExecToExecWhenFinished(List<Action> ___toExecuteWhenFinish

// Debug.Log($"BL Debug: types declared in PDL: {declaredInPDL.Select(a => a.Method).ToStringSafeEnumerable()}");

var targetMethodName = VersionControl.CurrentMinor == 3 ? "b__4_3" : "b__4_2";
var targetMethodName = VersionControl.CurrentMinor switch
{
2 => "b__4_2",
3 => "b__4_3",
4 => "b__4_5",
_ => throw new ArgumentOutOfRangeException()
};

// Log.Message($"BL Debug: Tasks defined in PDL: {string.Join(", ", declaredInPDL.Select(task => task.Method.FullDescription()))}");

Expand Down Expand Up @@ -135,20 +147,18 @@ public static bool PreExecToExecWhenFinished(List<Action> ___toExecuteWhenFinish
LongEventHandler.QueueLongEvent(() =>
{
// Log.Message("[BetterLoading] Blocking loading screen from being dismissed until post-load actions are complete.");
Thread.Sleep(1000);
Thread.Sleep(0);

while (!_finishedExecuting)
{
Thread.Sleep(200); //Wait
Thread.Sleep(0); //Wait
}

Log.Message($"[BetterLoading] Obtained synclock, assuming post-load actions are complete and starting static constructors.");

_done = true;

runStaticCtors();

Thread.Sleep(0);

_done = true;
}, null, false, null);

return false;
Expand Down
65 changes: 52 additions & 13 deletions Source/Stage/InitialLoad/8StageRunStaticCctors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Runtime.CompilerServices;
using System.Threading;
using HarmonyLib;
using RimWorld;
using UnityEngine;
using Verse;

Expand All @@ -29,11 +30,14 @@ public StageRunStaticCctors(Harmony instance) : base(instance)

public override string GetStageName()
{
return "Finalizing Mods";
return "Executing Static Constructors";
}

public override string? GetCurrentStepName()
{
if((_numRun == (_toRun?.Count ?? 1)) && !_finishedProcessing)
return VersionControl.CurrentMinor == 4 ? "Baking static atlases and cleaning up" : "Cleaning up...";

var result = _modType?.FullName ?? "Waiting...";
if (HasError())
result = $"WARNING: An error has occurred previously, now processing {result}";
Expand All @@ -53,13 +57,24 @@ public override int GetMaximumProgress()

public override bool IsCompleted()
{
return _numRun == _toRun?.Count;
return _numRun == _toRun?.Count && _finishedProcessing;
}

public override void DoPatching(Harmony instance)
{
instance.Patch(AccessTools.Method(typeof(StaticConstructorOnStartupUtility), nameof(StaticConstructorOnStartupUtility.CallAll)), new(typeof(StageRunStaticCctors), nameof(PreCallAll)));
// instance.Patch(AccessTools.Method(typeof(RuntimeHelpers), nameof(RuntimeHelpers.RunClassConstructor), new []{typeof(RuntimeTypeHandle)}), new HarmonyMethod(typeof(StageRunStaticCctors), nameof(PreRunClassConstructor)));
if (VersionControl.CurrentMinor == 4)
{
//1.4, we need to patch the entire <DoPlayLoad>b__4_5 method and make it not run, instead of just CallAll, because there's 2 other method calls in that anon method which we don't wanna do until after static constructors
var anonType = typeof(PlayDataLoader).GetNestedTypes(AccessTools.all).First(t => t.Name.Contains("<>"));
var method = AccessTools.Method(anonType, "<DoPlayLoad>b__4_5");
instance.Patch(method, new HarmonyMethod(typeof(StageRunStaticCctors), nameof(PreCallAll)));
}
else
{
//Pre-1.4, we can just patch CallAll
instance.Patch(AccessTools.Method(typeof(StaticConstructorOnStartupUtility), nameof(StaticConstructorOnStartupUtility.CallAll)), new(typeof(StageRunStaticCctors), nameof(PreCallAll)));
// instance.Patch(AccessTools.Method(typeof(RuntimeHelpers), nameof(RuntimeHelpers.RunClassConstructor), new []{typeof(RuntimeTypeHandle)}), new HarmonyMethod(typeof(StageRunStaticCctors), nameof(PreRunClassConstructor)));
}
}

public override bool HasError()
Expand All @@ -74,7 +89,7 @@ public override void BecomeActive()
var patches = Harmony.GetPatchInfo(AccessTools.Method(typeof(StaticConstructorOnStartupUtility), nameof(StaticConstructorOnStartupUtility.CallAll)));

//We register a prefix, others may have a postfix which will run on a background thread (probably not what they want)
if (patches.Postfixes.Count > 0)
if (patches?.Postfixes?.Count > 0)
{
Log.Warning("[BetterLoading] One or more mods have Harmony-Postfixed StaticConstructorOnStartupUtility#CallAll. This is likely to cause errors or undesired behavior, as BetterLoading changes this method to be called from another Thread than the UI one. A list of patches follows.");
Log.Warning("[BetterLoading] In addition, when BetterLoading is installed, this postfix will run before any static constructors do, so it likely will not behave as the modder intended anyway.");
Expand All @@ -95,8 +110,10 @@ public static IEnumerator StaticConstructAll()
throw new InvalidOperationException("StaticConstructAll called before _queue was set!");

GlobalTimingData.TicksStartedCctors = DateTime.UtcNow.Ticks;
Log.Message("[BetterLoading] Starting Antifreeze(tm) StaticConstructorCaller. Synchronizing retransmission chronicity...");
Log.Message("[BetterLoading] Starting StaticConstructAll() to run class constructors without freezing the game...");
Utils.DebugLog($"Going to run class constructor on {_toRun.Count} types");
Application.runInBackground = true;

foreach (var type in _toRun)
{
try
Expand All @@ -117,12 +134,13 @@ public static IEnumerator StaticConstructAll()
yield return null;
}

Application.runInBackground = Prefs.RunInBackground;

try
{
Log.Message("[BetterLoading] Finished calling static constructors at " + DateTime.Now.ToLongTimeString() + ".");
var existing = LongEventHandlerMirror.ToExecuteWhenFinished;

Utils.DebugLog($"Finished running class constructors. ToExecuteWhenFinished length (tasks added as a result of calling QueueLongEvent while running static cctors) is {existing.Count}, " +
$"_queue length is {_queue.Count}");

// Log.Message($"[BetterLoading] Restoring original job queue of {_queue.Count} item/s and merging with any just added (looking at you, Fluffy) ({existing.Count} entries have been added).");
if (existing.Count > 0 && _queue.Count == 0)
Expand All @@ -148,6 +166,14 @@ public static IEnumerator StaticConstructAll()
// Log.Message($"[BetterLoading] Job queue restored. Running GC...");

StaticConstructorOnStartupUtility.coreStaticAssetsLoaded = true;

var isOnePointFour = VersionControl.CurrentMajor == 1 && VersionControl.CurrentMinor == 4;
if (isOnePointFour)
{
Utils.DebugLog("1.4 detected, calling BakeStaticAtlases");
GlobalTextureAtlasManager.BakeStaticAtlases();
Utils.DebugLog("BakeStaticAtlases finished");
}

GC.Collect(int.MaxValue, GCCollectionMode.Forced); //Copied from PlayDataLoader

Expand All @@ -161,32 +187,45 @@ public static IEnumerator StaticConstructAll()
{
StageRunPostFinalizeCallbacks.ShouldInterceptNext = true;
_finishedProcessing = true;
Application.runInBackground = Prefs.RunInBackground;
// Log.Message("[BetterLoading] Lock released.");
}
}

public static bool PreCallAll()
{
// Log.Message("Static constructors? Oh, sit down, vanilla, I'll do it myself. Starting now, at " + DateTime.Now.ToLongTimeString(), true);
_toRun = GenTypes.AllTypesWithAttribute<StaticConstructorOnStartup>().ToList();

BetterLoadingMain.LoadingScreen!.StartCoroutine(StaticConstructAll());

// Log.Message("[BetterLoading] Overriding LongEventHandler's toExecuteWhenFinished", true);

var result = LongEventHandlerMirror.ToExecuteWhenFinished;

Utils.DebugLog($"Harmony prefix on StaticConstructorOnStartupUtility.CallAll() called. Found {_toRun.Count} types with StaticConstructorOnStartup attribute, and {result.Count} actions in ToExecuteWhenFinished.");
if (BetterLoadingConfigManager.Config.VerboseLogging)
{
Utils.DebugLog("Entries in ToExecuteWhenFinished:");
result.ForEach(action => Utils.DebugLog($" - {action.Method.DeclaringType?.Name} :: {action.Method.Name} ({action.Method.DeclaringType})"));
Utils.DebugLog("End of ToExecuteWhenFinished entries");
}

// Log.Message($"[BetterLoading] Got list of pending actions: {result}. Removing up to and including the static constructor call...", true);

var staticCallIdx = result.FindIndex(i => i.Method.DeclaringType?.Name == "PlayDataLoader" && i.Method.Name.Contains("m__2"));
var isOnePointFour = VersionControl.CurrentMajor == 1 && VersionControl.CurrentMinor == 4;
var methodNameSubstring = isOnePointFour ? "b__4_5" : "m__2";
var staticCallIdx = result.FindIndex(i => i.Method.DeclaringType?.DeclaringType?.Name == nameof(PlayDataLoader) && i.Method.Name.Contains(methodNameSubstring));

// Log.Message($"[BetterLoading] (Which is at index {staticCallIdx} of {result.Count})", true);

Utils.DebugLog($"PlayDataLoader static constructor call index is at position {staticCallIdx} of {result.Count} in the queue. Everything before this will be removed, and the remaining tasks saved");

result = result.Skip(staticCallIdx + 1).Take(int.MaxValue).ToList(); //Remove the static constructor call

_queue = result;

Utils.DebugLog($"After removing the static constructor call, the queue has {result.Count} items. Saving this for later.");

BetterLoadingMain.LoadingScreen!.StartCoroutine(StaticConstructAll());

// Log.Message($"[BetterLoading] Updating field in LEH to a new list of size {result.Count}...", true);
LongEventHandlerMirror.ToExecuteWhenFinished = new();

LongEventHandler.QueueLongEvent(WaitForStaticCtors, null, true, null);
Expand Down
8 changes: 4 additions & 4 deletions Source/Stage/InitialLoad/9StageRunPostFinalizeCallbacks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public StageRunPostFinalizeCallbacks(Harmony instance) : base(instance)

public override string GetStageName()
{
return "Running Post-Finalize Callbacks";
return "Running Post-Static-Constructor Long Events";
}

public override string? GetCurrentStepName()
Expand Down Expand Up @@ -110,24 +110,24 @@ public static bool PreExecToExecWhenFinished(List<Action> ___toExecuteWhenFinish
{
if (initialNumTasksToRun != _numTasksToRun)
Log.Message($"[BetterLoading] Processed an additional {_numTasksToRun - initialNumTasksToRun} post-finalize tasks.");
GlobalTimingData.TicksFinishedPostFinalize = DateTime.UtcNow.Ticks;
_finishedExecuting = true;
})
);

LongEventHandler.QueueLongEvent(() =>
{
Thread.Sleep(500);
Thread.Sleep(0);

// Log.Message("[BetterLoading] Blocking loading screen from being dismissed until post-finalize actions are complete.");
while (!_finishedExecuting)
{
Thread.Sleep(500); //Wait
Thread.Sleep(0); //Wait
}

Log.Message("[BetterLoading] Obtained lock, assuming we're done with post-finalize.");

Thread.Sleep(0);
GlobalTimingData.TicksFinishedPostFinalize = DateTime.UtcNow.Ticks;

_finishedExecuting = true;
}, null, true, null);
Expand Down
6 changes: 6 additions & 0 deletions Source/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,11 @@ public static bool HarmonyPatchCancelMethod()
{
return false;
}

public static void DebugLog(string message)
{
if (BetterLoadingConfigManager.Config.VerboseLogging)
Verse.Log.Message($"[BetterLoading Verbose] {message}");
}
}
}

0 comments on commit 47de726

Please sign in to comment.