Skip to content

Commit

Permalink
Merge pull request #354 from BUTR/dev
Browse files Browse the repository at this point in the history
v2.8.13
  • Loading branch information
Aragas authored Sep 12, 2023
2 parents 3315fa8 + 277b011 commit 65da602
Show file tree
Hide file tree
Showing 13 changed files with 121 additions and 135 deletions.
2 changes: 1 addition & 1 deletion build/common.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<!--Development Variables-->
<PropertyGroup>
<!--Module Version-->
<Version>2.8.12</Version>
<Version>2.8.13</Version>
<!--Harmony Version-->
<HarmonyVersion>2.2.2</HarmonyVersion>
<HarmonyExtensionsVersion>3.2.0.77</HarmonyExtensionsVersion>
Expand Down
5 changes: 5 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
---------------------------------------------------------------------------------------------------
Version: 2.8.13
Game Versions: v1.0.0,v1.0.1,v1.0.2,v1.0.3,v1.1.0,v1.1.1,v1.1.2,v1.1.3,v1.1.4,v1.1.5,v1.2.0,v1.2.1,v1.2.2,v1.2.3
* Might have fixed a reported save issue
* Reflection improvement
---------------------------------------------------------------------------------------------------
Version: 2.8.12
Game Versions: v1.0.0,v1.0.1,v1.0.2,v1.0.3,v1.1.0,v1.1.1,v1.1.2,v1.1.3,v1.1.4,v1.1.5,v1.2.0,v1.2.1,v1.2.2,v1.2.3
* Improved logging
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ internal sealed class MBGameManagerPatch
private static readonly MethodInfo? miTargetMethodGameStart = AccessTools2.Method("TaleWorlds.MountAndBlade.MBGameManager:OnGameStart");
private static readonly MethodInfo? miTargetMethodGameEnd = AccessTools2.Method("TaleWorlds.MountAndBlade.MBGameManager:OnGameEnd");

private static readonly MethodInfo? miPatchMethod = AccessTools2.Method("Bannerlord.ButterLib.Implementation.MBSubModuleBaseExtended.Patches.MBGameManagerPatch:Transpiler");
private static readonly MethodInfo? miPatchMethod = SymbolExtensions2.GetMethodInfo((IEnumerable<CodeInstruction> x, MethodBase y) => Transpiler(x, y));

private static readonly MethodInfo? miMBSubModuleBaseOnGameStartEvent = AccessTools2.Method("TaleWorlds.MountAndBlade.MBSubModuleBase:OnGameStart");
private static readonly MethodInfo? miMBSubModuleBaseOnGameEndEvent = AccessTools2.Method("TaleWorlds.MountAndBlade.MBSubModuleBase:OnGameEnd");

private static readonly MethodInfo? miDelayedOnGameStartEventCaller = AccessTools2.Method("Bannerlord.ButterLib.Implementation.MBSubModuleBaseExtended.Patches.MBGameManagerPatch:DelayedOnGameStartEvent");
private static readonly MethodInfo? miDelayedOnGameEndEventCaller = AccessTools2.Method("Bannerlord.ButterLib.Implementation.MBSubModuleBaseExtended.Patches.MBGameManagerPatch:DelayedOnGameEndEvent");
private static readonly MethodInfo? miDelayedOnGameStartEventCaller = SymbolExtensions2.GetMethodInfo((Game x, IGameStarter y) => DelayedOnGameStartEvent(x, y));
private static readonly MethodInfo? miDelayedOnGameEndEventCaller = SymbolExtensions2.GetMethodInfo((Game x) => DelayedOnGameEndEvent(x));

internal static bool Enable(Harmony harmony)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ internal sealed class ModulePatch
private static readonly Type? TargetType = typeof(TWModule);

private static readonly MethodInfo? miTargetMethodUnLoad = AccessTools2.Method(TargetType, "FinalizeSubModules");
private static readonly MethodInfo? miPatchMethodUnLoad = AccessTools2.Method("Bannerlord.ButterLib.Implementation.MBSubModuleBaseExtended.Patches.ModulePatch:FinalizeSubModulesPostfix");
private static readonly MethodInfo? miPatchMethodUnLoad = SymbolExtensions2.GetMethodInfo((TWModule x) => FinalizeSubModulesPostfix(x));

private static readonly MethodInfo? miTargetMethodScreenAsRoot = AccessTools2.Method(TargetType, "SetInitialModuleScreenAsRootScreen");
private static readonly MethodInfo? miPatchMethodScreenAsRoot = AccessTools2.Method("Bannerlord.ButterLib.Implementation.MBSubModuleBaseExtended.Patches.ModulePatch:Transpiler");
private static readonly MethodInfo? miPatchMethodScreenAsRoot = SymbolExtensions2.GetMethodInfo((IEnumerable<CodeInstruction> x) => Transpiler(x));

private static readonly MethodInfo? miMBSubModuleBaseScreenAsRootEvent = AccessTools2.Method("TaleWorlds.MountAndBlade.MBSubModuleBase:OnBeforeInitialModuleScreenSetAsRoot");
private static readonly MethodInfo? miDelayedScreenAsRootEventCaller = AccessTools2.Method("Bannerlord.ButterLib.Implementation.MBSubModuleBaseExtended.Patches.ModulePatch:DelayedScreenAsRootEvent");
private static readonly MethodInfo? miDelayedScreenAsRootEventCaller = SymbolExtensions2.GetMethodInfo((TWModule x) => DelayedScreenAsRootEvent(x));

internal static bool Enable(Harmony harmony)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,17 @@ static bool DoesNotExist(MBGUID objectId)

public void SetVariable(MBObjectBase @object, string name, object? data) => _vars[DataKey.Make(@object, name)] = data;

#nullable disable
public bool TryGetVariable<T>(MBObjectBase @object, string name, out T value)
public bool TryGetVariable<T>(MBObjectBase @object, string name, out T? value)
{
if (_vars.TryGetValue(DataKey.Make(@object, name), out var val) && (val is T || val is null))
if (_vars.TryGetValue(DataKey.Make(@object, name), out var val) && val is T or null)
{
value = (T) val;
value = (T?) val;
return true;
}

value = default;
return false;
}
#nullable restore

/* Flags Implementation */

Expand All @@ -101,24 +99,10 @@ public bool TryGetVariable<T>(MBObjectBase @object, string name, out T value)

/* DataKey Implementation */

private sealed class DataKey : IEquatable<DataKey>
private sealed record DataKey([field: SaveableField(0)] MBGUID ObjectId, [field: SaveableField(1)] string? Key)
{
[SaveableField(0)]
internal readonly MBGUID ObjectId;

[SaveableField(1)]
internal readonly string? Key;

private DataKey(MBGUID objectId, string key) => (ObjectId, Key) = (objectId, key);

internal static DataKey Make(MBObjectBase obj, string key) => new(obj.Id, key);

public bool Equals(DataKey? other) => ObjectId == other?.ObjectId && !(Key is null || other.Key is null) && Key.Equals(other.Key);

public override bool Equals(object? obj) => obj is DataKey k && Equals(k);

public override int GetHashCode() => HashCode.Combine(ObjectId, Key);

public override string ToString() => $"{ObjectId}::{Key}";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,122 +21,119 @@ internal sealed class CampaignBehaviorManagerPatch
private delegate void SaveBehaviorDataDelegate(CampaignBehaviorBase campaignBehavior);
private delegate void LoadBehaviorDataDelegate(CampaignBehaviorBase campaignBehavior);

private static ILogger _log = default!;
private static readonly MethodInfo? OnGameLoadedTargetMI =
AccessTools2.Method("TaleWorlds.CampaignSystem.CampaignBehaviors.CampaignBehaviorManager:OnGameLoaded") ??
AccessTools2.Method("TaleWorlds.CampaignSystem.CampaignBehaviors.CampaignBehaviorManager:LoadBehaviorData");

private static readonly MethodInfo? OnBeforeSaveTargetMI =
AccessTools2.Method("TaleWorlds.CampaignSystem.CampaignBehaviors.CampaignBehaviorManager:OnBeforeSave");

// Application:
private static readonly MethodInfo? OnGameLoadedPatchMI = SymbolExtensions2.GetMethodInfo((object? x) => OnGameLoadedPrefix(x));
private static readonly MethodInfo? OnBeforeSavePatchMI = SymbolExtensions2.GetMethodInfo((object? x) => OnBeforeSavePostfix(x));

internal static void Enable(Harmony harmony)
{
var provider = ButterLibSubModule.Instance?.GetServiceProvider() ?? ButterLibSubModule.Instance?.GetTempServiceProvider();
_log = provider?.GetService<ILogger<CampaignBehaviorManagerPatch>>() ?? NullLogger<CampaignBehaviorManagerPatch>.Instance;
var log = provider?.GetService<ILogger<CampaignBehaviorManagerPatch>>() ?? NullLogger<CampaignBehaviorManagerPatch>.Instance;

if (OnGameLoadedTargetMI is null)
_log.LogError("{Method} is null", nameof(OnGameLoadedTargetMI));
log.LogError("{Method} is null", nameof(OnGameLoadedTargetMI));
if (OnBeforeSaveTargetMI is null)
_log.LogError("{Method} is null", nameof(OnBeforeSaveTargetMI));
log.LogError("{Method} is null", nameof(OnBeforeSaveTargetMI));
if (OnGameLoadedPatchMI is null)
_log.LogError("{Method} is null", nameof(OnGameLoadedPatchMI));
log.LogError("{Method} is null", nameof(OnGameLoadedPatchMI));
if (OnBeforeSavePatchMI is null)
_log.LogError("{Method} is null", nameof(OnBeforeSavePatchMI));
if (LoadBehaviorDataMI is null)
_log.LogError("{Method} is null", nameof(LoadBehaviorDataMI));
if (SaveBehaviorDataMI is null)
_log.LogError("{Method} is null", nameof(SaveBehaviorDataMI));
if (CampaignBehaviorDataStoreT is null)
_log.LogError("{Method} is null", nameof(CampaignBehaviorDataStoreT));

if (OnGameLoadedTargetMI is null || OnBeforeSaveTargetMI is null ||
OnGameLoadedPatchMI is null || OnBeforeSavePatchMI is null ||
LoadBehaviorDataMI is null || SaveBehaviorDataMI is null ||
CampaignBehaviorDataStoreT is null)
{
log.LogError("{Method} is null", nameof(OnBeforeSavePatchMI));

if (OnGameLoadedTargetMI is null || OnBeforeSaveTargetMI is null || OnGameLoadedPatchMI is null || OnBeforeSavePatchMI is null)
return;
}

harmony.Patch(OnGameLoadedTargetMI, prefix: new HarmonyMethod(OnGameLoadedPatchMI));
harmony.Patch(OnBeforeSaveTargetMI, postfix: new HarmonyMethod(OnBeforeSavePatchMI));
}

internal static void Disable(Harmony harmony) { }

// Target and patch methods:

private static readonly MethodInfo? OnGameLoadedTargetMI =
AccessTools2.Method("TaleWorlds.CampaignSystem.CampaignBehaviors.CampaignBehaviorManager:OnGameLoaded") ??
AccessTools2.Method("TaleWorlds.CampaignSystem.CampaignBehaviors.CampaignBehaviorManager:InitializeCampaignBehaviors");

private static readonly MethodInfo? OnBeforeSaveTargetMI =
AccessTools2.Method("TaleWorlds.CampaignSystem.CampaignBehaviors.CampaignBehaviorManager:OnBeforeSave");

private static readonly MethodInfo? OnGameLoadedPatchMI =
AccessTools2.Method("Bannerlord.ButterLib.Implementation.ObjectSystem.Patches.CampaignBehaviorManagerPatch:OnGameLoadedPrefix");

private static readonly MethodInfo? OnBeforeSavePatchMI =
AccessTools2.Method("Bannerlord.ButterLib.Implementation.ObjectSystem.Patches.CampaignBehaviorManagerPatch:OnBeforeSavePostfix");

// Necessary reflection:

private static readonly Type? CampaignBehaviorDataStoreT =
typeof(Campaign).Assembly.GetType("TaleWorlds.CampaignSystem.CampaignBehaviorDataStore");

private static readonly MethodInfo? LoadBehaviorDataMI = AccessTools2.Method(CampaignBehaviorDataStoreT!, "LoadBehaviorData");

private static readonly MethodInfo? SaveBehaviorDataMI = AccessTools2.Method(CampaignBehaviorDataStoreT!, "SaveBehaviorData");

// Patch implementation:

[MethodImpl(MethodImplOptions.NoInlining)]
private static void OnGameLoadedPrefix(object? ____campaignBehaviorDataStore)
{
var mbObjectVariableStorage = ButterLibSubModule.Instance?.GetServiceProvider()?.GetService<IMBObjectExtensionDataStore>();
var provider = ButterLibSubModule.Instance?.GetServiceProvider() ?? ButterLibSubModule.Instance?.GetTempServiceProvider();
var log = provider?.GetService<ILogger<CampaignBehaviorManagerPatch>>() ?? NullLogger<CampaignBehaviorManagerPatch>.Instance;

if (mbObjectVariableStorage is null)
if (____campaignBehaviorDataStore is null)
{
log.LogError("{Method}: {Variable} is null", nameof(OnGameLoadedPrefix), nameof(____campaignBehaviorDataStore));
return;
}

if (ButterLibSubModule.Instance?.GetServiceProvider()?.GetService<IMBObjectExtensionDataStore>() is not { } mbObjectVariableStorage)
{
_log.LogError("{Method}: {Variable} is null", nameof(OnGameLoadedPrefix), nameof(mbObjectVariableStorage));
log.LogError("{Method}: {Variable} is null", nameof(OnGameLoadedPrefix), nameof(mbObjectVariableStorage));
return;
}

if (mbObjectVariableStorage is not CampaignBehaviorBase storageBehavior)
{
_log.LogError("{Method}: {Variable} is not a CampaignBehaviorBase", nameof(OnGameLoadedPrefix), nameof(mbObjectVariableStorage));
log.LogError("{Method}: {Variable} is not a CampaignBehaviorBase", nameof(OnGameLoadedPrefix), nameof(mbObjectVariableStorage));
return;
}

if (____campaignBehaviorDataStore is null)
if (AccessTools2.GetDelegate<LoadBehaviorDataDelegate>(____campaignBehaviorDataStore, ____campaignBehaviorDataStore.GetType(), "LoadBehaviorData") is not { } loadBehaviorData)
{
_log.LogError("{Method}: {Variable} is null", nameof(OnGameLoadedPrefix), nameof(____campaignBehaviorDataStore));
log.LogError("{Method}: {Variable} is not a SaveBehaviorDataDelegate", nameof(OnGameLoadedPrefix), nameof(loadBehaviorData));
return;
}

var loadBehaviorData = AccessTools2.GetDelegate<LoadBehaviorDataDelegate>(____campaignBehaviorDataStore, LoadBehaviorDataMI!);
loadBehaviorData?.Invoke(storageBehavior);
try
{
loadBehaviorData(storageBehavior);
}
catch (Exception e)
{
log.LogError(e, "{Method}", nameof(OnGameLoadedPrefix));
return;
}
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static void OnBeforeSavePostfix(object? ____campaignBehaviorDataStore)
{
var mbObjectVariableStorage = ButterLibSubModule.Instance?.GetServiceProvider()?.GetService<IMBObjectExtensionDataStore>();
var provider = ButterLibSubModule.Instance?.GetServiceProvider() ?? ButterLibSubModule.Instance?.GetTempServiceProvider();
var log = provider?.GetService<ILogger<CampaignBehaviorManagerPatch>>() ?? NullLogger<CampaignBehaviorManagerPatch>.Instance;

if (mbObjectVariableStorage is null)
if (____campaignBehaviorDataStore is null)
{
log.LogError("{Method}: {Variable} is null", nameof(OnBeforeSavePostfix), nameof(____campaignBehaviorDataStore));
return;
}

if (ButterLibSubModule.Instance?.GetServiceProvider()?.GetService<IMBObjectExtensionDataStore>() is not { } mbObjectVariableStorage)
{
_log.LogError("{Method}: {Variable} is null", nameof(OnBeforeSavePostfix), nameof(mbObjectVariableStorage));
log.LogError("{Method}: {Variable} is null", nameof(OnBeforeSavePostfix), nameof(mbObjectVariableStorage));
return;
}

if (mbObjectVariableStorage is not CampaignBehaviorBase storageBehavior)
{
_log.LogError("{Method}: {Variable} is not a CampaignBehaviorBase", nameof(OnBeforeSavePostfix), nameof(mbObjectVariableStorage));
log.LogError("{Method}: {Variable} is not a CampaignBehaviorBase", nameof(OnBeforeSavePostfix), nameof(mbObjectVariableStorage));
return;
}

if (____campaignBehaviorDataStore is null)
if (AccessTools2.GetDelegate<SaveBehaviorDataDelegate>(____campaignBehaviorDataStore, ____campaignBehaviorDataStore.GetType(), "SaveBehaviorData") is not { } saveBehaviorData)
{
_log.LogError("{Method}: {Variable} is null", nameof(OnBeforeSavePostfix), nameof(____campaignBehaviorDataStore));
log.LogError("{Method}: {Variable} is not a SaveBehaviorDataDelegate", nameof(OnBeforeSavePostfix), nameof(saveBehaviorData));
return;
}

var saveBehaviorData = AccessTools2.GetDelegate<SaveBehaviorDataDelegate>(____campaignBehaviorDataStore, SaveBehaviorDataMI!);
saveBehaviorData?.Invoke(storageBehavior);
try
{
saveBehaviorData(storageBehavior);
}
catch (Exception e)
{
log.LogError(e, "{Method}", nameof(OnBeforeSavePostfix));
return;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ internal static bool Enable(Harmony harmony)
{
return harmony.TryPatch(
AccessTools2.Constructor(typeof(CampaignBehaviorBase)),
postfix: AccessTools2.Method("Bannerlord.ButterLib.Implementation.SaveSystem.Patches.BehaviourNamePatch:CampaignBehaviorBaseCtorPostfix"));
postfix: SymbolExtensions2.GetMethodInfo((CampaignBehaviorBase x, string? y) => CampaignBehaviorBaseCtorPostfix(x, ref y)));
}

internal static bool Disable(Harmony harmony)
{
harmony.Unpatch(
AccessTools2.Constructor(typeof(CampaignBehaviorBase)),
AccessTools2.Method("Bannerlord.ButterLib.Implementation.SaveSystem.Patches.BehaviourNamePatch:CampaignBehaviorBaseCtorPostfix"));
SymbolExtensions2.GetMethodInfo((CampaignBehaviorBase x, string? y) => CampaignBehaviorBaseCtorPostfix(x, ref y)));

return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ internal static bool Disable(Harmony harmony)

private static bool CanAddTypeDefinition(TypeDefinitionBase? typeDef, Dictionary<Type, TypeDefinitionBase> typeDict)
{
if (typeDef is null || typeDef.Type is null)
if (typeDef?.Type is null)
return false;

if (typeDict.ContainsKey(typeDef.Type))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,17 @@ internal static bool Disable(Harmony harmony)
return true;
}

private static readonly Type? TargetType = typeof(MetaData).Assembly.GetType("TaleWorlds.SaveSystem.TypeExtensions");
private static readonly Type? TargetType = AccessTools2.TypeByName("TaleWorlds.SaveSystem.TypeExtensions");
private static readonly Type[] TargetMethodParams = { typeof(Type), typeof(ContainerType).MakeByRefType() };
private static readonly MethodInfo? TargetMethod = AccessTools2.Method(TargetType!, "IsContainer", TargetMethodParams);
private static readonly MethodInfo? PatchMethod = AccessTools2.Method("Bannerlord.ButterLib.Implementation.SaveSystem.Patches.TypeExtensionsPatch:IsContainerPrefix");
private static readonly MethodInfo? PatchMethod = SymbolExtensions2.GetMethodInfo((Type x, ContainerType y, bool z) => IsContainerPrefix(x, out y, ref z));

// ReSharper disable once RedundantAssignment
private static bool IsContainerPrefix(Type type, out ContainerType containerType, ref bool __result)
{
containerType = ContainerType.None;

if (type.IsGenericType && !type.IsGenericTypeDefinition)
if (type is {IsGenericType: true, IsGenericTypeDefinition: false})
type = type.GetGenericTypeDefinition();

if (type.IsArray)
Expand Down
Loading

0 comments on commit 65da602

Please sign in to comment.