RevenantX / LiteEntitySystem

Pure C# HighLevel API for multiplayer games
Apache License 2.0
190 stars 22 forks source link

RefMagic on Android + .NET #7

Closed Logerfo closed 11 months ago

Logerfo commented 11 months ago

Hello! I'm having mostly a good time integrating with your library, thank you for making it available! But now I'm having problems with it :(

I have a game client that runs fine on Windows. Now I'm trying to run on Android and I'm having trouble with RefMagic. Do you have it documented anywhere, by the way? First of all, Utils.IsMono doesn't seem to be enough for the conditional RefFieldValue call. Check if what I'm doing makes sense:

public static readonly bool IsMono = Type.GetType("Mono.Runtime") != null
                                  || RuntimeInformation.OSDescription.Contains("android");

After that, I'm getting the following error:

[libc] Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x40 in tid 23914 (.Client.Android), pid 23914 (.Client.Android)

Does this look like anything to you? I debugged it and it's not the first call that fails, but one of the first ten. I tried to move only this specific crashing call from RefFieldValueMono to RefFieldValueDotNet (probably insane) and got "arith type mismatch" (as expected). I also tried adding IntPtr.Size to the offset for this specific crashing call only, which passes the breaking point, but the returned reference is null, so it crashes somewhere else at call-site.

By the way, I'm running the last version of MonoGame on .NET 7 and x64 everywhere, but I've also tried every architecture I could and the repro seems the same. I'm using an Android emulator. Thanks!

RevenantX commented 11 months ago

@Logerfo didn't checked Android+.NET 7. Unity(Mono)+Android works fine. Seems that offsets can be another on Android.

Do you have it documented anywhere, by the way?

It just adds two values, here's source: https://github.com/RevenantX/LiteEntitySystem/blob/master/LiteEntitySystem/ILPart/RefMagic.il

Logerfo commented 11 months ago

Seems that offsets can be another on Android

@RevenantX what can I do to discover the offset? Thank you for your reply!

RevenantX commented 11 months ago

@Logerfo you can write simple app using RefMagic and try to scan memory (something like here https://github.com/RevenantX/LiteEntitySystem/blob/master/LiteEntitySystem/Internal/EntityClassData.cs#L74) Set some value to 0xDEADBEEF and try scan memory in code or IDE.

Logerfo commented 11 months ago

@RevenantX if the offset is different on Android with .NET 7, isn't the check Utils.RefFieldValue<uint>(to, monoFieldOffset) == to.TestValue supposed to fail? Because it is not failing. Any chance the offset might not always be the same?

Logerfo commented 11 months ago

Can you explain the reason for the mask & 0xFFFFFF? I'm trying to understand the concept behind this code and hopefully fix the problem sending a PR.

RevenantX commented 11 months ago

@RevenantX if the offset is different on Android with .NET 7, isn't the check Utils.RefFieldValue<uint>(to, monoFieldOffset) == to.TestValue supposed to fail? Because it is not failing. Any chance the offset might not always be the same?

So .NET7 on android has same offset like mono?

Any chance the offset might not always be the same?

I don't think this can be. Maybe there is some issues with some memory aligning.

Maybe try with some minimal project to test.

RevenantX commented 11 months ago

Can you explain the reason for the mask & 0xFFFFFF? I'm trying to understand the concept behind this code and hopefully fix the problem sending a PR.

Here's some additional info https://stackoverflow.com/questions/30817924/obtain-non-explicit-field-offset.

Maybe for newer .NET 7 this can be obtained using https://github.com/IllidanS4/SharpUtils/blob/master/Reflection/ReflectionTools.cs#L311

RevenantX commented 11 months ago

@Logerfo

I debugged it and it's not the first call that fails, but one of the first ten.

Also if some of them works maybe there is some pattern or error in another place.

Logerfo commented 11 months ago

SharpUtils is simultaneously interesting and nasty. After copying 35 files from them, I managed to build the GetOffset method, but I had to remove some pieces of code, because I'm targeting .NET 7 and the original source targets .NET Framework v4.6, which might or might not be the reason why my results were awful: offset 0 running the desktop client and runtime exception running the android client.

Logerfo commented 11 months ago

Also if some of them works maybe there is some pattern or error in another place.

I'm not sure if they work properly, all I can say for now it's that they don't crash, but I might dig deeper into it to see if I can give you a better answer.

Logerfo commented 11 months ago

I think I've found something! I changed the EntityClassData constructor a bit to have a step-by-step comprehension like that: image

And it seems like a problem is happening with the first field (which is a SyncString) of the first entity to hit the yellow-highlighted line. Here's the results running the desktop client: image

And running the Android client: image

Does this look like anything to you?

RevenantX commented 11 months ago

Hello! I'm having mostly a good time integrating with your library, thank you for making it available! But now I'm having problems with it :(

I have a game client that runs fine on Windows. Now I'm trying to run on Android and I'm having trouble with RefMagic. Do you have it documented anywhere, by the way? First of all, Utils.IsMono doesn't seem to be enough for the conditional RefFieldValue call. Check if what I'm doing makes sense:

public static readonly bool IsMono = Type.GetType("Mono.Runtime") != null
                                  || RuntimeInformation.OSDescription.Contains("android");

After that, I'm getting the following error:

[libc] Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x40 in tid 23914 (.Client.Android), pid 23914 (.Client.Android)

Does this look like anything to you? I debugged it and it's not the first call that fails, but one of the first ten. I tried to move only this specific crashing call from RefFieldValueMono to RefFieldValueDotNet (probably insane) and got "arith type mismatch" (as expected). I also tried adding IntPtr.Size to the offset for this specific crashing call only, which passes the breaking point, but the returned reference is null, so it crashes somewhere else at call-site.

By the way, I'm running the last version of MonoGame on .NET 7 and x64 everywhere, but I've also tried every architecture I could and the repro seems the same. I'm using an Android emulator. Thanks!

I've currently checked .Net + Monogame + Android and it works fine with your fix (RuntimeInformation.OSDescription.Contains("android");)

Maybe you can send to me project with problem?

Logerfo commented 11 months ago

I don't currently have a minimal repro. My game is being developed closed-source to avoid easing for cheaters, but I can give you access to the project if you're ok with the burden of the information overload that the non-minimal implementation can cause. I also changed a lot of the internal LiteEntitySystem code style to match the project's, so it can feel a little odd for you. What do you think?

RevenantX commented 11 months ago

@Logerfo well if you made changes, this can be anything. Including bugs of Monogame. Because simple monogame project with connection and players classes works fine with Mono check

Logerfo commented 11 months ago

I'll try to build a minimal repro using LiteEntitySystem as a git module.

Logerfo commented 11 months ago

I managed to reproduce the issue in a minimal repo. Here it is: https://github.com/Logerfo/liteentitysystem-android-bug-minimal-repro

RevenantX commented 11 months ago

@Logerfo should be fixed!

Logerfo commented 11 months ago

Confirmed! Thank you very much!!