Skip to content
This repository has been archived by the owner on Mar 29, 2022. It is now read-only.

Commit

Permalink
added support for delayed loading of other mods
Browse files Browse the repository at this point in the history
delayed loading allows mods to refer to other modules' assemblies
  • Loading branch information
Tyler-IN committed Apr 5, 2020
1 parent 12a3af7 commit 2c6b3eb
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 114 deletions.
44 changes: 19 additions & 25 deletions SubModule.xml
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Module>
<Name value="Community Patch"/>
<Id value="CommunityPatch"/>
<Version value="v0.0.1"/>
<SingleplayerModule value="true"/>
<MultiplayerModule value="false"/>
<Official value="false" />
<DependedModules>
<DependedModule Id="Native"/>
<DependedModule Id="SandBoxCore"/>
<DependedModule Id="Sandbox"/>
<DependedModule Id="StoryMode"/>
</DependedModules>
<SubModules>
<SubModule>
<Name value="CommunityPatch"/>
<DLLName value="CommunityPatch.dll"/>
<SubModuleClassType value="CommunityPatch.CommunityPatchSubModule"/>
<Tags>
<Tag key="DedicatedServerType" value="none" />
<Tag key="IsNoRenderModeElement" value="false" />
</Tags>
</SubModule>
</SubModules>

<Xmls/>
<Name value="Community Patch"/>
<Id value="CommunityPatch"/>
<Version value="v0.0.1"/>
<SingleplayerModule value="true"/>
<MultiplayerModule value="true"/>
<Official value="false"/>
<DependedModules/>
<SubModules>
<SubModule>
<Name value="CommunityPatch"/>
<DLLName value="CommunityPatch.dll"/>
<SubModuleClassType value="CommunityPatch.CommunityPatchSubModule"/>
<Tags>
<Tag key="DedicatedServerType" value="none"/>
<Tag key="IsNoRenderModeElement" value="false"/>
</Tags>
</SubModule>
</SubModules>
<Xmls/>
</Module>
40 changes: 9 additions & 31 deletions src/CommunityPatch/CommunityPatch.csproj
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<CopyLocalLockFileAssemblies>True</CopyLocalLockFileAssemblies>
<MSBuildWarningsAsMessages>MSB3277</MSBuildWarningsAsMessages>
<Configurations>Release;Debug</Configurations>
<Platforms>X64</Platforms>
<Platforms>x64</Platforms>
<RootNamespace />
<Version>0.0.1</Version>
<Title>Community Patch</Title>
Expand All @@ -14,7 +13,6 @@
<OutDir>../../bin/Win64_Shipping_Client</OutDir>
<LangVersion>8</LangVersion>
</PropertyGroup>

<ItemGroup>
<Reference Include="..\..\..\..\bin\Win64_Shipping_Client\System.*.dll">
<HintPath>%(Identity)</HintPath>
Expand All @@ -28,36 +26,16 @@
<HintPath>%(Identity)</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="..\..\..\Native\bin\Win64_Shipping_Client\TaleWorlds.*.dll">
<HintPath>%(Identity)</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="..\..\..\SandBox\bin\Win64_Shipping_Client\TaleWorlds.*.dll">
<HintPath>%(Identity)</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="..\..\..\SandBoxCore\bin\Win64_Shipping_Client\*.dll">
<HintPath>%(Identity)</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="..\..\..\StoryMode\bin\Win64_Shipping_Client\*.dll">
<HintPath>%(Identity)</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>

<ItemGroup>
<None Include="..\..\SubModule.xml">
<Link>SubModule.xml</Link>
</None>
<None Include="..\..\SubModule.xml">
<Link>SubModule.xml</Link>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Fody" Version="6.1.1" />
<PackageReference Include="HardwareProviders.CPU.Standard" Version="2.0.1" />
<PackageReference Include="HardwareProviders.Standard" Version="2.0.1" />
<PackageReference Include="ModuleInit.Fody" Version="2.1.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.1" />
<PackageReference Include="Tomlyn" Version="0.1.2" />
<PackageReference Include="HardwareProviders.CPU.Standard" Version="2.0.1" />
<PackageReference Include="HardwareProviders.Standard" Version="2.0.1" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.7.1" />
<PackageReference Include="Tomlyn" Version="0.1.2" />
</ItemGroup>
</Project>
</Project>
24 changes: 12 additions & 12 deletions src/CommunityPatch/CommunityPatch.sln
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageRelease", "..\..\too
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|X64 = Debug|X64
Release|X64 = Release|X64
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Debug|X64.Build.0 = Debug|X64
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Debug|X64.Deploy.0 = Debug|X64
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Debug|X64.ActiveCfg = Debug|X64
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Release|X64.Build.0 = Release|X64
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Release|X64.Deploy.0 = Release|X64
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Release|X64.ActiveCfg = Release|X64
{E4C55A69-67F6-4F62-8336-B5665C5B55FF}.Debug|X64.ActiveCfg = Debug|Any CPU
{E4C55A69-67F6-4F62-8336-B5665C5B55FF}.Debug|X64.Build.0 = Debug|Any CPU
{E4C55A69-67F6-4F62-8336-B5665C5B55FF}.Release|X64.ActiveCfg = Release|Any CPU
{E4C55A69-67F6-4F62-8336-B5665C5B55FF}.Release|X64.Build.0 = Release|Any CPU
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Debug|x64.Build.0 = Debug|x64
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Debug|x64.Deploy.0 = Debug|x64
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Debug|x64.ActiveCfg = Debug|x64
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Release|x64.Build.0 = Release|x64
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Release|x64.Deploy.0 = Release|x64
{D32B20F4-AB8C-4FDD-9C02-D71AEB37700B}.Release|x64.ActiveCfg = Release|x64
{E4C55A69-67F6-4F62-8336-B5665C5B55FF}.Debug|x64.ActiveCfg = Debug|Any CPU
{E4C55A69-67F6-4F62-8336-B5665C5B55FF}.Debug|x64.Build.0 = Debug|Any CPU
{E4C55A69-67F6-4F62-8336-B5665C5B55FF}.Release|x64.ActiveCfg = Release|Any CPU
{E4C55A69-67F6-4F62-8336-B5665C5B55FF}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
137 changes: 137 additions & 0 deletions src/CommunityPatch/CommunityPatchSubModule.Initialization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using TaleWorlds.Library;
using TaleWorlds.MountAndBlade;
using Module = TaleWorlds.MountAndBlade.Module;

namespace CommunityPatch {

public partial class CommunityPatchSubModule {

static CommunityPatchSubModule() {
// catch and record exceptions
AppDomain.CurrentDomain.FirstChanceException += (sender, args) => {
if (RecordFirstChanceExceptions)
RecordedFirstChanceExceptions.AddLast(args.Exception);
};
AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
RecordedUnhandledExceptions.AddLast((Exception) args.ExceptionObject);
CopyDiagnosticsToClipboard();
};

// TODO:
/*
AppDomain.CurrentDomain.TypeResolve += (sender, args) => {
return null;
};
*/

// help delay loaded libs refer to mods they depend on
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => {
MBSubModuleBase reqSm = null;
foreach (var sm in Module.CurrentModule.SubModules) {
var smAsm = sm.GetType().Assembly;
if (smAsm == args.RequestingAssembly)
reqSm = sm;
}

if (reqSm == null)
return null;

var resolvable = new LinkedList<(ModuleInfo Mod, SubModuleInfo SubMod)>();
ModuleInfo reqMi = null;
SubModuleInfo reqSmi = null;
var modules = ModuleInfo.GetModules();
foreach (var mi in modules) {
foreach (var smi in mi.SubModules) {
if (smi.Assemblies.Contains(args.Name))
resolvable.AddLast((mi, smi));

if (smi.SubModuleClassType != reqSm.GetType().FullName)
continue;

reqMi = mi;
reqSmi = smi;
}
}

if (reqSmi == null)
return null;

foreach (var modId in reqMi.DependedModuleIds) {
foreach (var resolution in resolvable) {
if (modId != resolution.Mod.Id)
continue;

var modDir = Path.GetDirectoryName(ModuleInfo.GetPath(modId));
if (modDir == null)
continue;

var modPath = Path.Combine(modDir, "bin", Common.ConfigName, args.Name + ".dll");
if (File.Exists(modPath))
return Assembly.LoadFile(modPath);
}
}

return null;
};
}

private static void LoadDelayedSubModules() {
foreach (var mod in ModuleInfo.GetModules()) {
var id = mod.Id;
var subModsXmlPath = ModuleInfo.GetPath(id);
var modDir = Path.GetDirectoryName(subModsXmlPath);
if (modDir == null)
continue;

var subModsXml = new XmlDocument();
subModsXml.Load(subModsXmlPath);
var delayedSubMods = subModsXml.SelectNodes("/Module/DelayedSubModules/SubModule")?.OfType<XmlElement>();
if (delayedSubMods == null)
continue;

var main = Module.CurrentModule;
var typeMain = typeof(Module);

foreach (var elem in delayedSubMods) {
var delayedSubModInfo = new SubModuleInfo();
delayedSubModInfo.LoadFrom(elem);

var dllPath = Path.Combine(modDir, "bin", Common.ConfigName, delayedSubModInfo.DLLName);

var subModAsm = AssemblyLoader.LoadFrom(dllPath);
var subModType = subModAsm.GetType(delayedSubModInfo.SubModuleClassType);
if (!typeof(MBSubModuleBase).IsAssignableFrom(subModType))
continue;

var delayLoadedSubMod = (MBSubModuleBase) subModType.InvokeMember(".ctor",
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance,
null, null, new object[0]);

typeMain.InvokeMember("AddSubModule",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod,
null, main, new object[] {subModAsm, delayedSubModInfo.SubModuleClassType});

var subMods = (ICollection<MBSubModuleBase>) typeMain.InvokeMember("_submodules",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField,
null, main, new object[0]);

subMods.Add(delayLoadedSubMod);

subModType.InvokeMember(nameof(OnSubModuleLoad),
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod,
null, delayLoadedSubMod, new object[0]);
}
}
}

}

}
17 changes: 17 additions & 0 deletions src/CommunityPatch/CommunityPatchSubModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using JetBrains.Annotations;
using TaleWorlds.CampaignSystem;
using TaleWorlds.Core;
using TaleWorlds.Library;
using TaleWorlds.Localization;
using TaleWorlds.MountAndBlade;
using Module = TaleWorlds.MountAndBlade.Module;
Expand Down Expand Up @@ -81,6 +84,20 @@ protected override void OnSubModuleLoad() {
base.OnSubModuleLoad();
}

private bool _ticked = false;

protected override void OnApplicationTick(float dt) {
if (!_ticked) {
_ticked = true;
SynchronizationContext.Current.Post(_ => {
LoadDelayedSubModules();
}, null);
base.OnApplicationTick(dt);
}

// other stuff?
}

private static void ShowMessage(string msg)
=> InformationManager.DisplayMessage(new InformationMessage(msg));

Expand Down
3 changes: 0 additions & 3 deletions src/CommunityPatch/FodyWeavers.xml

This file was deleted.

26 changes: 0 additions & 26 deletions src/CommunityPatch/FodyWeavers.xsd

This file was deleted.

17 changes: 0 additions & 17 deletions src/CommunityPatch/ModuleInitializer.cs

This file was deleted.

0 comments on commit 2c6b3eb

Please sign in to comment.