-
-
Notifications
You must be signed in to change notification settings - Fork 492
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
System.InvalidProgramException: Invalid IL code #38
Comments
Please add information about the method being patched as well as the used patch methods. Mainly the signatures. |
namespace TollRoad
{
public class Node
{
public static void Hook()
{
try
{
// NetNode override
//public void CalculateNode( ushort nodeID )
var harmony = HarmonyInstance.Create( "com.leigon.cities_skylines.toll_road" );
HarmonyInstance.DEBUG = true;
MethodInfo targetmethod = AccessTools.Method( typeof( NetNode ), "CalculateNode" );
HarmonyMethod prefixmethod = new HarmonyMethod( typeof( TollRoad.Node ).GetMethod( "CalculateNode_Prefix" ) );
harmony.Patch( targetmethod, prefixmethod, null );
}
catch( System.Exception ex )
{
Logger.LogError( "Node", ex.ToString() );
}
}
public static void CalculateNode_Prefix( NetNode __instance, ushort nodeID )
{
NetNode thisNode = NetManager.instance.m_nodes.m_buffer[ nodeID ];
Logger.Log( "NetNode", "CalculateNode" );
NetManager.instance.m_nodes.m_buffer[ nodeID ] = thisNode;
}
}
} Reflection of prefixed mathod: public void CalculateNode(ushort nodeID)
{
if (this.m_flags == NetNode.Flags.None)
return;
NetManager instance = Singleton<NetManager>.instance;
Vector3 vector3_1 = Vector3.zero;
int num1 = 0;
int num2 = 0;
bool flag1 = false;
bool flag2 = false;
bool flag3 = false;
bool flag4 = false;
bool flag5 = false;
bool flag6 = false;
bool flag7 = false;
bool flag8 = false;
bool flag9 = false;
bool flag10 = true;
bool flag11 = true;
bool flag12 = Singleton<TerrainManager>.instance.HasDetailMapping(this.m_position);
NetInfo with1 = (NetInfo) null;
int num3 = 0;
int num4 = 0;
NetInfo with2 = (NetInfo) null;
float num5 = -1E+07f;
for (int index = 0; index < 8; ++index)
{
ushort segment = this.GetSegment(index);
if ((int) segment != 0)
{
NetInfo info = instance.m_segments.m_buffer[(int) segment].Info;
float nodeInfoPriority = info.m_netAI.GetNodeInfoPriority(segment, ref instance.m_segments.m_buffer[(int) segment]);
if ((double) nodeInfoPriority > (double) num5)
{
with2 = info;
num5 = nodeInfoPriority;
}
}
}
if (with2 == null)
with2 = this.Info;
if (with2 != this.Info)
{
this.Info = with2;
Singleton<NetManager>.instance.UpdateNodeColors(nodeID);
if (!with2.m_canDisable)
this.m_flags &= NetNode.Flags.OneWayOutTrafficLights | NetNode.Flags.UndergroundTransition | NetNode.Flags.Created | NetNode.Flags.Deleted | NetNode.Flags.Original | NetNode.Flags.End | NetNode.Flags.Middle | NetNode.Flags.Bend | NetNode.Flags.Junction | NetNode.Flags.Moveable | NetNode.Flags.Untouchable | NetNode.Flags.Outside | NetNode.Flags.Temporary | NetNode.Flags.Double | NetNode.Flags.Fixed | NetNode.Flags.OnGround | NetNode.Flags.Ambiguous | NetNode.Flags.Water | NetNode.Flags.Sewage | NetNode.Flags.ForbidLaneConnection | NetNode.Flags.LevelCrossing | NetNode.Flags.OneWayIn | NetNode.Flags.Heating | NetNode.Flags.Electricity | NetNode.Flags.Collapsed | NetNode.Flags.DisableOnlyMiddle | NetNode.Flags.AsymForward | NetNode.Flags.AsymBackward | NetNode.Flags.CustomTrafficLights;
}
bool flag13 = false;
for (int index1 = 0; index1 < 8; ++index1)
{
ushort segment1 = this.GetSegment(index1);
if ((int) segment1 != 0)
{
++num1;
ushort startNode = instance.m_segments.m_buffer[(int) segment1].m_startNode;
ushort endNode = instance.m_segments.m_buffer[(int) segment1].m_endNode;
Vector3 startDirection = instance.m_segments.m_buffer[(int) segment1].m_startDirection;
Vector3 endDirection = instance.m_segments.m_buffer[(int) segment1].m_endDirection;
bool flag14 = (int) nodeID == (int) startNode;
Vector3 vector3_2 = !flag14 ? endDirection : startDirection;
NetInfo info1 = instance.m_segments.m_buffer[(int) segment1].Info;
ItemClass connectionClass = info1.GetConnectionClass();
if (!info1.m_netAI.CanModify())
flag11 = false;
int vehicleLaneCount1;
int vehicleLaneCount2;
if (flag14 == ((instance.m_segments.m_buffer[(int) segment1].m_flags & NetSegment.Flags.Invert) != NetSegment.Flags.None))
{
vehicleLaneCount1 = info1.m_backwardVehicleLaneCount;
vehicleLaneCount2 = info1.m_forwardVehicleLaneCount;
}
else
{
vehicleLaneCount1 = info1.m_forwardVehicleLaneCount;
vehicleLaneCount2 = info1.m_backwardVehicleLaneCount;
}
for (int index2 = index1 + 1; index2 < 8; ++index2)
{
ushort segment2 = this.GetSegment(index2);
if ((int) segment2 != 0)
{
NetInfo info2 = instance.m_segments.m_buffer[(int) segment2].Info;
if (info2.GetConnectionClass().m_service == connectionClass.m_service || (info2.m_nodeConnectGroups & info1.m_connectGroup) != NetInfo.ConnectGroup.None || (info1.m_nodeConnectGroups & info2.m_connectGroup) != NetInfo.ConnectGroup.None)
{
Vector3 vector3_3 = (int) nodeID != (int) instance.m_segments.m_buffer[(int) segment2].m_startNode ? instance.m_segments.m_buffer[(int) segment2].m_endDirection : instance.m_segments.m_buffer[(int) segment2].m_startDirection;
if ((double) vector3_2.x * (double) vector3_3.x + (double) vector3_2.z * (double) vector3_3.z < (double) (0.01f - Mathf.Min(info1.m_maxTurnAngleCos, info2.m_maxTurnAngleCos)))
{
if (info1.m_requireDirectRenderers && (info1.m_nodeConnectGroups == NetInfo.ConnectGroup.None || (info1.m_nodeConnectGroups & info2.m_connectGroup) != NetInfo.ConnectGroup.None) || info2.m_requireDirectRenderers && (info2.m_nodeConnectGroups == NetInfo.ConnectGroup.None || (info2.m_nodeConnectGroups & info1.m_connectGroup) != NetInfo.ConnectGroup.None))
++num2;
}
else
flag6 = true;
}
else
flag6 = true;
}
}
if ((int) instance.m_nodes.m_buffer[(int) startNode].m_elevation != (int) instance.m_nodes.m_buffer[(int) endNode].m_elevation)
flag10 = false;
Vector3 position1 = instance.m_nodes.m_buffer[(int) startNode].m_position;
Vector3 position2 = instance.m_nodes.m_buffer[(int) endNode].m_position;
flag12 = !flag14 ? flag12 && Singleton<TerrainManager>.instance.HasDetailMapping(position1) : flag12 && Singleton<TerrainManager>.instance.HasDetailMapping(position2);
if (NetSegment.IsStraight(position1, startDirection, position2, endDirection))
flag8 = true;
else
flag7 = true;
if (num1 == 1)
{
flag13 = flag14;
vector3_1 = vector3_2;
flag1 = true;
}
else if (num1 == 2 && info1.IsCombatible(with1) && (info1.IsCombatible(with2) && vehicleLaneCount1 != 0 == (num4 != 0)) && vehicleLaneCount2 != 0 == (num3 != 0))
{
float num6 = (float) ((double) vector3_1.x * (double) vector3_2.x + (double) vector3_1.z * (double) vector3_2.z);
if (vehicleLaneCount1 != num4 || vehicleLaneCount2 != num3)
{
if (vehicleLaneCount1 > vehicleLaneCount2)
{
flag4 = true;
flag3 = true;
}
else
{
flag5 = true;
flag3 = true;
}
}
else if ((double) num6 < -0.999000012874603)
flag2 = true;
else
flag3 = true;
flag9 = flag14 != flag13;
}
else
flag6 = true;
with1 = info1;
num3 = vehicleLaneCount1;
num4 = vehicleLaneCount2;
}
}
if (!with2.m_enableMiddleNodes & flag2)
flag3 = true;
if (!with2.m_enableBendingNodes & flag3)
flag6 = true;
if (with2.m_requireContinuous && (this.m_flags & NetNode.Flags.Untouchable) != NetNode.Flags.None)
flag6 = true;
if (with2.m_requireContinuous && !flag9 && (flag2 || flag3))
flag6 = true;
NetNode.Flags flags = this.m_flags & (NetNode.Flags.OneWayOutTrafficLights | NetNode.Flags.UndergroundTransition | NetNode.Flags.Created | NetNode.Flags.Deleted | NetNode.Flags.Original | NetNode.Flags.Disabled | NetNode.Flags.Untouchable | NetNode.Flags.Outside | NetNode.Flags.Temporary | NetNode.Flags.Double | NetNode.Flags.Fixed | NetNode.Flags.OnGround | NetNode.Flags.Ambiguous | NetNode.Flags.Water | NetNode.Flags.Sewage | NetNode.Flags.ForbidLaneConnection | NetNode.Flags.LevelCrossing | NetNode.Flags.OneWayIn | NetNode.Flags.Heating | NetNode.Flags.Electricity | NetNode.Flags.Collapsed | NetNode.Flags.DisableOnlyMiddle | NetNode.Flags.CustomTrafficLights);
if ((flags & NetNode.Flags.Outside) != NetNode.Flags.None)
this.m_flags = flags;
else if (flag6)
this.m_flags = flags | NetNode.Flags.Junction;
else if (flag3)
{
if (flag4)
flags |= NetNode.Flags.AsymForward;
if (flag5)
flags |= NetNode.Flags.AsymBackward;
this.m_flags = flags | NetNode.Flags.Bend;
}
else if (flag2)
{
if ((!flag7 || !flag8) && ((this.m_flags & (NetNode.Flags.Untouchable | NetNode.Flags.Double)) == NetNode.Flags.None && flag10) && flag11)
flags |= NetNode.Flags.Moveable;
this.m_flags = flags | NetNode.Flags.Middle;
}
else if (flag1)
{
if ((this.m_flags & NetNode.Flags.Untouchable) == NetNode.Flags.None && flag10 && (flag11 && with2.m_enableMiddleNodes))
flags |= NetNode.Flags.Moveable;
this.m_flags = flags | NetNode.Flags.End;
}
this.m_heightOffset = flag12 || !with2.m_requireSurfaceMaps ? (byte) 0 : (byte) 64;
this.m_connectCount = (byte) num2;
BuildingInfo building;
float heightOffset;
with2.m_netAI.GetNodeBuilding(nodeID, ref this, out building, out heightOffset);
this.UpdateBuilding(nodeID, building, heightOffset);
} |
Can you provide the IL code for the original method? Not necessarily the whole but mainly like +/- 10 lines around the calling for GetNodeBuilding to compare the IL code that Harmony spits out with the original IL code. Almost all problem can be examined by comparing the original IL code with the final Harmony result that is in the debug log. Because currently, Harmony does not support TRY/CATCH on the IL level. And even if the decompiled code does not have a try/catch statement, the IL code can actually have it and right now, Harmony cannot handle the meta data of try/catch ranges and thus the corresponding IL codes will not work as expected. As a side result, a finally handler is not executed. |
Sure.
|
Hi @pardeike Any luck with this? |
I only had a quick look but could not spot the problem right away. The thing is that this error is at a place where Harmony does not interface with the IL code much. It just copies it from the original. The only thing that might be unusual here is that it seems to be a I have to look more into this. Meanwhile, would you be able to create a minimal test case to reproduce this? The method here is a bit too big to handle. I am actively working on the next release that fixes a few things so the timing is right. Thanks |
Can you please verify if this error persist with the latest master? |
Hi, sorry for not helping out with this, I have had a bit of real life issues. With the latest master I get: System.InvalidProgramException: Invalid IL code in (wrapper dynamic-method) NetNode:CalculateNode_Patch1 (object,uint16): IL_0007: call 0x00000001 at (wrapper managed-to-native) System.RuntimeMethodHandle:GetFunctionPointer (intptr) I assume my code is out of date?
|
I have no idea why the IL code at 0007 is illegal. It is the call to your prefix method. That method is static and takes two parameters: NetNode __instance and ushort nodeID and both are loaded onto the stack with the IL commands before: L_0000: ldarg.0 and L_0001: ldarg 1. That makes sense if the original method is an instance method of NetNode. Because the 0th argument is the instance (NetNode) and the 1st argument will be the argument to the method, in this case the nodeID. Could it be that the nodeID of the original is not the same type as in your patch? I can see of the debug log that Harmony says uint16 but you are using a ushort. |
On a second thought, I think it has to do with the type of NetNode. Can you post the definition? |
|
Still cannot reproduce. This compiles and runs just fine: using Harmony;
using System;
namespace HarmonyConsoleApp
{
class Program
{
static void Main(string[] args)
{
var harmony = HarmonyInstance.Create("test");
harmony.PatchAll(typeof(Program).Assembly);
var node = new NetNode();
node.CalculateNode(123);
Console.ReadKey();
}
}
public struct NetNode
{
public void CalculateNode(ushort nodeID)
{
Console.WriteLine("CalculateNode " + nodeID);
}
}
[HarmonyPatch(typeof(NetNode))]
[HarmonyPatch("CalculateNode")]
static class Patch
{
[HarmonyPrefix]
public static void CalculateNode_Prefix(NetNode __instance, ushort nodeID)
{
Console.WriteLine("Patch with " + __instance + " #" + nodeID);
}
}
} |
Which .Net version are you using and also: are you testing in Debug or in Release compile mode? Are you sure you have the lates source code when you compile Harmony? |
.Net 4, Debug. I got latest through github desktop on a new clone today and installed c#7 via Install-Package Microsoft.Net.Compilers |
What happens if you try to run my example? |
Your sample works. |
Then the question you might be able to answer better than me is: what is the difference between my example and the concrete use case you have. |
Humm, I just created a new console app and using the skylines dll (and your sample without the struct) it works fine. There is either something to do with my project settings or how skylines calls the mod dll I guess? |
I have tried several iterations and they all work in the console app and not from the game... Eventually I tried something simple and this didn't work in game but did in the app:
` Same error. In morning I will create a new project and see if I can get that working, I can only assume its something I am doing (using a ThreadingExtension and hooking on OnCreated). Thanks for the help, will get back to you tomorrow. |
I started from scratch with a .net 4.5.2 debug dll mod simplest as possible with debug output: namespace TollRoad
}
Exiting to main menu:
Do you have Cities Skylines? |
Unfortunately, I don't have Cities Skylines. public static void CalculateNode_Prefix( ushort nodeID );
// or
public static void CalculateNode_Prefix( ); I just cannot see why it fails ont the CALL at loc 0007. Maybe I have to add Boxing but I never had to do this so far. |
Looks like its because NetNode2 is a struct. It does not work in game but does in the console app. If you need a copy of the game to test with I can get you one if that helps?
NetNode2 as a class
NetNode2 as a struct
|
I experience the same problem. Cities: Skylines utilizes mono to decompile and recompile mod DLLs on-the-fly to ensure compatibility with different operating systems. That could make the difference here.
|
This could be a variation on #77 |
I just fixed #77 - please verify if this is still an issue. I will close this for now. |
Hi
I am trying to mod Cities Skylines and came across your lib last week replacing Detours.
Just testing I am prefixing a simple function however a System.InvalidProgramException exeption is thrown.
I assume this is something similar to a previous issue... #3 as L_0750 is a function call with refs?
L_0750: callvirt Void GetNodeBuilding(UInt16, NetNode ByRef, BuildingInfo ByRef, Single ByRef)
Thanks in advance,
-Adrian
DONE
The text was updated successfully, but these errors were encountered: