Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

I love strippers! aka Network Errors Build Stripping question

Discussion in 'iOS and tvOS' started by mindlube, Feb 22, 2011.

  1. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    I am implementing the UDP + RPC type of networking as described in:
    Unity Multiplayer Tutorial
    For using Networking in Unity.
    Author: Andrius Kuznecovas Last Revision: 24-MAR-2010

    I'll put my (work in progress) source code that is causing this problem, in a followup post

    Using Unity 3.2 Pro. Platform = iOS and Android

    When stripping is set to use micro mscorlib, the editor fails to build:
    UnityException: Failed assemblies stripper (long exception....snip)
    Unhandled Exception: Mono.Linker.ResolutionException: Can not resolve reference: System.Void

    When stripping is set to to Strip Bytecode,it build I get a JIT Error at runtime iOS device
    ExecutionEngineException: Attempting to JIT compile method 'GameManagerState__TypeMetadata:.ctor ()' while running with --aot-only.

    When stripping is set to Strip Assemblies, it builds, but I get the same JIT error as above.

    When stripping is set to Disabled, it builds, but I get the same JIT error as above.

    This seems like a bug to me, but I have not attempted networking with Unity on iOS before.

    Should I replace Threads with Coroutines?

    Please advise, iOS + Unity experts. Thanks,

    Alex
     
  2. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using System.Net;
    4. using System.Net.Sockets;
    5. using System.Threading;
    6.  
    7. public class UnityMPWifiNetMgr : BaseNetMgr
    8. {
    9.     public DominosNetPlayer netPlayerPrefab;
    10.    
    11.     private NetworkView cachedNetView;
    12.        
    13.     private UdpClient udpServer;
    14.     private UdpClient udpClient;
    15.     private IPEndPoint udpReceiveEndPoint;
    16.     private string udpServerPort = "40400";
    17.     private int unityServerPort = 40401;
    18.     private string ip = "0.0.0.0";
    19.     private string ipBroadcast = "255.255.255.255";
    20.     private bool connected = false;
    21.     private string serverName = "";
    22.     private Thread clientThread;
    23.     private Thread serverThread;
    24.    
    25.     public bool IsHost {
    26.         get {
    27.             return (GameManager.instance.State.ClientType == ClientType.Host );
    28.         }
    29.     }
    30.        
    31.     public NetworkView CachedNetView {
    32.         get {
    33.             if (cachedNetView == null)
    34.                 cachedNetView = GetComponent<NetworkView> ();
    35.             return this.cachedNetView;
    36.         }
    37.         set {
    38.             cachedNetView = value;
    39.         }
    40.     }
    41.    
    42.     public void Awake ()
    43.     {
    44.         Application.runInBackground = true;
    45.     }
    46.    
    47.     public override void Start ()
    48.     {
    49.         base.Start ();
    50.         StartCoroutine(BootMPGame());
    51.     }
    52.        
    53.     public void OnDestroy ()
    54.     {
    55.        
    56.     }
    57.    
    58.     public override void OnDoPlayerConnect (object sender, BasePlayer player)
    59.     {
    60.         // notify local delegates      
    61.         base.OnDoPlayerConnect (sender, player);
    62.        
    63.         // if we are Host, then RPC call OnDoPlayerConnect's equivalent method
    64.         if (IsHost)
    65.         {
    66.             CachedNetView.RPC ("OnDoPlayerConnectRPC", RPCMode.OthersBuffered, player.Nic, player.NetPlayer);
    67.         }
    68.     }
    69.    
    70.     [RPC]  
    71.     public void OnDoPlayerConnectRPC (string nic, NetworkPlayer netPlayer, NetworkMessageInfo netInfo)
    72.     {
    73.         Debug.Log ("RPC call rec'd: " + netInfo);
    74.        
    75.         // create NetPlayer, assign nic and NetPlayer reference, then add to Players
    76.         DominosNetPlayer newPlayer = Instantiate (
    77.             netPlayerPrefab,
    78.             GameManager.instance.otherPlayerSpawnPoint.position,
    79.             Quaternion.identity
    80.             ) as DominosNetPlayer;
    81.         newPlayer.Nic = nic;
    82.         newPlayer.NetPlayer = netPlayer;
    83.         GameManager.instance.Players.Add (newPlayer);
    84.        
    85.         // notify local delegates
    86.         base.OnDoPlayerConnect (this, newPlayer);
    87.     }
    88.  
    89.     public override void OnDoPlayerDisconnect (object sender, BasePlayer player)
    90.     {
    91.         // notify local delegates
    92.         base.OnDoPlayerDisconnect (sender, player);
    93.  
    94.         // if we are Host, then RPC call OnDoPlayerConnect's equivalent method
    95.         if (IsHost)
    96.         {
    97.             CachedNetView.RPC ("OnDoPlayerConnectRPC", RPCMode.OthersBuffered, player.Nic, player.NetPlayer);
    98.         }
    99.     }
    100.    
    101.     // these delegate messages may have come from local senders, or via RPC call
    102.     public override void OnDoGameState (object sender, GameState newState)
    103.     {
    104.         // notify local delegates.
    105.         base.OnDoGameState (sender, newState);
    106.            
    107.         // notify remote delegates via RPC, if we are host
    108.         if (GameManager.instance.State.ClientType != ClientType.Host)
    109.             return;
    110.     }
    111.  
    112.     public override void OnDoPlayerTurnState (object sender, BasePlayer player, PlayerTurnStates newState)
    113.     {
    114.         // notify local delegates.
    115.         base.OnDoPlayerTurnState (sender, player, newState);
    116.        
    117.         // notify remote delegates via RPC, if we are host
    118.         if (GameManager.instance.State.ClientType != ClientType.Host)
    119.             return;
    120.     }
    121.  
    122.     public override void OnPlayBone (object sender, BasePlayer player, Bone playBone, Bone parentBone, BonePosition pos)
    123.     {
    124.         // notify local delegates.
    125.         base.OnPlayBone (sender, player, playBone, parentBone, pos);
    126.        
    127.    
    128.         if (GameManager.instance.State.ClientType != ClientType.Host)
    129.             return;
    130.        
    131.         // notify remote delegates via RPC, if we are host
    132.     }
    133.    
    134.        
    135.     private IEnumerator BootMPGame ()
    136.     {
    137.         GameManager.instance.State.ClientType = ClientType.Unknown;
    138.  
    139.         #if UNITY_IPHONE || UNITY_ANDROID
    140.         if (iPhoneSettings.internetReachability == iPhoneNetworkReachability.NotReachable)
    141.         {
    142.             Debug.LogWarning ("Internet not reachable");
    143.             yield return new WaitForSeconds (1);
    144.         }
    145.         #endif
    146.        
    147.         // the udp client will start a thread, listen on port, for broadcast of server name
    148.         // then the ServerName property will be set
    149.         LoadUDPClient ();
    150.         yield return new WaitForSeconds (1);
    151.        
    152.         if (serverName != string.Empty)
    153.         {
    154.             Debug.LogWarning ("client located server @ " + serverName);
    155.             Debug.LogWarning("client connecting....");
    156.             Network.Connect (serverName, unityServerPort);
    157.         }
    158.         else
    159.         {
    160.             Debug.LogWarning ("no udp server detected");
    161.            
    162.             // force udpClient and thread to exit first
    163.             connected = true;
    164.             connected = false;
    165.             udpClient.Close ();
    166.             yield return 0;
    167.            
    168.             Debug.LogWarning("udp client stopped");
    169.            
    170.             // become the server
    171.             LoadUDPServer ();
    172.         }
    173.        
    174.         if(GameManager.instance.State.ClientType == ClientType.Host)
    175.         {
    176.             //add DevicePlayer as Player 0
    177.             GameManager.instance.Players.Clear();
    178.             DevicePlayer.instance.NetPlayer = Network.player;
    179.             GameManager.instance.Players.Add( DevicePlayer.instance );         
    180.                
    181.             while( GameManager.instance.Players.Count <  GameManager.instance.State.MaxPlayers )
    182.             {
    183.                 Debug.LogWarning("waiting for players");
    184.                 yield return new WaitForSeconds(1);
    185.             }
    186.         }
    187.     }
    188.  
    189.     void OnFailedToConnect (NetworkConnectionError error)
    190.     {
    191.         Debug.LogWarning ("Could not connect to server: " + error);
    192.     }
    193.    
    194.     void OnFailedToConnectToMasterServer (NetworkConnectionError info)
    195.     {
    196.         Debug.LogWarning ("Could not connect to master server: " + info);
    197.     }
    198.    
    199.     void OnDisconnectedFromServer (NetworkDisconnection info)
    200.     {
    201.         if (Network.isServer)          
    202.             Debug.LogWarning ("Local server connection disconnected");
    203.         else if (info == NetworkDisconnection.LostConnection)
    204.             Debug.LogWarning ("Lost connection to the server");
    205.         else
    206.             Debug.LogWarning ("Successfully diconnected from the server");
    207.     }
    208.    
    209.     public void OnServerInitialized ()
    210.     {
    211.         Debug.LogWarning ("Unity Network Server initialized");
    212.        
    213.         connected = true;
    214.         GameManager.instance.State.ClientType = ClientType.Host;
    215.        
    216.         try
    217.         {
    218.             ip = Network.player.ipAddress.ToString ();
    219.             udpServer = new UdpClient (System.Convert.ToInt32 (udpServerPort));
    220.             udpReceiveEndPoint = new IPEndPoint (IPAddress.Parse (ip), System.Convert.ToInt32 (udpServerPort));
    221.             serverThread = new Thread (new ThreadStart (UDPServerTask));
    222.             serverThread.Start ();
    223.         }
    224.         catch {}
    225.        
    226.         Debug.LogWarning ("UDP broadcasting of server ip: " + ip);
    227.     }
    228.    
    229.     public void OnConnectedToServer ()
    230.     {
    231.         Debug.LogWarning ("Connected to server");
    232.        
    233.         connected = true;
    234.         GameManager.instance.State.ClientType = ClientType.RemoteClient;
    235.     }
    236.    
    237.     void OnPlayerConnected (NetworkPlayer player)
    238.     {              
    239.         Debug.LogWarning ("Player connected from " + player.ipAddress + ":" + player.port);
    240.     }
    241.  
    242.     void OnPlayerDisconnected (NetworkPlayer player)
    243.     {
    244.         Debug.LogWarning ("Server destroying player");
    245.         Network.RemoveRPCs (player, 0);
    246.         Network.DestroyPlayerObjects (player);
    247.     }
    248.    
    249.     /// <summary>
    250.     /// creates receive point and client, starts the udpclient thread
    251.     /// </summary>
    252.     private void LoadUDPClient ()
    253.     {
    254.         Debug.LogWarning ("Starting UDP client...");
    255.        
    256.         try
    257.         {
    258.             udpClient = new UdpClient (System.Convert.ToInt32 (udpServerPort));
    259.             udpReceiveEndPoint = new IPEndPoint (IPAddress.Parse (ip), System.Convert.ToInt32 (udpServerPort));
    260.             clientThread = new Thread (new ThreadStart (UDPClientTask));
    261.             clientThread.Start ();
    262.             Debug.LogWarning ("UDP client started");
    263.         }
    264.         catch
    265.         {}     
    266.     }
    267.  
    268.     /// <summary>
    269.     /// Reads the server name from any udp broadcasts.
    270.     /// </summary>
    271.     private void UDPClientTask ()
    272.     {
    273.         try {
    274.             while (true)
    275.             {
    276.                 byte[] recData = udpClient.Receive (ref udpReceiveEndPoint);
    277.                 System.Text.ASCIIEncoding encode = new System.Text.ASCIIEncoding ();
    278.                 serverName = encode.GetString (recData);
    279.                
    280.                 if (serverName != string.Empty)
    281.                 {
    282.                     // if the server was selected above, and unity network connected, then close
    283.                     // the udp client and break out of thread
    284.                     if (connected)
    285.                     {
    286.                         udpClient.Close ();
    287.                         break;
    288.                     }
    289.                 }
    290.             }
    291.         } catch {}
    292.     }
    293.    
    294.     /// <summary>
    295.     /// Inits the Unity Server, and starts a thread to broadcast over udp.
    296.     /// </summary>
    297.     private void LoadUDPServer ()
    298.     {
    299.         Debug.LogWarning("Becoming Server...");
    300.         Network.InitializeSecurity ();
    301.         Network.InitializeServer (GameManager.instance.State.MaxPlayers, unityServerPort, false);
    302.     }
    303.    
    304.     /// <summary>
    305.     /// broadcasts our ip address over udp. it will be read as ServerName property by the client.
    306.     /// </summary>
    307.     private void UDPServerTask ()
    308.     {
    309.         try {
    310.             while (true)
    311.             {
    312.                 System.Text.ASCIIEncoding encode = new System.Text.ASCIIEncoding ();
    313.                 byte[] sendData = encode.GetBytes (ip);
    314.                 udpServer.Send (sendData, sendData.Length, ipBroadcast, System.Convert.ToInt32 (udpServerPort));
    315.                 Thread.Sleep (100);
    316.             }
    317.         } catch {
    318.         }
    319.     }          
    320. }
     
    Last edited: Feb 22, 2011
  3. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    After a lot trial and error compilation, and I discovered this is the offending line of code which will not compile when
    build stripping set to: use micro mscorlib

    // private UdpClient udpServer
    udpServer.Send (sendData, sendData.Length, ipBroadcast, System.Convert.ToInt32 (udpServerPort));

    Is this is a bug with the stripping? I don't understand why it fails on that single line of code. as opposed to when the UdpClient object was created.

    The other errors about JIT- well I guess those have to do with [Serializable] not working right when build stripping is NOT set to use micro mscorlib, which is another mystery unto itself.
     
  4. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    UdpClient.Send() doesn't compile with use micro mscorlib (Bug?)

    [Serializable] and BinaryFormatter.Serialize throws runtime exceptions UNLESS use micro mscorlib

    So I am stuck between a rock and a hard place. Need to either use XML serialization (and add additional 1.2MB of .dll) or rewrite my game saving to be less fancy I guess.
     
  5. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    Aha! just in case anyone else goes down this same nefarious path :)
    I discovered that BinaryFormatter works much better on iOS, if you implement ISerializable. I guess if you don't do that, it has to use dynamic reflection and/or generics to figure out the object at runtime.
    Yay!
     
  6. Mantas-Puida

    Mantas-Puida

    Joined:
    Nov 13, 2008
    Posts:
    1,864
    Micro mscorlib is not compatible with .NET sockets. It was designed to support very minimal set of .NET classes that required by UnityEngine.dll to run (like System.String and few others).

    Usually I advice people to avoid BinaryFormatter on iOS and make custom serializer, which actually happens when you implement ISerializable.
     
  7. arkanoid87

    arkanoid87

    Joined:
    May 26, 2012
    Posts:
    1
    I'm experiencing the very same problem now on iOS. I need to serialize use RPC, I confirm that Binary serialization won't work outside mscorlib and RPC won't work inside.

    I've also tried with protobuf-net serialization but I'm encountering hard problems (unity 3.5.2f2)

    After hours of work, I still don't have a working serialization mechanism for my game :(

    EDIT: finally I've managed how to work with protobuf-net, at writing time those information are still ok, but you have to follow them strictly: you HAVE TO build 2 dll, one for the Model and another for the ModelSerializer, the second one is generated through ProtoBuf TypeModel Compile
     
    Last edited: May 26, 2012
  8. mindlube

    mindlube

    Joined:
    Oct 3, 2008
    Posts:
    993
    arkanoid, glad you got the protobuf-net working (the same solution I settled upon!) and yes you must precompile dlls for the model and the serializer. Another ios-compatible serialization alternative is a stripped down JSON parser, such as the one distributed by prime31. Cheers!