Hard Unity 5.2.1f1 Crash - Unity Bug Reporter doesn't run, can't report to bug tracker.

Because I can’t even use Unity Bug Reporter, let alone use Unity to open up the project, I cannot report a really major bug I found in UNET. This major crash can easily be reproduced.

Unity compiled my just-recently-edited script which causes it to hard crash immediately, not even triggering a Unity Bug Reporter window.

The offending code lies in a structurally syntax-correct code placement. This is what I know:

  • Need a [System.Serializable] struct with methods inside it.
  • Use SyncListStruct, where T is the struct created.
  • Make a class that inherits SyncListStruct.
  • Make this class a class member variable in a NetworkBehaviour class.
  • (Optional) Add an initializer to the class member where declared.
  • Make the class member [UnityEngine.SerializeField].
  • Add a few [Command] and [ClientRpc].

The Just-Recently-Edited script in full text is given below. To reproduce the issue, just copy/paste in a new script in an empty Unity 5.2.1f1 project, and let Unity compile:

using UnityEngine;
using UnityEngine.Networking;
using System;
using System.Collections.Generic;

[System.Serializable]
public struct SplitGroup {
    public GameUnit ownerUnit;
    public GameUnit splitUnit;
    public float elapsedTime;
    public Vector3 rotationVector;
    public Vector3 origin;

    public SplitGroup(GameUnit ownerUnit, GameUnit splitUnit) {
        this.ownerUnit = ownerUnit;
        this.splitUnit = splitUnit;
        this.elapsedTime = 0f;
        this.origin = ownerUnit.gameObject.transform.position;

        //TODO: Add a radius where the unit will always go towards.
        SpawnRange range = this.ownerUnit.GetComponentInChildren<SpawnRange>();
        float angle = UnityEngine.Random.Range(-180f, 180f);
        this.rotationVector = Quaternion.Euler(0f, angle, 0f) * (Vector3.one * range.radius);

        //Alternately, Stop() the nav mesh agent when the split group is created. Resume() the nav mesh agent when removing the split group.
        //If that doesn't work, then this is the best solution to "halt" the nav mesh agent.
        NavMeshAgent agent = this.ownerUnit.GetComponent<NavMeshAgent>();
        if (agent != null) {
            agent.Stop();
        }
        agent = this.splitUnit.GetComponent<NavMeshAgent>();
        if (agent != null) {
            agent.Stop();
        }
    }

    public void Update() {
        Debug.Log("Updating... from Split Group ");
        this.ownerUnit.isSelected = false;
        this.splitUnit.isSelected = false;

        Vector3 pos = Vector3.Lerp(this.origin, this.origin + this.rotationVector, this.elapsedTime);
        this.ownerUnit.gameObject.transform.position = pos;
        pos = Vector3.Lerp(this.origin, this.origin - this.rotationVector, this.elapsedTime);
        this.splitUnit.gameObject.transform.position = pos;
    }

    public void Stop() {
        NavMeshAgent agent = this.ownerUnit.GetComponent<NavMeshAgent>();
        if (agent != null) {
            agent.ResetPath();
            agent.Resume();
        }

        agent = this.splitUnit.GetComponent<NavMeshAgent>();
        if (agent != null) {
            agent.ResetPath();
            agent.Resume();
        }
    }
};

public class SyncSplitGroup : SyncListStruct<SplitGroup> {
}

public class SplitManager : NetworkBehaviour {
    [SerializeField]
    public SyncSplitGroup splitGroupList;
    [SerializeField]
    public List<SplitGroup> removeList;
    [SerializeField]
    public SelectionManager selectionManager;
    [SerializeField]
    public Spawner spawner;
    public GameObject gameUnitPrefab;

    //Split Manager is designed to streamline the creation of new game units.
    //To achieve this, there needs to be two different array list that keeps track of all the creations, called Split Groups.
    //One keeps track of the Split Groups, the other removes them from the tracking list.

    public void Start() {
        if (!this.hasAuthority) {
            return;
        }

        if (this.splitGroupList == null) {
            this.splitGroupList = new SyncSplitGroup();
        }
        if (this.selectionManager == null) {
            GameObject[] managers = GameObject.FindGameObjectsWithTag("SelectionManager");
            foreach (GameObject manager in managers) {
                SelectionManager select = manager.GetComponent<SelectionManager>();
                if (select != null && select.hasAuthority) {
                    this.selectionManager = select;
                    break;
                }
            }
            if (this.selectionManager == null) {
                Debug.LogError("Cannot find Selection Manager. Aborting");
            }
        }
        if (this.spawner == null) {
            GameObject[] spawners = GameObject.FindGameObjectsWithTag("Spawner");
            foreach (GameObject obj in spawners) {
                Spawner spawner = obj.GetComponent<Spawner>();
                if (spawner != null && spawner.hasAuthority) {
                    this.spawner = spawner;
                    break;
                }
            }
            if (this.spawner == null) {
                Debug.LogError("Spawner is never set. Please check.");
            }
        }
    }

    public void Update() {
        if (!this.hasAuthority) {
            return;
        }

        //When the player starts the action to split a game unit into two, it takes in all the selected game units
        //one by one, and splits them individually.
        if (Input.GetKeyDown(KeyCode.S)) {
            if (this.selectionManager != null) {
                AddingNewSplitGroup();
            }
        }
        UpdateSplitGroup();
    }

    private void UpdateSplitGroup() {
        if (this.splitGroupList != null && this.splitGroupList.Count > 0) {
            for (int i = 0; i < this.splitGroupList.Count; i++) {
                Debug.Log("Updating split group...." + i);
                SplitGroup group = this.splitGroupList[i];
                if (group.elapsedTime > 1f) {
                    CmdGroupStop(i);
                    if (!this.selectionManager.allObjects.Contains(group.splitUnit.gameObject)) {
                        this.selectionManager.allObjects.Add(group.splitUnit.gameObject);
                    }
                    if (!this.selectionManager.allObjects.Contains(group.ownerUnit.gameObject)) {
                        this.selectionManager.allObjects.Add(group.ownerUnit.gameObject);
                    }
                    this.removeList.Add(group);
                }
                else {
                    //Some weird C# language design...
                    group.Update();
                    group.elapsedTime += Time.deltaTime / 3f;
                    this.splitGroupList[i] = group;
                }
            }
        }

        if (this.removeList != null && this.removeList.Count > 0) {
            foreach (SplitGroup group in this.removeList) {
                this.splitGroupList.Remove(group);
            }
            this.removeList.Clear();
        }
    }

    private void AddingNewSplitGroup() {
        foreach (GameObject obj in this.selectionManager.selectedObjects) {
            CmdSplit(obj);
        }
        return;
    }

    [Command]
    public void CmdSplit(GameObject obj) {
        //This is profoundly one of the hardest puzzles I had tackled. Non-player object spawning non-player object.
        //Instead of the usual spawning design used in the Spawner script, the spawning codes here are swapped around.
        //In Spawner, you would called on NetworkServer.SpawnWithClientAuthority() in the [ClientRpc]. Here, it's in [Command].
        //I am guessing it has to do with how player objects and non-player objects interact with UNET.
        GameObject split = MonoBehaviour.Instantiate(this.gameUnitPrefab) as GameObject;
        split.transform.position = obj.transform.position;
        NetworkIdentity managerIdentity = this.GetComponent<NetworkIdentity>();
        NetworkServer.SpawnWithClientAuthority(split, managerIdentity.clientAuthorityOwner);
        RpcSplit(obj, split);
    }

    [ClientRpc]
    public void RpcSplit(GameObject obj, GameObject split) {
        //We do not call on NetworkServer methods here. This is used only to sync up with the original game unit for all clients.
        //This includes adding the newly spawned game unit into the Selection Manager that handles keeping track of all game units.
        //if (!this.hasAuthority) {
        //    return;
        //}
        GameUnit original = obj.GetComponent<GameUnit>();
        GameUnit copy = split.GetComponent<GameUnit>();
        Copy(original, copy);
        NavMeshAgent originalAgent = obj.GetComponent<NavMeshAgent>();
        originalAgent.ResetPath();
        NavMeshAgent copyAgent = copy.GetComponent<NavMeshAgent>();
        copyAgent.ResetPath();
        this.splitGroupList.Add(new SplitGroup(original, copy));
        this.selectionManager.allObjects.Add(split);
    }

    [Command]
    public void CmdGroupStop(int i) {
        RpcGroupStop(i);
    }

    [ClientRpc]
    public void RpcGroupStop(int i) {
        if (i < this.splitGroupList.Count) {
            SplitGroup group = this.splitGroupList[i];
            group.Stop();
        }
    }

    private void Copy(GameUnit original, GameUnit copy) {
        copy.isSelected = original.isSelected;
        copy.transform.position = original.transform.position;
        copy.transform.rotation = original.transform.rotation;
        copy.transform.localScale = original.transform.localScale;
        copy.oldTargetPosition = original.oldTargetPosition;
        copy.isDirected = original.isDirected = false;
    }
}

Note: The code below works normally, so I really don’t know how to SSCCE the code above:

using UnityEngine;
using UnityEngine.Networking;
using System;
using System.Collections.Generic;

[System.Serializable]
public struct SplitGroup {
    public SplitGroup() {
    }
};

public class SyncSplitGroup : SyncListStruct<SplitGroup> {
}


public class SplitManager : NetworkBehaviour {
    [SerializeField]
    public SyncSplitGroup splitGroupList = new SyncSplitGroup();

    [Command]
    public void CmdTest(){
       RpcTest();
    }

    [ClientRpc]
    public void RpcTest(){
    }
}

Can you open a new project in Unity and run the bug reporter from that? It is much better if you file the bug than if we do, it keeps you in the loop.

I wrote a bug report very early in the morning. (Case 734382)

The bug doesn’t occur in a new Unity project, so it seems to me there’s an isolated incident in my project.

There is a whole bunch of factors, and I’m doing my best to nail it down. The bug’s still there, even after a restart or a reboot.

1 Like