Search Unity

  1. Calling all beginners! Join the FPS Beginners Mods Challenge until December 13.
    Dismiss Notice
  2. It's Cyber Week at the Asset Store!
    Dismiss Notice

Unity Multiplayer Which one is supposed to handle MissingReferenceException? The developer, or the engine?

Discussion in 'Connected Games' started by asperatology, Jul 27, 2015.

  1. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    969
    I got an error message when destroying a game object on a network game using UNET.

    Code (CSharp):
    1. MissingReferenceException: The object of type 'NetworkIdentity' has been destroyed but you are still trying to access it.
    2. Your script should either check if it is null or you should not destroy the object.
    3. UnityEngine.Networking.NetworkServer.DestroyObject (UnityEngine.Networking.NetworkIdentity uv) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkServer.cs:1388)
    4. UnityEngine.Networking.NetworkServer.DestroyPlayersForConnection (UnityEngine.Networking.NetworkConnection conn) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkServer.cs:1351)
    5. UnityEngine.Networking.NetworkManager.OnServerDisconnect (UnityEngine.Networking.NetworkConnection conn) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkManager.cs:693)
    6. UnityEngine.Networking.NetworkManager.OnServerDisconnectInternal (UnityEngine.Networking.NetworkMessage netMsg) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkManager.cs:587)
    7. UnityEngine.Networking.NetworkMessageHandlers.InvokeHandler (Int16 msgType, UnityEngine.Networking.NetworkConnection conn, UnityEngine.Networking.NetworkReader reader, Int32 channelId) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkMessageHandlers.cs:73)
    8. UnityEngine.Networking.NetworkMessageHandlers.InvokeHandlerNoData (Int16 msgType, UnityEngine.Networking.NetworkConnection conn) (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkMessageHandlers.cs:60)
    9. UnityEngine.Networking.NetworkServer.InternalUpdate () (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkServer.cs:681)
    10. UnityEngine.Networking.NetworkServer.Update () (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkServer.cs:514)
    11. UnityEngine.Networking.NetworkIdentity.UNetStaticUpdate () (at C:/buildslave/unity/build/Extensions/Networking/Runtime/NetworkIdentity.cs:724)
    12.  
    I am wondering who should handle this error, the developer or the engine?

    If it's supposed to be on the developer side, how should I design the code, so that I can prevent the error from happening?

    This is the destroy game object code:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3. using UnityEngine.Networking;
    4.  
    5. public class CubeUnit : NetworkBehaviour {
    6.     public enum OrderStatus {
    7.         ATTACK, MOVE, SPLIT, MERGE
    8.     }
    9.  
    10.     public OrderStatus status;
    11.     public CubeUnit targetUnit;
    12.     public float attackRadius;
    13.     public float attackCooldown;
    14.     public int attackPower;
    15.     [SyncVar] public int healthPoints;
    16.  
    17.     private float attackCooldownTimer;
    18.  
    19.     private void Start() {
    20.         if (this.healthPoints <= 0) {
    21.             this.healthPoints = 1;
    22.         }
    23.         if (this.attackCooldown <= 3f) {
    24.             this.attackCooldown = 3f;
    25.         }
    26.         this.attackCooldownTimer = this.attackCooldown;
    27.         this.attackPower = 1;
    28.         this.targetUnit = null;
    29.         this.status = OrderStatus.ATTACK;
    30.  
    31.         Renderer renderer = this.GetComponent<Renderer>();
    32.         if (renderer != null) {
    33.             Vector3 size = renderer.bounds.size;
    34.             this.attackRadius = size.magnitude + 1f;
    35.         }
    36.         else {
    37.             this.attackRadius = 3f;
    38.         }
    39.     }
    40.  
    41.     //When the unit state is set to attack another unit, it will then begin the counter that counts until the cooldown reaches zero, and then attack.
    42.     private void Update() {
    43.         if (this.attackCooldownTimer < 0f) {
    44.             switch (this.status) {
    45.                 default:
    46.                 case OrderStatus.ATTACK:
    47.                     if (this.targetUnit == null) {
    48.                         CheckSurroundings();
    49.                     }
    50.                     AttackUnit(this.targetUnit);
    51.                     break;
    52.             }
    53.             this.attackCooldownTimer = this.attackCooldown;
    54.         }
    55.         else {
    56.             this.attackCooldownTimer -= Time.deltaTime;
    57.         }
    58.     }
    59.  
    60.     // -----------------------------------------------------------------------------
    61.  
    62.     public void SetAttackTarget(Vector3 target) {
    63.         this.status = OrderStatus.ATTACK;
    64.         this.SetAgentDestination(target);
    65.     }
    66.  
    67.     public void SetAgentDestination(Vector3 target) {
    68.         NavMeshAgent agent = this.GetComponent<NavMeshAgent>();
    69.         if (agent != null) {
    70.             agent.SetDestination(target);
    71.         }
    72.     }
    73.  
    74.     public void SetAgentsDestinationFormation(Vector3 target) {
    75.         //Set a new formation pattern for this for more than 2 units.
    76.     }
    77.  
    78.     public void TakeDamage(int value) {
    79.         if (this.healthPoints <= 0) {
    80.             CmdKillMe(this.gameObject);   //  <----------------------------   THIS KILLS THE GAME OBJECT.
    81.             this.targetUnit = null;
    82.         }
    83.         else {
    84.             this.healthPoints -= value;
    85.         }
    86.     }
    87.  
    88.     // -----------------------------------------------------------------------------
    89.  
    90.     private void CheckSurroundings() {
    91.         if (this.targetUnit == null) {
    92.             Transform origin = this.GetComponent<Transform>();
    93.             Collider[] colliders = Physics.OverlapSphere(origin.position, 5.0f);
    94.             foreach (Collider col in colliders) {
    95.                 CubeUnit unit = col.GetComponent<CubeUnit>();
    96.                 if (unit != null) {
    97.                     this.targetUnit = unit;
    98.                     return;
    99.                 }
    100.             }
    101.         }
    102.     }
    103.  
    104.     private void AttackUnit(CubeUnit other) {
    105.         if (other == null) {
    106.             return;
    107.         }
    108.         Transform transform = this.GetComponent<Transform>();
    109.         Transform otherTransform = other.GetComponent<Transform>();
    110.         if (Vector3.Distance(transform.position, otherTransform.position) < this.attackRadius) {
    111.             other.TakeDamage(this.attackPower);   //<-------------------   CALLS FOR THE ENEMY TO TAKE DAMANGE EVERY ATTACK TICK (3 seconds)
    112.         }
    113.     }
    114.  
    115.     // -----------------------------------------------------------------------------
    116.  
    117.     [Command]
    118.     public void CmdKillMe(GameObject obj) {
    119.         NetworkServer.Destroy(obj);
    120.     }
    121. }
    122.  
    123.  
     
  2. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    FYI, this code is insecure. It allows a malicious client to kill another player by passing their game object. Commands are explicitly called on the player that issued them, so adding a "self" to a command is redundant and breaks the security model.

    Code (CSharp):
    1.  [Command]
    2.    public void CmdKillMe(GameObject obj) {
    3.         NetworkServer.Destroy(obj);
    4.    }
    But anyway, the engine should handle that exception. I will file a bug on this.
     
  3. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    969
    Which code is insecure? My "destroy game object code", or the engine code? If it's me, I never knew I wrote an insecure code. :D

    Thank you for clarifying.
     
    Last edited: Jul 27, 2015
  4. Disastorm

    Disastorm

    Joined:
    Jun 16, 2013
    Posts:
    69
    He means you should do this instead

    Code (CSharp):
    1. public void CmdKillMe() {
    2.         NetworkServer.Destroy(this.gameObject);
    3.    }
    or something along those lines so that a player can't tell the server to kill someone else instead of "me"
     
  5. asperatology

    asperatology

    Joined:
    Mar 10, 2015
    Posts:
    969
    Yeah, that's more reasonable.