colyseus / colyseus-unity-sdk

⚔ Colyseus Multiplayer SDK for Unity
https://docs.colyseus.io/getting-started/unity-sdk/
MIT License
371 stars 100 forks source link

JsonSerializationException: Unable to deserialize instance of 'BgDataMessage' for iOS on device.. #204

Closed VkDevPh closed 1 year ago

VkDevPh commented 1 year ago

When exporting my Unity project for iOS, the following exception is thrown during runtime:

JsonSerializationException: Unable to deserialize instance of 'BgDataMessage' because there is no parameterless constructor is defined on type.

It's working fine while playing on Unity Editor. Unity version : 2020.2.2f1 Colyseus SDK version : 0.14.19

Following is the BgDataMessage.cs. class I'm using.

using System.Collections.Generic;

public class BgDataMessage
{
    public int max_win_streak, total_games_played, total_games_win;
    public float turn_timeout_seconds;
    public ulong bet_amount, max_bet_amount;
    public bool can_reconnect;
    public string _id, user_id, last_game_room_id, last_game_session_id;
    public Dictionary<string, List<long>> bet_ranges;
}

Here is how I'm registering for OnMessage: _roomLobby.OnMessage<BgDataMessage>(BgGameManager.MessageBgData, (bgData) => OnMessageBgData?.Invoke(bgData));

Xcode Stacktrace on Exception:

JsonSerializationException: Unable to deserialize instance of 'BgDataMessage' because there is no parameterless constructor is defined on type.
  at GameDevWare.Serialization.Metadata.TypeDescription.CreateInstance () [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Serializers.ObjectSerializer.PopulateInstance (GameDevWare.Serialization.IndexedDictionary`2[KeyT,ValueT] container, System.Object instance) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Serializers.ObjectSerializer.Deserialize (GameDevWare.Serialization.IJsonReader reader) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.JsonReaderExtentions.ReadValue (GameDevWare.Serialization.IJsonReader reader, System.Type valueType, System.Boolean nextToken) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Enumerable+<OfTypeIterator>d__97`1[TResult].System.Collections.IEnumerable.GetEnumerator () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncTaskCache.CreateCacheableTask[TResult] (TResult result) [0x00000] in <00000000000000000000000000000000>:0 
  at Colyseus.ColyseusRoom`1[T].ParseMessage (System.Byte[] bytes) [0x00000] in <00000000000000000000000000000000>:0 
  at NativeWebSocket.WebSocketMessageEventHandler.Invoke (System.Byte[] data) [0x00000] in <00000000000000000000000000000000>:0 
  at NativeWebSocket.WebSocket.DispatchMessageQueue () [0x00000] in <00000000000000000000000000000000>:0 
  at Colyseus.ColyseusClient+ColyseusAddRoomEventHandler.EndInvoke (System.IAsyncResult result) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ContextCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.Run () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Action.Invoke () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.SendOrPostCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.SendOrPostCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
UnityEngine.UnitySynchronizationContext:Exec()
UnityEngine.UnitySynchronizationContext:Exec()

Kindly help me to resolve the issue ASAP as my game is ready and I'm not able to play it on device. Totally stuck! :(

endel commented 1 year ago

Hey @VkDevPh, this issue is related to https://github.com/colyseus/colyseus-unity-sdk/issues/135#issuecomment-1254051078

The workaround suggested for now is to compile using Mono backend and disable code stripping, can you try that out and let us know if that works? Cheers

VkDevPh commented 1 year ago

Hello Endel, Thanks for your quick respose. 1) Code stripping is already disabled for my project. 2) Unity doesn't support Mono backend anymore for iOS platform.

Screenshot 2022-09-26 at 10 51 09 AM

ref. https://docs.unity3d.com/2023.1/Documentation/Manual/class-PlayerSettingsiOS.html

Scripting Backend The scripting backend determines how Unity compiles and executes C# code in your Project. This setting defaults to IL2CPP for iOS and can’t be changed. IL2CPP compiles C# code into CIL, converts the CIL to C++, and then compiles that C++ into native machine code, which executes directly at run time. For more information, see documentation on IL2CPP. C++ code generated by the IL2CPP scripting backend can be updated incrementally, which allows incremental C++ build systems to compile only the changed source files. This can significantly lower iteration times.

Player settings for both points :

Screenshot 2022-09-26 at 10 06 01 AM Screenshot 2022-09-26 at 10 07 40 AM

Still stuck with same issue. Please suggest further to resolve the same.

endel commented 1 year ago

Hi @VkDevPh, thanks for the info. If that didn't do perhaps this other workaround (https://github.com/colyseus/colyseus-unity-sdk/issues/198#issuecomment-1190639325) could help?

"The quick and dirty fix I described in the original post was to change line 200 of Assets/Colyseus/Runtime/GameDevWare.Serialization/Metadata/MetadataReflection.cs file to this:"

if (/*AotRuntime ||*/ type.IsAbstract || type.IsInterface)
    return false;

Let me know if that works for you. This issue has been brought up a couple of times and we should be working on fixing it sooner than later!

VkDevPh commented 1 year ago

Hi Endel, Appreciate your quick reply again. :)

I tried this (https://github.com/colyseus/colyseus-unity-sdk/issues/198#issuecomment-1190639325) approach. Good thing is that it got rid of JsonSerializationException: Unable to deserialize instance of exception but a new exception occurred.

Please have a look and let me know if I'm missing something. (On Unity Editor It's working fine like before. No change.) following is the stack trace:

NullReferenceException: Object reference not set to an instance of an object.
  at System.Linq.Expressions.Interpreter.LightLambda.MakeRunDelegateCtor (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Interpreter.LightLambda.GetRunDelegateCtor (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Interpreter.LightLambda.MakeDelegate (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Expression`1[TDelegate].Compile (System.Boolean preferInterpretation) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Metadata.MetadataReflection.TryGetConstructor (System.Type type, System.Func`1[System.Object]& ctrFn, System.Reflection.ConstructorInfo& defaultConstructor) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Metadata.TypeDescription.Get (System.Type type) [0x00000] in <000000000000000000
NullReferenceException: Object reference not set to an instance of an object.
  at System.Linq.Expressions.Interpreter.LightLambda.MakeRunDelegateCtor (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Interpreter.LightLambda.GetRunDelegateCtor (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Interpreter.LightLambda.MakeDelegate (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Expression`1[TDelegate].Compile (System.Boolean preferInterpretation) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Metadata.MetadataReflection.TryGetConstructor (System.Type type, System.Func`1[System.Object]& ctrFn, System.Reflection.ConstructorInfo& defaultConstructor) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Metadata.TypeDescription.Get (System.Type type) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Serializers.ObjectSerializer..ctor (GameDevWare.Serialization.SerializationContext context, System.Type type) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.SerializationContext.CreateObjectSerializer (System.Type valueType) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.SerializationContext.GetSerializerForType (System.Type valueType) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.JsonReaderExtentions.ReadValue (GameDevWare.Serialization.IJsonReader reader, System.Type valueType, System.Boolean nextToken) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Enumerable+<OfTypeIterator>d__97`1[TResult].System.Collections.IEnumerable.GetEnumerator () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncTaskCache.CreateCacheableTask[TResult] (TResult result) [0x00000] in <00000000000000000000000000000000>:0 
  at Colyseus.ColyseusRoom`1[T].ParseMessage (System.Byte[] bytes) [0x00000] in <00000000000000000000000000000000>:0 
  at NativeWebSocket.WebSocketMessageEventHandler.Invoke (System.Byte[] data) [0x00000] in <00000000000000000000000000000000>:0 
  at NativeWebSocket.WebSocket.DispatchMessageQueue () [0x00000] in <00000000000000000000000000000000>:0 
  at Colyseus.ColyseusClient+ColyseusAddRoomEventHandler.EndInvoke (System.IAsyncResult result) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ContextCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.Run () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Action.Invoke () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.SendOrPostCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.SendOrPostCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 

Thanks

VkDevPh commented 1 year ago

Hi Endel, Appreciate your quick reply again. :)

I tried this (#198 (comment)) approach. Good thing is that it got rid of JsonSerializationException: Unable to deserialize instance of exception but a new exception occurred.

Please have a look and let me know if I'm missing something. (On Unity Editor It's working fine like before. No change.) following is the stack trace:

NullReferenceException: Object reference not set to an instance of an object.
  at System.Linq.Expressions.Interpreter.LightLambda.MakeRunDelegateCtor (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Interpreter.LightLambda.GetRunDelegateCtor (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Interpreter.LightLambda.MakeDelegate (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Expression`1[TDelegate].Compile (System.Boolean preferInterpretation) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Metadata.MetadataReflection.TryGetConstructor (System.Type type, System.Func`1[System.Object]& ctrFn, System.Reflection.ConstructorInfo& defaultConstructor) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Metadata.TypeDescription.Get (System.Type type) [0x00000] in <000000000000000000
NullReferenceException: Object reference not set to an instance of an object.
  at System.Linq.Expressions.Interpreter.LightLambda.MakeRunDelegateCtor (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Interpreter.LightLambda.GetRunDelegateCtor (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Interpreter.LightLambda.MakeDelegate (System.Type delegateType) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Expressions.Expression`1[TDelegate].Compile (System.Boolean preferInterpretation) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Metadata.MetadataReflection.TryGetConstructor (System.Type type, System.Func`1[System.Object]& ctrFn, System.Reflection.ConstructorInfo& defaultConstructor) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Metadata.TypeDescription.Get (System.Type type) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.Serializers.ObjectSerializer..ctor (GameDevWare.Serialization.SerializationContext context, System.Type type) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.SerializationContext.CreateObjectSerializer (System.Type valueType) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.SerializationContext.GetSerializerForType (System.Type valueType) [0x00000] in <00000000000000000000000000000000>:0 
  at GameDevWare.Serialization.JsonReaderExtentions.ReadValue (GameDevWare.Serialization.IJsonReader reader, System.Type valueType, System.Boolean nextToken) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Linq.Enumerable+<OfTypeIterator>d__97`1[TResult].System.Collections.IEnumerable.GetEnumerator () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncTaskCache.CreateCacheableTask[TResult] (TResult result) [0x00000] in <00000000000000000000000000000000>:0 
  at Colyseus.ColyseusRoom`1[T].ParseMessage (System.Byte[] bytes) [0x00000] in <00000000000000000000000000000000>:0 
  at NativeWebSocket.WebSocketMessageEventHandler.Invoke (System.Byte[] data) [0x00000] in <00000000000000000000000000000000>:0 
  at NativeWebSocket.WebSocket.DispatchMessageQueue () [0x00000] in <00000000000000000000000000000000>:0 
  at Colyseus.ColyseusClient+ColyseusAddRoomEventHandler.EndInvoke (System.IAsyncResult result) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ContextCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00000] in <00000000000000000000000000000000>:0 
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.Run () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Action.Invoke () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.SendOrPostCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <00000000000000000000000000000000>:0 
  at System.Threading.SendOrPostCallback.Invoke (System.Object state) [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 
  at UnityEngine.UnitySynchronizationContext.Exec () [0x00000] in <00000000000000000000000000000000>:0 

Thanks

Hi Endel, I tried to debug, but this exception is coming at line no. 213.

ctrFn = Expression.Lambda<Func<object>>(
                    Expression.Convert(
                        Expression.New(defaultConstructor),
                        typeof(object))
                    ).Compile();

Please help me to resolve this.

VkDevPh commented 1 year ago

Hi Edel, Do we have any update on it?

NullReferenceException at line no. 213 in Class MetadataReflection.cs.

I'm still stuck here and not able to play the game on an iOS device. waiting for a reply from your end.

Thanks

deniszykov commented 1 year ago

Hi @VkDevPh! This is case of Unity code stripping. You need to setup link.xml and make sure ILLinker would not delete constructor. As alternative solution you could reference anywhere in you code this constructor and it will retain.

if (typeof(string).Name == "") // any non true expression which is NOT compile time evaluable
{
    new BgDataMessage(...);
}

and this is bug in Unity's runtime which should be reported:

NullReferenceException: Object reference not set to an instance of an object.
  at System.Linq.Expressions.Interpreter.LightLambda.MakeRunDelegateCtor (System.Type delegateType)