-
Notifications
You must be signed in to change notification settings - Fork 324
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
Unity il2cpp support #249
Unity il2cpp support #249
Conversation
@dylanh724 and @joshpeterson: This seems to be on the path to working. All the Discord-related stuff works just fine in both Mono and il2cpp. Still working on the UnityEvent stuff. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm!
The error message here from IL2CPP is not too helpful. It would be nice if it happened to mention the name of the method that is causing the problem. I'm going to add this to the error message for a future Unity release. @dylanh724: It is possible for you to patch the source code in your local Unity installation to add this information to the error message. Here is how to do it: Look for the Data\il2cpp\libil2cpp\vm\PlatformInvoke.cpp file in your editor installation. In the function named // Okay, we cannot marshal it for some reason. Figure out why.
if (Method::IsInstance(d->method))
vm::Exception::Raise(vm::Exception::GetNotSupportedException("IL2CPP does not support marshaling delegates that point to instance methods to native code.")); You can replace that code with this code: std::string methodName = il2cpp::vm::Method::GetFullName(d->method);
// Okay, we cannot marshal it for some reason. Figure out why.
if (Method::IsInstance(d->method))
{
std::string errorMessage = "IL2CPP does not support marshaling delegates that point to instance methods to native code. The method we're attempting to marshal is: " + methodName;
vm::Exception::Raise(vm::Exception::GetNotSupportedException(errorMessage.c_str()));
} Then build the player for IL2CPP in the editor again. When the error happens, you should see the full name of the method that is supposed to be marshaled, but is not able to be marshaled correctly. Hopefully that will help track down the remaining issue. |
Heads up - We just had a patch in our game and may need a couple days to get the time to test this again~ |
@joshpeterson Anything special I need to do to get this code replacement working? I'm still seeing the old error. Is there a 2nd place where this can be with the same error? EDIT: OH. The backup file wasn't renamed to .bak, so maybe c++ files pick up classes no matter what the file name? I'm unfamiliar. Renamed backup to .bak and trying again. Also restarting Unity. EDIT: Ok, I haven't edited my Unity non-project files for a long time. I was accessing my old 5.6 one ;p I forgot about the Hub's new hierarchy and forgot to remove 5.6. Oops! Trying again. |
@dylanh724 No, I think this is the only place this error message occurs. Regarding .bak files, Unity will only compile and use .cpp files, so that should not have an impact. Is it possible for you to send me the project or submit it via a bug report to us at Unity? I can have a look at it and try to determine what is wrong. |
@joshpeterson the change reflects now -- however, I am getting this error that seems to trace back straight to that file changed: https://pastebin.com/gaH0Ue3p
For clarity, here is what was changed including a bit of the block around it: Il2CppMethodPointer reversePInvokeWrapper = MetadataCache::GetReversePInvokeWrapperFromIndex(d->method->methodDefinition->reversePInvokeWrapperIndex);
if (reversePInvokeWrapper == NULL)
{
// Okay, we cannot marshal it for some reason. Figure out why.
if (Method::IsInstance(d->method))
{
std::string errorMessage = "IL2CPP does not support marshaling delegates that point to instance methods to native code. The method we're attempting to marshal is: " + methodName;
vm::Exception::Raise(vm::Exception::GetNotSupportedException(errorMessage.c_str()));
}
vm::Exception::Raise(vm::Exception::GetNotSupportedException("To marshal a managed method, please add an attribute named 'MonoPInvokeCallback' to the method definition."));
} Is it supposed to be |
@dylanh724 I think you need one more line: std::string methodName = il2cpp::vm::Method::GetFullName(d->method); This should be just above this line: std::string errorMessage = "IL2CPP does not support marshaling delegates that point to instance methods to native code. The method we're attempting to marshal is: " + methodName; |
@joshpeterson Oh man, missed that copy+paste. Got it: NotSupportedException: IL2CPP does not support marshaling delegates that point to instance methods to native code. The method we're attempting to marshal is: DiscordMgr::OnReady
at DiscordController.OnEnable () [0x00000] in <00000000000000000000000000000000>:0
|
Cool! So that indicates the |
Hm, that's odd. To compare two calls: // Not working?
[MonoPInvokeCallback(typeof(OnReadyInfo))]
public static void ReadyCallback(ref DiscordUser connectedUser) { }
public delegate void OnReadyInfo(ref DiscordUser connectedUser);
// Working
[MonoPInvokeCallback(typeof(OnErrorInfo))]
public static void ErrorCallback(int errorCode, string message) { }
public delegate void OnErrorInfo(int errorCode, string message);
// Handlers
public struct EventHandlers
{
public OnReadyInfo readyCallback;
public OnDisconnectedInfo disconnectedCallback;
public OnErrorInfo errorCallback;
public OnJoinInfo joinCallback;
public OnSpectateInfo spectateCallback;
public OnRequestInfo requestCallback;
} It all seems to look legit? I did just realize that the two ready functions had different names for their variable, which I just fixed, but I feel like that shouldn't cause this error? |
That does all look correct. However, the problem might be elsewhere. Is there a class named |
@joshpeterson The main reason why this is awkward is because they have a // DiscordController.cs
handlers = new DiscordRpc.EventHandlers();
handlers.readyCallback += s_discordMgr.OnReady;
[...] This is what I tried before -- if I could just skip the handlers, I could probably figure this out. So we now have this:// discordRpc.cs
[MonoPInvokeCallback(typeof(OnReadyInfo))]
public static void ReadyCallback(ref DiscordUser user) { }
public delegate void OnReadyInfo(ref DiscordUser connectedUser);
[ ... ]
public struct EventHandlers
{
public OnReadyInfo readyCallback; // THESE should be static?
[ ... ]
}
[DllImport("discord-rpc", EntryPoint = "Discord_Initialize", CallingConvention = CallingConvention.Cdecl)]
public static extern void Initialize(string applicationId, ref EventHandlers handlers, bool autoRegister, string optionalSteamId);
[ ... ] // discordController.cs
void OnEnable()
{
handlers = new DiscordRpc.EventHandlers();
handlers.readyCallback += s_discordMgr.OnReady;
[ ... ]
}
public void OnReady(ref DiscordRpc.DiscordUser connectedDiscordUser)
{
// The goal is to get here.
} |
@dylanh724 I don't think the |
DiscordMgr is my own script (it was getting a bit confusing with the statics, leaving DiscordMgr my final callback). I called that through the static callback via a singleton. Only the first callback needs to be static, right? Here are the scripts I use, watered down a bit as best as my 3am mind can do @joshpeterson I'll check here again in the morning (GMT+8) |
@dylanh724 This is rather confusing. It looks like the code you have should work. I don't see |
@joshpeterson GOT IT WORKING! I had a bad build, so tested an old build by accident. I really shouldn't be doing this at 3am ;D But yeah, works (the exact code I sent you)! So this PR needs their callbacks to be static with that [] mono attribute. @msciotti Check out the zip file above for a working build example. My code has a
About that BountyHow to handle that one? Josh and msci both helped -- seems like a multi-effort thing. I toss $25 to each of you? To claim your bounty piece, DM me on Discord ( https://discord.gg/tol i42-Xblade ) with a PayPal email and I'll toss it over. Good job, folks. |
@dylanh724 That is great to hear! I'm happy you have this working now.
I'd rather not accept it - I'm just doing my job at Unity. :) If you still want to pay it though, I'd suggest a charity, maybe Girls Who Code? |
Seconded. If you really feel like it, Girls Who Code is fantastic. Thank you both for your help! |
@dylanh724 it seems like you ran into a similar issue that I did, which was errors when trying to assign the callbacks to methods in a singleton I think we may want to get somewhere in the middle here. The existing code does work as is, and i don't want to implement a specific pattern in the example for folks who may want an example a little more basic. Is there a way that we can structure things where the existing code works, and it's also easily extended to a use case like yours? |
The alternative would be in Or use an anonymous callback // ############################################################
// DiscordController
// ############################################################
using UnityEngine;
using AOT;
#region "Core Vars"
public string applicationId;
public string optionalSteamId;
public static DiscordController s_discordCtrl;
private DiscordRpc.EventHandlers handlers;
public DiscordRpc.RichPresence presence = new DiscordRpc.RichPresence();
public DiscordRpc.DiscordUser joinRequest;
#endregion
#region "Demo Vars"
public int clickCounter;
public DiscordJoinEvent onJoin;
public DiscordJoinEvent onSpectate;
public DiscordJoinRequestEvent onJoinRequest;
public UnityEngine.Events.UnityEvent onConnect;
public UnityEngine.Events.UnityEvent onDisconnect;
public UnityEngine.Events.UnityEvent hasResponded;
// <And those serialization fields - seems like I deleted them>
#endregion
#region "Core Static IL2CPP Callbacks"
[MonoPInvokeCallback(typeof(DiscordRpc.OnReadyInfo))]
public static void OnReadyStatic(ref DiscordRpc.DiscordUser connectedDiscordUser)
{ s_discordCtrl.OnReady(ref connectedDiscordUser); }
[MonoPInvokeCallback(typeof(DiscordRpc.OnDisconnectedInfo))]
public static void OnDisconnectedStatic(int errorCode, string message)
{ DiscordController.s_discordCtrl.OnDisconnected(errorCode, message); }
[MonoPInvokeCallback(typeof(DiscordRpc.OnErrorInfo))]
public static void OnErrStatic(int errorCode, string message)
{ DiscordController.s_discordCtrl.OnErr(errorCode, message); }
[MonoPInvokeCallback(typeof(DiscordRpc.OnJoinInfo))]
public static void OnOtherJoinByPublicInvStatic(string secret)
{ DiscordController.s_discordCtrl.OnOtherJoinByPublicInv(secret); }
[MonoPInvokeCallback(typeof(DiscordRpc.OnRequestInfo))]
public static void OnMyReqToJoinOtherResStatic(ref DiscordRpc.DiscordUser request)
{ DiscordController.s_discordCtrl.OnMyReqToJoinOtherRes(ref request); }
[MonoPInvokeCallback(typeof(DiscordRpc.OnSpectateInfo))]
public static void OnOtherSpectateStatic(secret)
{ DiscordController.s_discordCtrl.OnOtherSpectate(secret); }
#endregion
#region "Demo Callbacks"
private void OnReady(ref connectedDiscordUser)
{
Debug.Log("[DiscordCtrl] @ OnReady"];
}
// <...and the other callbacks that are normally here - just changed callback to private>
#endregion
#region "Init/End"
private void Awake()
{
Debug.Log("[DiscordController] @ Awake: Singleton+Persist");
// Singleton + Persist
if (s_discordCtrl == null)
{
s_discordCtrl = this;
DontDestroyOnLoad(this.gameObject);
}
else if (this != s_discordCtrl)
Destroy(gameObject);
}
private void OnEnable()
{
// <The usual stuff>
}
private void OnDisable()
{
// <The usual shutdown stuff>
}
#endregion ^ This is cherry picked from both of my scripts and added back some of the demo items, but I may be missing some or syntax may be slightly off: Needs some verification. Something like this should work, though. May also want to confirm naming conventions, this was quick n dirty. Apologies for not including everything, may seem sorta lazy - but alas... lacking some time today. Hopefully this helps. It may be more accurate to use your own code and just move things around using the patterns above:
|
When will we get new release with this included? |
I wanted to try and roll a release together with this + choosing which pipe number to open to support choosing the Discord client to connect to. However, the Windows side of things hung me up on that other PR. Let me take some of Dylan's suggestions above and work on the example, and I can merge + release this this week. |
- callbackCalls didnt seem to do anything
Alright, so I've been trying to poke at this a lot. I've gotten to more or less the same place that I was at before, which I'm not sure is the fault of the DiscordRpc stuff but perhaps just my unfamiliarity with doing this stuff in Unity the right way. I was going about refactoring some stuff into a public class DiscordManager
{
private static DiscordManager _instance;
public static DiscordManager Instance
{
get
{
if (_instance == null)
{
_instance = new DiscordManager();
}
return _instance;
}
}
public void Ready(ref DiscordRpc.DiscordUser connectedUser)
{
Debug.Log(string.Format("Discord: connected to {0}#{1}: {2}", connectedUser.username, connectedUser.discriminator, connectedUser.userId));
// onConnect.Invoke();
}
} I can assign callbacks without issue: handlers = new DiscordRpc.EventHandlers();
handlers.readyCallback += DiscordManager.Instance.Ready; All this code runs perfectly fine, and callbacks fire. No need to change where the For the time being, I'm planning to merge this as-is. It will support compiling with il2cpp, and the example runs as expected. I do not know why the |
@msciotti Try changing this: if (_instance == null)
_instance = new DiscordManager(); to this: if (_instance == null)
_instance = this; I doubt it contributes to the freeze, but usually you want the instance to be this current instance, unless you're doing something beyond me or any previous vars set will be unset. With this, you have 2 instances running: The local script that sets this, and the new instance. This means that the handlers may be set in the current instance, but the singleton (3rd party) instance may not have it set. ....but if it's set at start/awake/onenable, then it'll set itself. Which means you'd have two callbacks. Maybe that's what's causing the freeze? Strange callback happenings with 2 instances? No matter what code you put [that's not a stack overflow], you probably shouldn't get freezes... did you try stepping through with VS in the Unity editor? While you're there, Debug.Log(string.Format(" can be simplified to Debug.LogFormat(" |
Unity freezes when you have infinite loop |
@msciotti Sorry, I don't have any ideas about why this causes a freeze. |
@dylanh724 I'll give that a go, see if it makes a difference. I'll try this out tomorrow and see where I get. If I can't make much more forward progress on that, I'll merge and roll a new release for the sake of at least actually supporting the il2cpp compiling stuff 👍 |
Merging this to at least have a supporting release. I will continue to tinker with examples when I have time. |
* Initial il2cpp support attempts * Fix crashes * Different variable name * Fix indenting * Change back unneeded stuff - callbackCalls didnt seem to do anything (cherry picked from commit e6390c8)
As an update, none of my callbacks are firing in Unity. Was something changed in the backend so it won't work when testing in the editor? EDIT: Seems likely unrelated, moved this here: #261 |
Trying to support #243