Skip to content

Commit

Permalink
v1.1 makes VE compatible with RW 1.2 and adds a null wrapper for Acti…
Browse files Browse the repository at this point in the history
…vator.CreateInstance()
  • Loading branch information
pardeike committed Sep 12, 2021
1 parent 8688920 commit f3a3c92
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 15 deletions.
Binary file added 1.2/Assemblies/VisualExceptions.dll
Binary file not shown.
Binary file added 1.3/Assemblies/VisualExceptions.dll
Binary file not shown.
2 changes: 1 addition & 1 deletion About/Manifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Manifest>
<identifier>net.pardeike.rimworld.mod.visualexceptions</identifier>
<version>1.0.4.0</version>
<version>1.1.0.0</version>
<targetVersions>
<li>1.2.0</li>
<li>1.3.0</li>
Expand Down
Binary file removed Current/Assemblies/0Harmony.dll
Binary file not shown.
Binary file removed Current/Assemblies/VisualExceptions.dll
Binary file not shown.
4 changes: 2 additions & 2 deletions LoadFolders.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<loadFolders>
<v1.2>
<li>/</li>
<li>Current</li>
<li>1.2</li>
</v1.2>
<v1.3>
<li>/</li>
<li>Current</li>
<li>1.3</li>
</v1.3>
</loadFolders>
86 changes: 79 additions & 7 deletions Source/Patcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ static class Patcher
internal static void Apply()
{
var harmony = new Harmony(harmony_id);
_ = new PatchClassProcessor(harmony, typeof(RunloopExceptionHandler)).Patch();
_ = new PatchClassProcessor(harmony, typeof(ExceptionsAndActivatorHandler)).Patch();
_ = new PatchClassProcessor(harmony, typeof(ShowLoadingExceptions)).Patch();
_ = new PatchClassProcessor(harmony, typeof(AddHarmonyTabWhenNecessary)).Patch();
_ = new PatchClassProcessor(harmony, typeof(RememberHarmonyIDs)).Patch();
Expand All @@ -29,16 +29,82 @@ internal static void Apply()
// adds exception handlers
//
[HarmonyPatch]
static class RunloopExceptionHandler
static class ExceptionsAndActivatorHandler
{
static readonly MethodInfo Handle = SymbolExtensions.GetMethodInfo(() => ExceptionState.Handle(null));

static readonly Dictionary<MethodInfo, MethodInfo> createInstanceMethods = new Dictionary<MethodInfo, MethodInfo>
{
{ SymbolExtensions.GetMethodInfo(() => Activator.CreateInstance(typeof(void))), SymbolExtensions.GetMethodInfo(() => PatchedActivator.CreateInstance(typeof(void), null)) },
{ SymbolExtensions.GetMethodInfo(() => Activator.CreateInstance(typeof(void), new object[0])), SymbolExtensions.GetMethodInfo(() => PatchedActivator.CreateInstance(typeof(void), new object[0], null)) },
};

class PatchedActivator
{
static object GetInfo(object obj)
{
if (obj == null) return null;
var def = obj as Def;
if (def != null) return def;
var fields = AccessTools.GetDeclaredFields(def.GetType());
foreach (var field in fields)
if (typeof(Def).IsAssignableFrom(field.FieldType))
{
def = field.GetValue(obj) as Def;
if (def != null) return def;
}
return obj;
}

internal static object CreateInstance(Type type, object obj)
{
if (type != null) return Activator.CreateInstance(type);
var info = GetInfo(obj);
var message = "Activator.CreateInstance(type) called with a null type";
if (info != null) message += $", possible context={info}";
throw new ArgumentNullException(message);
}

internal static object CreateInstance(Type type, object[] objects, object me)
{
if (type != null) return Activator.CreateInstance(type, objects);
var info = GetInfo(me);
var message = $"Activator.CreateInstance(type, object[]) called with a null type, objects=[{objects.Join(o => o?.ToString() ?? "null")}]";
if (info != null) message += $", possible context={info}";
throw new ArgumentNullException(message);
}
}

[HarmonyPriority(int.MaxValue)]
internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
{
var list = instructions.ToList();
var idx = 0;

var found = false;
for (var i = 0; i < list.Count; i++)
if (list[i].opcode == OpCodes.Call)
if (list[i].operand is MethodInfo methodInfo && createInstanceMethods.TryGetValue(methodInfo, out var replacement))
{
if (original.IsStatic)
{
var parameters = original.GetParameters();
var defIndex = parameters.Select(p => p.ParameterType).FirstIndexOf(type => type.IsGenericType == false && type.IsByRef == false && typeof(Def).IsAssignableFrom(type));
if (defIndex >= 0 && defIndex < parameters.Length)
{
list.Insert(i, new CodeInstruction(OpCodes.Ldarg, defIndex));
list[++i].operand = replacement;
found = true;
}
}
else
{
list.Insert(i, new CodeInstruction(OpCodes.Ldarg_0));
list[++i].operand = replacement;
found = true;
}
}

var idx = 0;
try
{
while (true)
Expand All @@ -62,6 +128,7 @@ internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruct
{
Log.Error($"Transpiler Exception: {ex}");
}

if (found == false) return null;
return list.AsEnumerable();
}
Expand All @@ -80,17 +147,22 @@ static bool IsCatchException(CodeInstruction code)
return code.blocks.Any(block => block.blockType == ExceptionBlockType.BeginCatchBlock && block.catchType == typeof(Exception));
}

static bool HasCreateInstance(CodeInstruction code)
{
return code.operand is MethodInfo methodInfo && createInstanceMethods.ContainsKey(methodInfo);
}

static bool HasCatch(MethodBase method)
{
try
{
var result = PatchProcessor.GetOriginalInstructions(method).Any(IsCatchException);
return result;
if (PatchProcessor.GetOriginalInstructions(method).Any(code => IsCatchException(code) || HasCreateInstance(code)))
return true;
}
catch
{
return false;
}
return false;
}
}

Expand Down
11 changes: 6 additions & 5 deletions Source/VisualExceptions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
<TargetFramework>net472</TargetFramework>
<LangVersion>8.0</LangVersion>
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\Current\Assemblies\</OutputPath>
<OutputPath>..\1.3\Assemblies\</OutputPath>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Version>1.0.4.0</Version>
<Version>1.1.0.0</Version>
<Copyright>Copyright Andreas Pardeike</Copyright>
</PropertyGroup>

Expand All @@ -30,9 +30,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.3.3072" GeneratePathProperty="true" />
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.3.3117" GeneratePathProperty="true" />
<PackageReference Include="Lib.Harmony" Version="2.1.1">
<ExcludeAssets></ExcludeAssets>
<ExcludeAssets>runtime</ExcludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.Platforms" Version="5.0.2" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.2">
Expand Down Expand Up @@ -84,7 +85,7 @@
ModBuilder XMLPut -file "$(MSBuildProjectDirectory)\..\About\Manifest.xml" -xpath /Manifest/version -value "{{$(MSBuildProjectName)-version}}"
)
if defined INSTALL_MOD (
"%INSTALL_MOD%" "$(Configuration)" "$(MSBuildProjectDirectory)\..\" "$(MSBuildProjectName)" "About Current Sounds Textures" "LoadFolders.xml"
"%INSTALL_MOD%" "$(Configuration)" "$(MSBuildProjectDirectory)\..\" "$(MSBuildProjectName)" "About 1.2 1.3 Sounds Textures" "LoadFolders.xml"
)
</PostBuildEvent>
<Company>Brrainz</Company>
Expand Down

0 comments on commit f3a3c92

Please sign in to comment.